Merge branch 'v1.8' of github.com:metersphere/metersphere into v1.8

This commit is contained in:
chenjianxing 2021-03-30 10:54:04 +08:00
commit e56e2e19b5
118 changed files with 3884 additions and 2628 deletions

View File

@ -17,7 +17,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<shiro.version>1.6.0</shiro.version> <shiro.version>1.6.0</shiro.version>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<jmeter.version>5.2.1</jmeter.version> <jmeter.version>5.4.1</jmeter.version>
<nacos.version>1.1.3</nacos.version> <nacos.version>1.1.3</nacos.version>
<dubbo.version>2.7.8</dubbo.version> <dubbo.version>2.7.8</dubbo.version>
<graalvm.version>20.1.0</graalvm.version> <graalvm.version>20.1.0</graalvm.version>
@ -276,18 +276,11 @@
<artifactId>spring-boot-starter-data-ldap</artifactId> <artifactId>spring-boot-starter-data-ldap</artifactId>
</dependency> </dependency>
<!-- swagger2 解析 --> <!-- swagger 解析 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-parser</artifactId>
<version>1.0.51</version>
</dependency>
<!-- swagger3 解析 最新版本会有swagger-core版本冲突 -->
<dependency> <dependency>
<groupId>io.swagger.parser.v3</groupId> <groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser</artifactId> <artifactId>swagger-parser</artifactId>
<version>2.0.18</version> <version>2.0.22</version>
</dependency> </dependency>
<!-- 执行 js 代码依赖 --> <!-- 执行 js 代码依赖 -->

View File

@ -74,11 +74,21 @@ public class ApiAutomationController {
apiAutomationService.deleteBatch(ids); apiAutomationService.deleteBatch(ids);
} }
@PostMapping("/deleteBatchByCondition")
public void deleteBatchByCondition(@RequestBody ApiScenarioBatchRequest request) {
apiAutomationService.deleteBatchByCondition(request);
}
@PostMapping("/removeToGc") @PostMapping("/removeToGc")
public void removeToGc(@RequestBody List<String> ids) { public void removeToGc(@RequestBody List<String> ids) {
apiAutomationService.removeToGc(ids); apiAutomationService.removeToGc(ids);
} }
@PostMapping("/removeToGcByBatch")
public void removeToGcByBatch(@RequestBody ApiScenarioBatchRequest request) {
apiAutomationService.removeToGcByBatch(request);
}
@PostMapping("/reduction") @PostMapping("/reduction")
public void reduction(@RequestBody List<String> ids) { public void reduction(@RequestBody List<String> ids) {
apiAutomationService.reduction(ids); apiAutomationService.reduction(ids);

View File

@ -7,12 +7,12 @@ import io.metersphere.api.dto.definition.*;
import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.ApiTestCase; import io.metersphere.base.domain.ApiTestCase;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs; import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
import io.metersphere.track.service.TestPlanApiCaseService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -29,7 +29,8 @@ public class ApiTestCaseController {
@Resource @Resource
private ApiTestCaseService apiTestCaseService; private ApiTestCaseService apiTestCaseService;
@Resource
private TestPlanApiCaseService testPlanApiCaseService;
@PostMapping("/list") @PostMapping("/list")
public List<ApiTestCaseResult> list(@RequestBody ApiTestCaseRequest request) { public List<ApiTestCaseResult> list(@RequestBody ApiTestCaseRequest request) {
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId()); request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
@ -48,6 +49,12 @@ public class ApiTestCaseController {
return null; return null;
} }
} }
@GetMapping("/getStateByTestPlan/{id}")
public String getStateByTestPlan(@PathVariable String id ) {
String status=testPlanApiCaseService.getState(id);
return status;
}
@PostMapping("/list/{goPage}/{pageSize}") @PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<ApiTestCaseDTO>> listSimple(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiTestCaseRequest request) { public Pager<List<ApiTestCaseDTO>> listSimple(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiTestCaseRequest request) {
@ -133,5 +140,6 @@ public class ApiTestCaseController {
@GetMapping(value = "/jenkins/exec/result/{id}") @GetMapping(value = "/jenkins/exec/result/{id}")
public String getExecResult(@PathVariable String id) { public String getExecResult(@PathVariable String id) {
return apiTestCaseService.getExecResult(id); return apiTestCaseService.getExecResult(id);
} }
} }

View File

@ -29,6 +29,7 @@ public class EsbDataStruct {
private boolean required; private boolean required;
private String description; private String description;
private List<EsbDataStruct> children; private List<EsbDataStruct> children;
private String status = "";
public void init(){ public void init(){
this.uuid = UUID.randomUUID().toString(); this.uuid = UUID.randomUUID().toString();
@ -127,6 +128,9 @@ public class EsbDataStruct {
if (element != null) { if (element != null) {
if (this.children == null || this.children.isEmpty()) { if (this.children == null || this.children.isEmpty()) {
if(this.value == null ){
this.value = "";
}
element.addText(this.value); element.addText(this.value);
} else { } else {
for (EsbDataStruct child : children) { for (EsbDataStruct child : children) {

View File

@ -75,6 +75,8 @@ import org.apache.jorphan.collections.HashTree;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*; import java.util.*;
public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> { public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
@ -132,12 +134,68 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
return (HashTree) field.get(scriptWrapper); return (HashTree) field.get(scriptWrapper);
} }
public boolean isProtocolDefaultPort(HTTPSamplerProxy source) {
String portAsString = source.getPropertyAsString("HTTPSampler.port");
if (portAsString != null && !portAsString.isEmpty()) {
return false;
} else {
return true;
}
}
public String url(String protocol, String host, String port, String file) {
protocol = protocol.toLowerCase();
if (StringUtils.isNotEmpty(file) && !file.startsWith("/")) {
file += "/";
}
return protocol + "://" + host + ":" + port + file;
}
public String getUrl(HTTPSamplerProxy source) throws MalformedURLException {
String path = source.getPath();
if (!path.startsWith("http://") && !path.startsWith("https://")) {
String domain = source.getDomain();
String protocol = source.getProtocol();
String method = source.getMethod();
StringBuilder pathAndQuery = new StringBuilder(100);
if ("file".equalsIgnoreCase(protocol)) {
domain = null;
} else if (!path.startsWith("/")) {
pathAndQuery.append('/');
}
pathAndQuery.append(path);
if ("GET".equals(method) || "DELETE".equals(method) || "OPTIONS".equals(method)) {
String queryString = source.getQueryString(source.getContentEncoding());
if (queryString.length() > 0) {
if (path.contains("?")) {
pathAndQuery.append("&");
} else {
pathAndQuery.append("?");
}
pathAndQuery.append(queryString);
}
}
String portAsString = source.getPropertyAsString("HTTPSampler.port");
return this.isProtocolDefaultPort(source) ? new URL(protocol, domain, pathAndQuery.toString()).toExternalForm() : this.url(protocol, domain, portAsString, pathAndQuery.toString());
} else {
return new URL(path).toExternalForm();
}
}
private void convertHttpSampler(MsHTTPSamplerProxy samplerProxy, Object key) { private void convertHttpSampler(MsHTTPSamplerProxy samplerProxy, Object key) {
try { try {
HTTPSamplerProxy source = (HTTPSamplerProxy) key; HTTPSamplerProxy source = (HTTPSamplerProxy) key;
BeanUtils.copyBean(samplerProxy, source); BeanUtils.copyBean(samplerProxy, source);
samplerProxy.setRest(new ArrayList<KeyValue>() {{
this.add(new KeyValue());
}});
samplerProxy.setArguments(new ArrayList<KeyValue>() {{
this.add(new KeyValue());
}});
if (source != null && source.getHTTPFiles().length > 0) { if (source != null && source.getHTTPFiles().length > 0) {
samplerProxy.getBody().setBinary(new ArrayList<>()); samplerProxy.getBody().initBinary();
samplerProxy.getBody().setType(Body.FORM_DATA); samplerProxy.getBody().setType(Body.FORM_DATA);
List<KeyValue> keyValues = new LinkedList<>(); List<KeyValue> keyValues = new LinkedList<>();
for (HTTPFileArg arg : source.getHTTPFiles()) { for (HTTPFileArg arg : source.getHTTPFiles()) {
@ -156,13 +214,15 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
samplerProxy.getBody().setKvs(keyValues); samplerProxy.getBody().setKvs(keyValues);
} }
samplerProxy.setProtocol(RequestType.HTTP); samplerProxy.setProtocol(RequestType.HTTP);
samplerProxy.setPort(source.getPort() + ""); samplerProxy.setPort(source.getPropertyAsString("HTTPSampler.port"));
samplerProxy.setDomain(source.getDomain());
if (source.getArguments() != null) { if (source.getArguments() != null) {
if (source.getPostBodyRaw()) { if (source.getPostBodyRaw()) {
samplerProxy.getBody().setType(Body.RAW); samplerProxy.getBody().setType(Body.RAW);
source.getArguments().getArgumentsAsMap().forEach((k, v) -> { source.getArguments().getArgumentsAsMap().forEach((k, v) -> {
samplerProxy.getBody().setRaw(v); samplerProxy.getBody().setRaw(v);
}); });
samplerProxy.getBody().initKvs();
} else { } else {
List<KeyValue> keyValues = new LinkedList<>(); List<KeyValue> keyValues = new LinkedList<>();
source.getArguments().getArgumentsAsMap().forEach((k, v) -> { source.getArguments().getArgumentsAsMap().forEach((k, v) -> {
@ -173,11 +233,12 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
samplerProxy.setArguments(keyValues); samplerProxy.setArguments(keyValues);
} }
} }
samplerProxy.getBody().initBinary();
} }
samplerProxy.setPath(""); // samplerProxy.setPath(source.getPath());
samplerProxy.setMethod(source.getMethod()); samplerProxy.setMethod(source.getMethod());
if (source.getUrl() != null) { if (this.getUrl(source) != null) {
samplerProxy.setUrl(source.getUrl().toString()); samplerProxy.setUrl(this.getUrl(source));
} }
samplerProxy.setId(UUID.randomUUID().toString()); samplerProxy.setId(UUID.randomUUID().toString());
samplerProxy.setType("HTTPSamplerProxy"); samplerProxy.setType("HTTPSamplerProxy");

View File

@ -58,6 +58,7 @@ public class MsDefinitionParser extends MsAbstractParser<ApiDefinitionImport> {
private ApiDefinitionImport parseMsFormat(String testStr, ApiTestImportRequest importRequest) { private ApiDefinitionImport parseMsFormat(String testStr, ApiTestImportRequest importRequest) {
ApiDefinitionImport apiDefinitionImport = JSON.parseObject(testStr, ApiDefinitionImport.class); ApiDefinitionImport apiDefinitionImport = JSON.parseObject(testStr, ApiDefinitionImport.class);
Map<String, List<ApiTestCaseWithBLOBs>> caseMap = new HashMap<>(); Map<String, List<ApiTestCaseWithBLOBs>> caseMap = new HashMap<>();
if (apiDefinitionImport.getCases() != null) {
apiDefinitionImport.getCases().forEach(item -> { apiDefinitionImport.getCases().forEach(item -> {
List<ApiTestCaseWithBLOBs> caseList = caseMap.get(item.getApiDefinitionId()); List<ApiTestCaseWithBLOBs> caseList = caseMap.get(item.getApiDefinitionId());
if (caseList == null) { if (caseList == null) {
@ -66,6 +67,7 @@ public class MsDefinitionParser extends MsAbstractParser<ApiDefinitionImport> {
} }
caseList.add(item); caseList.add(item);
}); });
}
apiDefinitionImport.getData().forEach(apiDefinition -> { apiDefinitionImport.getData().forEach(apiDefinition -> {
parseApiDefinition(apiDefinition, importRequest, caseMap); parseApiDefinition(apiDefinition, importRequest, caseMap);
}); });

View File

@ -70,8 +70,9 @@ public class Swagger2Parser extends SwaggerAbstractParser {
parseParameters(operation, request); parseParameters(operation, request);
addBodyHeader(request); addBodyHeader(request);
if (StringUtils.isNotBlank(basePath)) { if (StringUtils.isNotBlank(basePath)) {
apiDefinition.setPath(basePath + apiDefinition.getPath()); String pathStr = basePath + apiDefinition.getPath().replaceAll("//","/");
request.setPath(basePath + request.getPath()); apiDefinition.setPath(pathStr);
request.setPath(pathStr);
} }
apiDefinition.setRequest(JSON.toJSONString(request)); apiDefinition.setRequest(JSON.toJSONString(request));
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation, operation.getResponses()))); apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation, operation.getResponses())));

View File

@ -114,17 +114,19 @@ public class MsScenario extends MsTestElement {
// 设置共享cookie // 设置共享cookie
config.setEnableCookieShare(enableCookieShare); config.setEnableCookieShare(enableCookieShare);
Map<String, EnvironmentConfig> envConfig = new HashMap<>(16); Map<String, EnvironmentConfig> envConfig = new HashMap<>(16);
if (config.getConfig() == null) {
// 兼容历史数据 // 兼容历史数据
if (environmentMap == null || environmentMap.isEmpty()) { if (this.environmentMap == null || this.environmentMap.isEmpty()) {
environmentMap = new HashMap<>(16); this.environmentMap = new HashMap<>(16);
if (StringUtils.isNotBlank(environmentId)) { if (StringUtils.isNotBlank(environmentId)) {
environmentMap.put(SessionUtils.getCurrentProjectId(), environmentId); // 兼容1.8之前 没有environmentMap但有environmentId的数据
this.environmentMap.put("historyProjectID", environmentId);
} }
} }
if (environmentMap != null && !environmentMap.isEmpty()) { if (this.environmentMap != null && !this.environmentMap.isEmpty()) {
environmentMap.keySet().forEach(projectId -> { this.environmentMap.keySet().forEach(projectId -> {
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentMap.get(projectId)); ApiTestEnvironmentWithBLOBs environment = environmentService.get(this.environmentMap.get(projectId));
if (environment != null && environment.getConfig() != null) { if (environment != null && environment.getConfig() != null) {
EnvironmentConfig env = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); EnvironmentConfig env = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
envConfig.put(projectId, env); envConfig.put(projectId, env);
@ -132,6 +134,7 @@ public class MsScenario extends MsTestElement {
}); });
config.setConfig(envConfig); config.setConfig(envConfig);
} }
}
if (CollectionUtils.isNotEmpty(this.getVariables())) { if (CollectionUtils.isNotEmpty(this.getVariables())) {
config.setVariables(this.variables); config.setVariables(this.variables);
} }

View File

@ -213,7 +213,7 @@ public abstract class MsTestElement {
csvDataSet.setName(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName()); csvDataSet.setName(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName());
csvDataSet.setProperty("fileEncoding", StringUtils.isEmpty(item.getEncoding()) ? "UTF-8" : item.getEncoding()); csvDataSet.setProperty("fileEncoding", StringUtils.isEmpty(item.getEncoding()) ? "UTF-8" : item.getEncoding());
if (CollectionUtils.isNotEmpty(item.getFiles())) { if (CollectionUtils.isNotEmpty(item.getFiles())) {
if (!config.isOperating() && new File(BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()).exists()) { if (!config.isOperating() && !new File(BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()).exists()) {
MSException.throwException(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName() + "[ CSV文件不存在 ]"); MSException.throwException(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName() + "[ CSV文件不存在 ]");
} }
csvDataSet.setProperty("filename", BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()); csvDataSet.setProperty("filename", BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName());

View File

@ -49,7 +49,7 @@ public class MsIfController extends MsTestElement {
ifController.setName(this.getName()); ifController.setName(this.getName());
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName()); ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel")); ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
ifController.setCondition("true"); ifController.setCondition(this.getCondition());
ifController.setEvaluateAll(false); ifController.setEvaluateAll(false);
ifController.setUseExpression(true); ifController.setUseExpression(true);
return ifController; return ifController;

View File

@ -91,6 +91,9 @@ public class MsHTTPSamplerProxy extends MsTestElement {
@JSONField(ordinal = 36) @JSONField(ordinal = 36)
private MsAuthManager authManager; private MsAuthManager authManager;
@JSONField(ordinal = 37)
private boolean urlOrPath;
@Override @Override
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) { public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
// 非导出操作且不是启用状态则跳过执行 // 非导出操作且不是启用状态则跳过执行
@ -126,6 +129,11 @@ public class MsHTTPSamplerProxy extends MsTestElement {
config.setConfig(getEnvironmentConfig(useEnvironment)); config.setConfig(getEnvironmentConfig(useEnvironment));
} }
// 1.8 之前历史数据
if(StringUtils.isEmpty(this.getProjectId()) && config.getConfig()!= null && !config.getConfig().isEmpty()){
this.setProjectId("historyProjectID");
}
// 添加环境中的公共变量 // 添加环境中的公共变量
Arguments arguments = this.addArguments(config); Arguments arguments = this.addArguments(config);
if (arguments != null) { if (arguments != null) {
@ -140,23 +148,26 @@ public class MsHTTPSamplerProxy extends MsTestElement {
url = this.getUrl(); url = this.getUrl();
isUrl = true; isUrl = true;
} }
URL urlObject = new URL(url);
if (isUrl) { if (isUrl) {
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
url.replaceAll(this.getPort(), "10990");
}
URL urlObject = new URL(url);
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8")); sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8"));
if (urlObject.getPort() > 0) { if (urlObject.getPort() > 0 && urlObject.getPort() != 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
sampler.setPort(urlObject.getPort()); sampler.setPort(urlObject.getPort());
} else {
sampler.setProperty("HTTPSampler.port", this.getPort());
} }
sampler.setProtocol(urlObject.getProtocol()); sampler.setProtocol(urlObject.getProtocol());
sampler.setPath(urlObject.getPath());
} else { } else {
sampler.setDomain(config.getConfig().get(this.getProjectId()).getHttpConfig().getDomain()); sampler.setDomain(config.getConfig().get(this.getProjectId()).getHttpConfig().getDomain());
sampler.setPort(config.getConfig().get(this.getProjectId()).getHttpConfig().getPort()); sampler.setPort(config.getConfig().get(this.getProjectId()).getHttpConfig().getPort());
sampler.setProtocol(config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol()); sampler.setProtocol(config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol());
sampler.setPath(this.getPath());
} }
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath(); String envPath = sampler.getPath();
if (StringUtils.isNotBlank(this.getPath()) && !isUrl) {
envPath += this.getPath();
sampler.setPath(envPath);
}
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) { if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
envPath = getRestParameters(URLDecoder.decode(envPath, "UTF-8")); envPath = getRestParameters(URLDecoder.decode(envPath, "UTF-8"));
sampler.setPath(envPath); sampler.setPath(envPath);
@ -177,9 +188,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
if (!url.startsWith("http://") && !url.startsWith("https://")) { if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "http://" + url; url = "http://" + url;
} }
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
url.replaceAll(this.getPort(), "10990");
}
URL urlObject = new URL(url); URL urlObject = new URL(url);
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8")); sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8"));
if (urlObject.getPort() > 0 && urlObject.getPort() != 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
sampler.setPort(urlObject.getPort()); sampler.setPort(urlObject.getPort());
} else {
sampler.setProperty("HTTPSampler.port", this.getPort());
}
sampler.setProtocol(urlObject.getProtocol()); sampler.setProtocol(urlObject.getProtocol());
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath(); String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
sampler.setPath(envPath); sampler.setPath(envPath);
@ -327,10 +345,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
} }
public boolean isURL(String str) { public boolean isURL(String str) {
//转换为小写
try { try {
new URL(str); String regex = "^((https|http|ftp|rtsp|mms)?://)"
return true; + "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?"
+ "(([0-9]{1,3}\\.){3}[0-9]{1,3}" + "|" + "([0-9a-z_!~*'()-]+\\.)*"
+ "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\."
+ "[a-z]{2,6})"
+ "(:[0-9]{1,5})?"
+ "((/?)|"
+ "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
return str.matches(regex) || (str.matches("^(http|https|ftp)://.*$") && str.matches(".*://\\$\\{.*$"));
} catch (Exception e) { } catch (Exception e) {
return false; return false;
} }
@ -339,5 +363,5 @@ public class MsHTTPSamplerProxy extends MsTestElement {
private boolean isRest() { private boolean isRest() {
return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0; return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0;
} }
} }

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto.definition.request.sampler;
import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType; import com.alibaba.fastjson.annotation.JSONType;
import io.metersphere.api.dto.automation.EsbDataStruct;
import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.ParameterConfig; import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor; import io.metersphere.api.dto.definition.request.processors.pre.MsJSR223PreProcessor;
@ -70,6 +71,12 @@ public class MsTCPSampler extends MsTestElement {
@JSONField(ordinal = 39) @JSONField(ordinal = 39)
private String projectId; private String projectId;
/**
* 新加两个参数场景保存/修改时需要的参数不会传递JMeter只是用于最后的保留
*/
private List<EsbDataStruct> esbDataStruct;
private List<EsbDataStruct> backEsbDataStruct;
@Override @Override
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) { public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
// 非导出操作且不是启用状态则跳过执行 // 非导出操作且不是启用状态则跳过执行

View File

@ -65,15 +65,12 @@ public class Body {
sampler.setHTTPFiles(httpFileArgs(requestId)); sampler.setHTTPFiles(httpFileArgs(requestId));
sampler.setDoMultipart(true); sampler.setDoMultipart(true);
} }
} else {
if (!this.isJson()) {
sampler.setPostBodyRaw(true);
} else { } else {
if (StringUtils.isNotEmpty(this.format) && "JSON-SCHEMA".equals(this.format) && this.getJsonSchema() != null) { if (StringUtils.isNotEmpty(this.format) && "JSON-SCHEMA".equals(this.format) && this.getJsonSchema() != null) {
this.raw = JSONSchemaGenerator.getJson(com.alibaba.fastjson.JSON.toJSONString(this.getJsonSchema())); this.raw = JSONSchemaGenerator.getJson(com.alibaba.fastjson.JSON.toJSONString(this.getJsonSchema()));
} }
}
KeyValue keyValue = new KeyValue("", "JSON-SCHEMA", this.getRaw(), true, true); KeyValue keyValue = new KeyValue("", "JSON-SCHEMA", this.getRaw(), true, true);
sampler.setPostBodyRaw(true);
keyValue.setEnable(true); keyValue.setEnable(true);
keyValue.setEncode(false); keyValue.setEncode(false);
body.add(keyValue); body.add(keyValue);

View File

@ -1,12 +1,10 @@
package io.metersphere.api.jmeter; package io.metersphere.api.jmeter;
import io.metersphere.api.dto.definition.ApiTestCaseInfo;
import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.service.*; import io.metersphere.api.service.*;
import io.metersphere.base.domain.ApiDefinitionExecResult; import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.base.domain.ApiScenarioReport; import io.metersphere.base.domain.ApiScenarioReport;
import io.metersphere.base.domain.ApiTestReport; import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.base.domain.TestPlanReport;
import io.metersphere.commons.constants.*; import io.metersphere.commons.constants.*;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
@ -16,7 +14,6 @@ import io.metersphere.notice.sender.NoticeModel;
import io.metersphere.notice.service.NoticeSendService; import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService; import io.metersphere.service.SystemParameterService;
import io.metersphere.track.service.TestPlanReportService; import io.metersphere.track.service.TestPlanReportService;
import io.metersphere.track.service.TestPlanService;
import io.metersphere.track.service.TestPlanTestCaseService; import io.metersphere.track.service.TestPlanTestCaseService;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -201,7 +198,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
apiDefinitionService.addResult(testResult); apiDefinitionService.addResult(testResult);
//测试计划定时任务-接口执行逻辑的话需要同步测试计划的报告数据 //测试计划定时任务-接口执行逻辑的话需要同步测试计划的报告数据
if (StringUtils.equals(this.runMode, ApiRunMode.SCHEDULE_API_PLAN.name())) { if (StringUtils.equalsAny(this.runMode, ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name())) {
apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult, ApiRunMode.SCHEDULE_API_PLAN.name()); apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult, ApiRunMode.SCHEDULE_API_PLAN.name());
List<String> testPlanReportIdList = new ArrayList<>(); List<String> testPlanReportIdList = new ArrayList<>();
testPlanReportIdList.add(debugReportId); testPlanReportIdList.add(debugReportId);
@ -250,10 +247,16 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
} }
} }
if (StringUtils.equals(ReportTriggerMode.API.name(), report.getTriggerMode())||StringUtils.equals(ReportTriggerMode.SCHEDULE.name(), report.getTriggerMode())) {
sendTask(report, reportUrl, testResult); sendTask(report, reportUrl, testResult);
} }
}
private static void sendTask(ApiTestReport report, String reportUrl, TestResult testResult) { private static void sendTask(ApiTestReport report, String reportUrl, TestResult testResult) {
if (report == null) {
return;
}
SystemParameterService systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class); SystemParameterService systemParameterService = CommonBeanFactory.getBean(SystemParameterService.class);
NoticeSendService noticeSendService = CommonBeanFactory.getBean(NoticeSendService.class); NoticeSendService noticeSendService = CommonBeanFactory.getBean(NoticeSendService.class);
assert systemParameterService != null; assert systemParameterService != null;

View File

@ -90,6 +90,8 @@ public class ApiAutomationService {
@Resource @Resource
@Lazy @Lazy
private TestPlanScenarioCaseService testPlanScenarioCaseService; private TestPlanScenarioCaseService testPlanScenarioCaseService;
@Resource
private EsbApiParamService esbApiParamService;
public List<ApiScenarioDTO> list(ApiScenarioRequest request) { public List<ApiScenarioDTO> list(ApiScenarioRequest request) {
request = this.initRequest(request, true, true); request = this.initRequest(request, true, true);
@ -184,6 +186,9 @@ public class ApiAutomationService {
scenario.setCreateTime(System.currentTimeMillis()); scenario.setCreateTime(System.currentTimeMillis());
scenario.setNum(getNextNum(request.getProjectId())); scenario.setNum(getNextNum(request.getProjectId()));
//检查场景的请求步骤如果含有ESB请求步骤的话要做参数计算处理
esbApiParamService.checkScenarioRequests(request);
apiScenarioMapper.insert(scenario); apiScenarioMapper.insert(scenario);
List<String> bodyUploadIds = request.getBodyUploadIds(); List<String> bodyUploadIds = request.getBodyUploadIds();
@ -205,6 +210,9 @@ public class ApiAutomationService {
List<String> bodyUploadIds = request.getBodyUploadIds(); List<String> bodyUploadIds = request.getBodyUploadIds();
FileUtils.createBodyFiles(bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
//检查场景的请求步骤如果含有ESB请求步骤的话要做参数计算处理
esbApiParamService.checkScenarioRequests(request);
final ApiScenarioWithBLOBs scenario = buildSaveScenario(request); final ApiScenarioWithBLOBs scenario = buildSaveScenario(request);
apiScenarioMapper.updateByPrimaryKeySelective(scenario); apiScenarioMapper.updateByPrimaryKeySelective(scenario);
extScheduleMapper.updateNameByResourceID(request.getId(), request.getName());// 修改场景name同步到修改首页定时任务 extScheduleMapper.updateNameByResourceID(request.getId(), request.getName());// 修改场景name同步到修改首页定时任务
@ -234,6 +242,10 @@ public class ApiAutomationService {
} else { } else {
scenario.setUserId(request.getUserId()); scenario.setUserId(request.getUserId());
} }
if (StringUtils.isEmpty(request.getApiScenarioModuleId()) || StringUtils.isEmpty(request.getModulePath())) {
scenario.setApiScenarioModuleId("root");
scenario.setModulePath("/默认模块");
}
return scenario; return scenario;
} }
@ -371,6 +383,9 @@ public class ApiAutomationService {
public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID) { public APIScenarioReportResult createScenarioReport(String id, String scenarioId, String scenarioName, String triggerMode, String execType, String projectId, String userID) {
APIScenarioReportResult report = new APIScenarioReportResult(); APIScenarioReportResult report = new APIScenarioReportResult();
if (triggerMode.equals(ApiRunMode.SCENARIO.name()) || triggerMode.equals(ApiRunMode.DEFINITION.name())) {
triggerMode = ReportTriggerMode.MANUAL.name();
}
report.setId(id); report.setId(id);
report.setTestId(id); report.setTestId(id);
if (StringUtils.isNotEmpty(scenarioName)) { if (StringUtils.isNotEmpty(scenarioName)) {
@ -998,4 +1013,18 @@ public class ApiAutomationService {
} }
}); });
} }
public void removeToGcByBatch(ApiScenarioBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query));
this.removeToGc(request.getIds());
}
public void deleteBatchByCondition(ApiScenarioBatchRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiScenarioMapper.selectIdsByQuery((ApiScenarioRequest) query));
this.deleteBatch(request.getIds());
}
} }

View File

@ -3,10 +3,7 @@ package io.metersphere.api.service;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult; import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult;
import io.metersphere.api.jmeter.TestResult; import io.metersphere.api.jmeter.TestResult;
import io.metersphere.base.domain.ApiDefinitionExecResult; import io.metersphere.base.domain.*;
import io.metersphere.base.domain.ApiDefinitionExecResultExample;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiTestCaseMapper; import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper; import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
@ -73,6 +70,13 @@ public class ApiDefinitionExecResultService {
testCaseReviewApiCaseService.setExecResult(item.getName(), status); testCaseReviewApiCaseService.setExecResult(item.getName(), status);
} }
// 清空上次执行结果的内容只保留当前最新一条内容
ApiDefinitionExecResult prevResult = extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(item.getName(), type);
if (prevResult != null) {
prevResult.setContent(null);
definitionExecResultMapper.updateByPrimaryKeyWithBLOBs(prevResult);
}
// 更新用例最后执行结果 // 更新用例最后执行结果
ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = new ApiTestCaseWithBLOBs(); ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = new ApiTestCaseWithBLOBs();
apiTestCaseWithBLOBs.setId(saveResult.getResourceId()); apiTestCaseWithBLOBs.setId(saveResult.getResourceId());
@ -127,6 +131,12 @@ public class ApiDefinitionExecResultService {
} }
saveResult.setUserId(userID); saveResult.setUserId(userID);
// 前一条数据内容清空
ApiDefinitionExecResult prevResult = extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(item.getName(), finalSaveResultType);
if (prevResult != null) {
prevResult.setContent(null);
apiDefinitionExecResultMapper.updateByPrimaryKeyWithBLOBs(prevResult);
}
apiDefinitionExecResultMapper.insert(saveResult); apiDefinitionExecResultMapper.insert(saveResult);
}); });
} }

View File

@ -262,7 +262,10 @@ public class ApiDefinitionService {
test.setEnvironmentId(request.getEnvironmentId()); test.setEnvironmentId(request.getEnvironmentId());
test.setUserId(request.getUserId()); test.setUserId(request.getUserId());
test.setTags(request.getTags()); test.setTags(request.getTags());
if (StringUtils.isEmpty(request.getModulePath()) || StringUtils.isEmpty(request.getModuleId())) {
test.setModulePath("/默认模块");
test.setModuleId("root");
}
apiDefinitionMapper.updateByPrimaryKeySelective(test); apiDefinitionMapper.updateByPrimaryKeySelective(test);
return test; return test;
} }
@ -279,7 +282,6 @@ public class ApiDefinitionService {
test.setProtocol(request.getProtocol()); test.setProtocol(request.getProtocol());
test.setMethod(request.getMethod()); test.setMethod(request.getMethod());
test.setPath(request.getPath()); test.setPath(request.getPath());
test.setModuleId(request.getModuleId());
test.setProjectId(request.getProjectId()); test.setProjectId(request.getProjectId());
request.getRequest().setId(request.getId()); request.getRequest().setId(request.getId());
test.setRequest(JSONObject.toJSONString(request.getRequest())); test.setRequest(JSONObject.toJSONString(request.getRequest()));
@ -287,6 +289,11 @@ public class ApiDefinitionService {
test.setUpdateTime(System.currentTimeMillis()); test.setUpdateTime(System.currentTimeMillis());
test.setStatus(APITestStatus.Underway.name()); test.setStatus(APITestStatus.Underway.name());
test.setModulePath(request.getModulePath()); test.setModulePath(request.getModulePath());
test.setModuleId(request.getModuleId());
if (StringUtils.isEmpty(request.getModulePath()) || StringUtils.isEmpty(request.getModuleId())) {
test.setModulePath("/默认模块");
test.setModuleId("root");
}
test.setResponse(JSONObject.toJSONString(request.getResponse())); test.setResponse(JSONObject.toJSONString(request.getResponse()));
test.setEnvironmentId(request.getEnvironmentId()); test.setEnvironmentId(request.getEnvironmentId());
test.setNum(getNextNum(request.getProjectId())); test.setNum(getNextNum(request.getProjectId()));
@ -392,6 +399,7 @@ public class ApiDefinitionService {
apiDefinition.setRequest(JSONObject.toJSONString(msHTTPSamplerProxy)); apiDefinition.setRequest(JSONObject.toJSONString(msHTTPSamplerProxy));
return request; return request;
} }
private String setImportTCPHashTree(ApiDefinitionWithBLOBs apiDefinition) { private String setImportTCPHashTree(ApiDefinitionWithBLOBs apiDefinition) {
String request = apiDefinition.getRequest(); String request = apiDefinition.getRequest();
MsTCPSampler tcpSampler = JSONObject.parseObject(request, MsTCPSampler.class); MsTCPSampler tcpSampler = JSONObject.parseObject(request, MsTCPSampler.class);
@ -509,7 +517,7 @@ public class ApiDefinitionService {
} }
public void addResult(TestResult res) { public void addResult(TestResult res) {
if (!res.getScenarios().isEmpty() && !res.getScenarios().get(0).getRequestResults().isEmpty()) { if (res != null && CollectionUtils.isNotEmpty(res.getScenarios()) && res.getScenarios().get(0) != null && CollectionUtils.isNotEmpty(res.getScenarios().get(0).getRequestResults())) {
RequestResult result = res.getScenarios().get(0).getRequestResults().get(0); RequestResult result = res.getScenarios().get(0).getRequestResults().get(0);
if (result.getName().indexOf("<->") != -1) { if (result.getName().indexOf("<->") != -1) {
result.setName(result.getName().substring(0, result.getName().indexOf("<->"))); result.setName(result.getName().substring(0, result.getName().indexOf("<->")));
@ -596,6 +604,10 @@ public class ApiDefinitionService {
} }
for (int i = 0; i < data.size(); i++) { for (int i = 0; i < data.size(); i++) {
ApiDefinitionWithBLOBs item = data.get(i); ApiDefinitionWithBLOBs item = data.get(i);
if (StringUtils.isEmpty(item.getModuleId()) || StringUtils.isEmpty(item.getModulePath())) {
item.setModuleId("root");
item.setModulePath("/默认模块");
}
if (item.getName().length() > 255) { if (item.getName().length() > 255) {
item.setName(item.getName().substring(0, 255)); item.setName(item.getName().substring(0, 255));
} }

View File

@ -86,12 +86,12 @@ public class ApiDocumentService {
apiInfoDTO.setStatus(apiModel.getStatus()); apiInfoDTO.setStatus(apiModel.getStatus());
if (apiModel.getRequest() != null) { if (apiModel.getRequest() != null) {
JSONObject requestJsonObj = JSONObject.parseObject(apiModel.getRequest()); JSONObject requestObj = this.genJSONObject(apiModel.getRequest());
//head赋值conversionModelToDTO if(requestObj!=null){
if (requestJsonObj.containsKey("headers")) { if (requestObj.containsKey("headers")) {
JSONArray requestHeadDataArr = new JSONArray(); JSONArray requestHeadDataArr = new JSONArray();
//head赋值 //head赋值
JSONArray headArr = requestJsonObj.getJSONArray("headers"); JSONArray headArr = requestObj.getJSONArray("headers");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
JSONObject headObj = headArr.getJSONObject(index); JSONObject headObj = headArr.getJSONObject(index);
if (headObj.containsKey("name") && headObj.containsKey("value")) { if (headObj.containsKey("name") && headObj.containsKey("value")) {
@ -102,30 +102,37 @@ public class ApiDocumentService {
} }
//url参数赋值 //url参数赋值
JSONArray urlParamArr = new JSONArray(); JSONArray urlParamArr = new JSONArray();
if (requestJsonObj.containsKey("arguments")) { if (requestObj.containsKey("arguments")) {
//urlParam -- query赋值 try{
JSONArray headArr = requestJsonObj.getJSONArray("arguments"); JSONArray headArr = requestObj.getJSONArray("arguments");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
JSONObject headObj = headArr.getJSONObject(index); JSONObject headObj = headArr.getJSONObject(index);
if (headObj.containsKey("name") && headObj.containsKey("value")) { if (headObj.containsKey("name") && headObj.containsKey("value")) {
urlParamArr.add(headObj); urlParamArr.add(headObj);
} }
} }
}catch (Exception e){
} }
if (requestJsonObj.containsKey("rest")) { }
if (requestObj.containsKey("rest")) {
try{
//urlParam -- rest赋值 //urlParam -- rest赋值
JSONArray headArr = requestJsonObj.getJSONArray("rest"); JSONArray headArr = requestObj.getJSONArray("rest");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
JSONObject headObj = headArr.getJSONObject(index); JSONObject headObj = headArr.getJSONObject(index);
if (headObj.containsKey("name") && headObj.containsKey("value")) { if (headObj.containsKey("name") && headObj.containsKey("value")) {
urlParamArr.add(headObj); urlParamArr.add(headObj);
} }
} }
}catch (Exception e){
}
} }
apiInfoDTO.setUrlParams(urlParamArr.toJSONString()); apiInfoDTO.setUrlParams(urlParamArr.toJSONString());
//请求体参数类型 //请求体参数类型
if (requestJsonObj.containsKey("body")) { if (requestObj.containsKey("body")) {
JSONObject bodyObj = requestJsonObj.getJSONObject("body"); try{
JSONObject bodyObj = requestObj.getJSONObject("body");
if (bodyObj.containsKey("type")) { if (bodyObj.containsKey("type")) {
String type = bodyObj.getString("type"); String type = bodyObj.getString("type");
if (StringUtils.equals(type, "WWW_FORM")) { if (StringUtils.equals(type, "WWW_FORM")) {
@ -160,7 +167,6 @@ public class ApiDocumentService {
if (bodyObj.containsKey("raw")) { if (bodyObj.containsKey("raw")) {
String raw = bodyObj.getString("raw"); String raw = bodyObj.getString("raw");
apiInfoDTO.setRequestBodyStrutureData(raw); apiInfoDTO.setRequestBodyStrutureData(raw);
JSONObject previewObj = JSONObject.parseObject(raw);
this.setPreviewData(previewJsonArray, raw); this.setPreviewData(previewJsonArray, raw);
} }
} else if (StringUtils.equalsAny(type, "Form Data", "WWW_FORM")) { } else if (StringUtils.equalsAny(type, "Form Data", "WWW_FORM")) {
@ -211,13 +217,19 @@ public class ApiDocumentService {
} }
} }
} }
}catch (Exception e){
}
}
} }
} }
//赋值响应头 //赋值响应头
if (apiModel.getResponse() != null) { if (apiModel.getResponse() != null) {
JSONObject responseJsonObj = JSONObject.parseObject(apiModel.getResponse()); JSONObject responseJsonObj = this.genJSONObject(apiModel.getResponse());
if (responseJsonObj!=null && responseJsonObj.containsKey("headers")) { if (responseJsonObj!=null && responseJsonObj.containsKey("headers")) {
try{
JSONArray responseHeadDataArr = new JSONArray(); JSONArray responseHeadDataArr = new JSONArray();
JSONArray headArr = responseJsonObj.getJSONArray("headers"); JSONArray headArr = responseJsonObj.getJSONArray("headers");
for (int index = 0; index < headArr.size(); index++) { for (int index = 0; index < headArr.size(); index++) {
@ -227,9 +239,13 @@ public class ApiDocumentService {
} }
} }
apiInfoDTO.setResponseHead(responseHeadDataArr.toJSONString()); apiInfoDTO.setResponseHead(responseHeadDataArr.toJSONString());
}catch (Exception e){
}
} }
// 赋值响应体 // 赋值响应体
if (responseJsonObj!=null && responseJsonObj.containsKey("body")) { if (responseJsonObj!=null && responseJsonObj.containsKey("body")) {
try {
JSONObject bodyObj = responseJsonObj.getJSONObject("body"); JSONObject bodyObj = responseJsonObj.getJSONObject("body");
if (bodyObj.containsKey("type")) { if (bodyObj.containsKey("type")) {
String type = bodyObj.getString("type"); String type = bodyObj.getString("type");
@ -284,9 +300,14 @@ public class ApiDocumentService {
} }
} }
} }
}catch (Exception e){
}
} }
// 赋值响应码 // 赋值响应码
if (responseJsonObj!=null && responseJsonObj.containsKey("statusCode")) { if (responseJsonObj!=null && responseJsonObj.containsKey("statusCode")) {
try {
JSONArray responseStatusDataArr = new JSONArray(); JSONArray responseStatusDataArr = new JSONArray();
JSONArray statusArr = responseJsonObj.getJSONArray("statusCode"); JSONArray statusArr = responseJsonObj.getJSONArray("statusCode");
for (int index = 0; index < statusArr.size(); index++) { for (int index = 0; index < statusArr.size(); index++) {
@ -296,6 +317,9 @@ public class ApiDocumentService {
} }
} }
apiInfoDTO.setResponseCode(responseStatusDataArr.toJSONString()); apiInfoDTO.setResponseCode(responseStatusDataArr.toJSONString());
}catch (Exception e){
}
} }
} }
} }
@ -304,6 +328,15 @@ public class ApiDocumentService {
return apiInfoDTO; return apiInfoDTO;
} }
private JSONObject genJSONObject(String request) {
JSONObject returnObj = null;
try{
returnObj = JSONObject.parseObject(request);
}catch (Exception e){
}
return returnObj;
}
private void setPreviewData(JSONArray previewArray, String data) { private void setPreviewData(JSONArray previewArray, String data) {
try { try {
JSONObject previewObj = JSONObject.parseObject(data); JSONObject previewObj = JSONObject.parseObject(data);

View File

@ -253,6 +253,13 @@ public class ApiScenarioReportService {
String status = "Success"; String status = "Success";
report.setStatus(status); report.setStatus(status);
scenarioReportMapper.updateByPrimaryKeySelective(report); scenarioReportMapper.updateByPrimaryKeySelective(report);
// 把上一条调试的数据内容清空
ApiScenarioReport prevResult = extApiScenarioReportMapper.selectPreviousReportByScenarioId(report.getScenarioId(), reportId);
if (prevResult != null) {
ApiScenarioReportDetailExample example = new ApiScenarioReportDetailExample();
example.createCriteria().andReportIdEqualTo(prevResult.getId());
apiScenarioReportDetailMapper.deleteByExample(example);
}
}); });
sqlSession.flushStatements(); sqlSession.flushStatements();
} }

View File

@ -19,6 +19,7 @@ import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.*; import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.*; import io.metersphere.base.mapper.ext.*;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.TestPlanStatus; import io.metersphere.commons.constants.TestPlanStatus;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.*;
@ -551,7 +552,14 @@ public class ApiTestCaseService {
} }
public String run(RunCaseRequest request) { public String run(RunCaseRequest request) {
ApiTestCaseWithBLOBs testCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getCaseId()); ApiTestCaseWithBLOBs testCaseWithBLOBs=new ApiTestCaseWithBLOBs();
if(StringUtils.equals(request.getRunMode(), ApiRunMode.JENKINS_API_PLAN.name())){
testCaseWithBLOBs= apiTestCaseMapper.selectByPrimaryKey(request.getReportId());
request.setCaseId(request.getReportId());
}else{
testCaseWithBLOBs= apiTestCaseMapper.selectByPrimaryKey(request.getCaseId());
}
// 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取 // 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取
if (testCaseWithBLOBs != null && StringUtils.isNotEmpty(testCaseWithBLOBs.getRequest())) { if (testCaseWithBLOBs != null && StringUtils.isNotEmpty(testCaseWithBLOBs.getRequest())) {
try { try {

View File

@ -3,11 +3,13 @@ package io.metersphere.api.service;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.automation.EsbDataStruct; import io.metersphere.api.dto.automation.EsbDataStruct;
import io.metersphere.api.dto.automation.SaveApiScenarioRequest;
import io.metersphere.api.dto.automation.parse.EsbDataParser; import io.metersphere.api.dto.automation.parse.EsbDataParser;
import io.metersphere.api.dto.definition.ApiDefinitionResult; import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.ApiTestCaseResult; import io.metersphere.api.dto.definition.ApiTestCaseResult;
import io.metersphere.api.dto.definition.SaveApiDefinitionRequest; import io.metersphere.api.dto.definition.SaveApiDefinitionRequest;
import io.metersphere.api.dto.definition.SaveApiTestCaseRequest; import io.metersphere.api.dto.definition.SaveApiTestCaseRequest;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs; import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
@ -313,6 +315,35 @@ public class EsbApiParamService {
return keyValueList; return keyValueList;
} }
private List<KeyValue> genKeyValueListByDataStruct(MsTCPSampler tcpSampler, List<EsbDataStruct> dataStructRequestList) {
List<KeyValue> keyValueList = new ArrayList<>();
String sendRequest = tcpSampler.getRequest();
String paramRegexStr = "\\$\\{([^}]*)\\}";
try {
if (StringUtils.isNotEmpty(sendRequest)) {
List<String> paramList = new ArrayList<>();
Pattern regex = Pattern.compile(paramRegexStr);
Matcher matcher = regex.matcher(sendRequest);
while (matcher.find()) {
paramList.add(matcher.group(1));
}
for (String param : paramList) {
String value = this.genValueFromEsbDataStructByParam(dataStructRequestList, param);
if (StringUtils.isNotEmpty(value)) {
KeyValue kv = new KeyValue();
kv.setName(param);
kv.setValue(value);
kv.setRequired(true);
keyValueList.add(kv);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return keyValueList;
}
//通过报文模版中的变量参数解析报文数据结构生成对应的xml数据 //通过报文模版中的变量参数解析报文数据结构生成对应的xml数据
private String genValueFromEsbDataStructByParam(List<EsbDataStruct> dataStructRequestList, String param) { private String genValueFromEsbDataStructByParam(List<EsbDataStruct> dataStructRequestList, String param) {
String returnValue = ""; String returnValue = "";
@ -341,10 +372,32 @@ public class EsbApiParamService {
return request; return request;
} }
public void handleEsbRequest(MsTCPSampler tcpSampler) {
try {
//修改reqeust.parameters, 将树结构类型数据转化为表格类型数据供执行时参数的提取
if (tcpSampler.getEsbDataStruct() != null ) {
List<KeyValue> keyValueList = this.genKeyValueListByDataStruct(tcpSampler, tcpSampler.getEsbDataStruct());
tcpSampler.setParameters(keyValueList);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void deleteByResourceIdIn(List<String> apiIds) { public void deleteByResourceIdIn(List<String> apiIds) {
EsbApiParamsExample example = new EsbApiParamsExample(); EsbApiParamsExample example = new EsbApiParamsExample();
example.createCriteria().andResourceIdIn(apiIds); example.createCriteria().andResourceIdIn(apiIds);
esbApiParamsMapper.deleteByExample(example); esbApiParamsMapper.deleteByExample(example);
} }
public void checkScenarioRequests(SaveApiScenarioRequest request) {
if(request.getScenarioDefinition() != null ){
List<MsTestElement> hashTreeList = request.getScenarioDefinition().getHashTree();
for (MsTestElement testElement :hashTreeList) {
if(testElement instanceof MsTCPSampler){
this.handleEsbRequest((MsTCPSampler)testElement);
}
}
}
}
} }

View File

@ -76,12 +76,17 @@ public class HistoricalDataUpgradeService {
return scenario; return scenario;
} }
private MsScenario createScenario(Scenario oldScenario) { private MsScenario createScenario(Scenario oldScenario, String projectId) {
MsScenario scenario = new MsScenario(); MsScenario scenario = new MsScenario();
scenario.setOldVariables(oldScenario.getVariables()); scenario.setOldVariables(oldScenario.getVariables());
scenario.setName(oldScenario.getName()); scenario.setName(oldScenario.getName());
scenario.setEnableCookieShare(oldScenario.isEnableCookieShare()); scenario.setEnableCookieShare(oldScenario.isEnableCookieShare());
scenario.setEnvironmentId(oldScenario.getEnvironmentId()); scenario.setEnvironmentId(oldScenario.getEnvironmentId());
if (StringUtils.isNotEmpty(oldScenario.getEnvironmentId())) {
HashMap<String, String> envMap = new HashMap<>();
envMap.put(projectId, oldScenario.getEnvironmentId());
scenario.setEnvironmentMap(envMap);
}
scenario.setReferenced("Upgrade"); scenario.setReferenced("Upgrade");
scenario.setId(oldScenario.getId()); scenario.setId(oldScenario.getId());
scenario.setResourceId(UUID.randomUUID().toString()); scenario.setResourceId(UUID.randomUUID().toString());
@ -397,6 +402,7 @@ public class HistoricalDataUpgradeService {
MsScenario scenarioTest = createScenarioByTest(test); MsScenario scenarioTest = createScenarioByTest(test);
LinkedList<MsTestElement> listSteps = new LinkedList<>(); LinkedList<MsTestElement> listSteps = new LinkedList<>();
List<Scenario> scenarios = JSON.parseArray(test.getScenarioDefinition(), Scenario.class); List<Scenario> scenarios = JSON.parseArray(test.getScenarioDefinition(), Scenario.class);
String envId = null;
if (CollectionUtils.isNotEmpty(scenarios)) { if (CollectionUtils.isNotEmpty(scenarios)) {
// 批量处理 // 批量处理
for (Scenario scenario : scenarios) { for (Scenario scenario : scenarios) {
@ -405,7 +411,7 @@ public class HistoricalDataUpgradeService {
} }
scenario.setId(test.getId() + "=" + scenario.getId()); scenario.setId(test.getId() + "=" + scenario.getId());
scenario.setName(test.getName() + "_" + scenario.getName()); scenario.setName(test.getName() + "_" + scenario.getName());
MsScenario scenario1 = createScenario(scenario); MsScenario scenario1 = createScenario(scenario, saveHistoricalDataUpgrade.getProjectId());
String scenarioDefinition = JSON.toJSONString(scenario1); String scenarioDefinition = JSON.toJSONString(scenario1);
num++; num++;
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario.getId(), scenario.getName(), scenario.getRequests().size(), scenarioDefinition, mapper, num); createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario.getId(), scenario.getName(), scenario.getRequests().size(), scenarioDefinition, mapper, num);
@ -417,10 +423,20 @@ public class HistoricalDataUpgradeService {
step.setResourceId(UUID.randomUUID().toString()); step.setResourceId(UUID.randomUUID().toString());
step.setReferenced("REF"); step.setReferenced("REF");
listSteps.add(step); listSteps.add(step);
if (StringUtils.isNotEmpty(scenario.getEnvironmentId())) {
envId = scenario.getEnvironmentId();
}
} }
} }
num++; num++;
scenarioTest.setHashTree(listSteps); scenarioTest.setHashTree(listSteps);
if (StringUtils.isNotEmpty(envId)) {
HashMap<String, String> envMap = new HashMap<>();
envMap.put(saveHistoricalDataUpgrade.getProjectId(), envId);
scenarioTest.setEnvironmentMap(envMap);
scenarioTest.setEnvironmentId(envId);
}
String scenarioDefinition = JSON.toJSONString(scenarioTest); String scenarioDefinition = JSON.toJSONString(scenarioTest);
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenarioTest.getId(), scenarioTest.getName(), listSteps.size(), scenarioDefinition, mapper, num); createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenarioTest.getId(), scenarioTest.getName(), listSteps.size(), scenarioDefinition, mapper, num);
} }

View File

@ -1,17 +1,18 @@
package io.metersphere.base.domain; package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import java.io.Serializable;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true) @ToString(callSuper = true)
public class TestCaseWithBLOBs extends TestCase implements Serializable { public class TestCaseWithBLOBs extends TestCase implements Serializable {
private String remark; private String remark;
private String steps; private String steps; //与TestCaseExcelData里的属性名不一致BeanUtils.copyBean()复制不了值需要手动赋值
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
} }

View File

@ -3,6 +3,7 @@ package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.QueryAPIReportRequest; import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.dto.automation.APIScenarioReportResult; import io.metersphere.api.dto.automation.APIScenarioReportResult;
import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.base.domain.ApiScenarioReport; import io.metersphere.base.domain.ApiScenarioReport;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Select;
@ -23,4 +24,7 @@ public interface ExtApiScenarioReportMapper {
List<ApiDataCountResult> countByProjectIdGroupByExecuteResult(String projectId); List<ApiDataCountResult> countByProjectIdGroupByExecuteResult(String projectId);
List<ApiScenarioReport> selectLastReportByIds(@Param("scenarioIdList") List<String> ids); List<ApiScenarioReport> selectLastReportByIds(@Param("scenarioIdList") List<String> ids);
ApiScenarioReport selectPreviousReportByScenarioId(@Param("scenarioId") String scenarioId, @Param("nowId") String nowId);
} }

View File

@ -216,4 +216,9 @@
) orderData ON orderData.id = report.id; ) orderData ON orderData.id = report.id;
</select> </select>
<select id="selectPreviousReportByScenarioId" resultType="io.metersphere.base.domain.ApiScenarioReport">
select * from api_scenario_report
WHERE execute_type in ("Completed","Debug") and scenario_id=#{scenarioId} and id != #{nowId} ORDER BY create_time desc LIMIT 1
</select>
</mapper> </mapper>

View File

@ -91,6 +91,9 @@
<if test="reportRequest.projectId != null"> <if test="reportRequest.projectId != null">
AND project.id = #{reportRequest.projectId,jdbcType=VARCHAR} AND project.id = #{reportRequest.projectId,jdbcType=VARCHAR}
</if> </if>
<if test="reportRequest.testId != null">
AND ltr.test_id = #{reportRequest.testId,jdbcType=VARCHAR}
</if>
<if test="reportRequest.filters != null and reportRequest.filters.size() > 0"> <if test="reportRequest.filters != null and reportRequest.filters.size() > 0">
<foreach collection="reportRequest.filters.entrySet()" index="key" item="values"> <foreach collection="reportRequest.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0"> <if test="values != null and values.size() > 0">

View File

@ -274,7 +274,7 @@
#{value} #{value}
</foreach> </foreach>
</when> </when>
<when test="key=='status'"> <when test="key=='reviewStatus'">
and test_case.review_status in and test_case.review_status in
<foreach collection="values" item="value" separator="," open="(" close=")"> <foreach collection="values" item="value" separator="," open="(" close=")">
#{value} #{value}

View File

@ -1,6 +1,7 @@
package io.metersphere.commons.user; package io.metersphere.commons.user;
import io.metersphere.commons.utils.CodingUtil; import io.metersphere.commons.utils.CodingUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.UserDTO; import io.metersphere.dto.UserDTO;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -28,7 +29,7 @@ public class SessionUser extends UserDTO implements Serializable {
SessionUser sessionUser = new SessionUser(); SessionUser sessionUser = new SessionUser();
BeanUtils.copyProperties(user, sessionUser); BeanUtils.copyProperties(user, sessionUser);
List<String> infos = Arrays.asList(user.getId(), RandomStringUtils.random(6), "" + System.currentTimeMillis()); List<String> infos = Arrays.asList(user.getId(), RandomStringUtils.randomAlphabetic(6), SessionUtils.getSessionId(), "" + System.currentTimeMillis());
sessionUser.csrfToken = CodingUtil.aesEncrypt(StringUtils.join(infos, "|"), secret, iv); sessionUser.csrfToken = CodingUtil.aesEncrypt(StringUtils.join(infos, "|"), secret, iv);
return sessionUser; return sessionUser;
} }

View File

@ -10,7 +10,6 @@ import org.apache.shiro.subject.support.DefaultSubjectContext;
import java.util.Collection; import java.util.Collection;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import static io.metersphere.commons.constants.SessionConstants.ATTR_USER; import static io.metersphere.commons.constants.SessionConstants.ATTR_USER;
@ -32,6 +31,10 @@ public class SessionUtils {
} }
} }
public static String getSessionId() {
return (String) SecurityUtils.getSubject().getSession().getId();
}
private static Session getSessionByUsername(String username) { private static Session getSessionByUsername(String username) {
DefaultSessionManager sessionManager = CommonBeanFactory.getBean(DefaultSessionManager.class); DefaultSessionManager sessionManager = CommonBeanFactory.getBean(DefaultSessionManager.class);
Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions(); Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();

View File

@ -10,7 +10,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class KafkaProperties { public class KafkaProperties {
public static final String KAFKA_PREFIX = "kafka"; public static final String KAFKA_PREFIX = "kafka";
private String acks = "all"; private String acks = "0"; // 不要设置all
private String topic; private String topic;
private String fields; private String fields;
private String timestamp; private String timestamp;

View File

@ -9,5 +9,7 @@ public class ExcelResponse<T> {
private Boolean success; private Boolean success;
private List<ExcelErrData<T>> errList; private List<ExcelErrData<T>> errList;
private Boolean isUpdated; //是否有更新过用例
} }

View File

@ -8,6 +8,8 @@ import lombok.Setter;
@Setter @Setter
public class TestCaseExcelData { public class TestCaseExcelData {
@ExcelIgnore
private Integer num;
@ExcelIgnore @ExcelIgnore
private String name; private String name;
@ExcelIgnore @ExcelIgnore

View File

@ -13,6 +13,10 @@ import javax.validation.constraints.Pattern;
@ColumnWidth(15) @ColumnWidth(15)
public class TestCaseExcelDataCn extends TestCaseExcelData { public class TestCaseExcelDataCn extends TestCaseExcelData {
@ExcelProperty("ID")
@NotRequired
private Integer num;
@NotBlank(message = "{cannot_be_null}") @NotBlank(message = "{cannot_be_null}")
@Length(max = 255) @Length(max = 255)
@ExcelProperty("用例名称") @ExcelProperty("用例名称")

View File

@ -13,6 +13,10 @@ import javax.validation.constraints.Pattern;
@ColumnWidth(15) @ColumnWidth(15)
public class TestCaseExcelDataTw extends TestCaseExcelData { public class TestCaseExcelDataTw extends TestCaseExcelData {
@ExcelProperty("ID")
@NotRequired
private Integer num;
@NotBlank(message = "{cannot_be_null}") @NotBlank(message = "{cannot_be_null}")
@Length(max = 255) @Length(max = 255)
@ExcelProperty("用例名稱") @ExcelProperty("用例名稱")

View File

@ -14,6 +14,10 @@ import javax.validation.constraints.Pattern;
@ColumnWidth(15) @ColumnWidth(15)
public class TestCaseExcelDataUs extends TestCaseExcelData { public class TestCaseExcelDataUs extends TestCaseExcelData {
@ExcelProperty("ID")
@NotRequired
private Integer num;
@NotBlank(message = "{cannot_be_null}") @NotBlank(message = "{cannot_be_null}")
@Length(max = 255) @Length(max = 255)
@ExcelProperty("Name") @ExcelProperty("Name")

View File

@ -1,12 +1,16 @@
package io.metersphere.excel.listener; package io.metersphere.excel.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.commons.constants.TestCaseConstants; import io.metersphere.commons.constants.TestCaseConstants;
import io.metersphere.commons.utils.BeanUtils; import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.TestCaseExcelData; import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.utils.ExcelValidateHelper;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import io.metersphere.track.service.TestCaseService; import io.metersphere.track.service.TestCaseService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -22,10 +26,18 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
private String projectId; private String projectId;
protected List<TestCaseExcelData> updateList = new ArrayList<>(); //存储待更新用例的集合
protected boolean isUpdated = false; //判断是否更新过用例将会传给前端
Set<String> testCaseNames; Set<String> testCaseNames;
Set<String> userIds; Set<String> userIds;
public boolean isUpdated() {
return isUpdated;
}
public TestCaseDataListener(Class clazz, String projectId, Set<String> testCaseNames, Set<String> userIds) { public TestCaseDataListener(Class clazz, String projectId, Set<String> testCaseNames, Set<String> userIds) {
this.clazz = clazz; this.clazz = clazz;
this.testCaseService = (TestCaseService) CommonBeanFactory.getBean("testCaseService"); this.testCaseService = (TestCaseService) CommonBeanFactory.getBean("testCaseService");
@ -39,12 +51,15 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
String nodePath = data.getNodePath(); String nodePath = data.getNodePath();
StringBuilder stringBuilder = new StringBuilder(errMsg); StringBuilder stringBuilder = new StringBuilder(errMsg);
//校验所属模块"
if (nodePath != null) { if (nodePath != null) {
String[] nodes = nodePath.split("/"); String[] nodes = nodePath.split("/");
//校验模块深度
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) { if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
stringBuilder.append(Translator.get("test_case_node_level_tip") + stringBuilder.append(Translator.get("test_case_node_level_tip") +
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; "); TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; ");
} }
//模块名不能为空
for (int i = 0; i < nodes.length; i++) { for (int i = 0; i < nodes.length; i++) {
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) { if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
stringBuilder.append(Translator.get("module_not_null") + "; "); stringBuilder.append(Translator.get("module_not_null") + "; ");
@ -57,10 +72,39 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
// stringBuilder.append(Translator.get("functional_method_tip") + "; "); // stringBuilder.append(Translator.get("functional_method_tip") + "; ");
// } // }
//校验维护人
if (!userIds.contains(data.getMaintainer())) { if (!userIds.contains(data.getMaintainer())) {
stringBuilder.append(Translator.get("user_not_exists") + "" + data.getMaintainer() + "; "); stringBuilder.append(Translator.get("user_not_exists") + "" + data.getMaintainer() + "; ");
} }
/*
校验Excel中是否有ID
有的话校验ID是否已在当前项目中存在存在则更新用例
不存在则继续校验看是否重复不重复则新建用例
*/
if (null != data.getNum()) { //当前读取的数据有ID
if (null != testCaseService.checkIdExist(data.getNum(), projectId)) { //该ID在当前项目中存在
//如果前面所经过的校验都没报错
if (StringUtils.isEmpty(stringBuilder)) {
updateList.add(data); //将当前数据存入更新列表
stringBuilder.append("update_testcase"); //该信息用于在invoke方法中判断是否该更新用例
}
return stringBuilder.toString();
} else {
/*
该ID在当前数据库中不存在应当继续校验用例是否重复,
在下面的校验过程中num的值会被用于判断是否重复所以应当先设置为null
*/
data.setNum(null);
}
}
/*
校验用例
*/
if (testCaseNames.contains(data.getName())) { if (testCaseNames.contains(data.getName())) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs(); TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data); BeanUtils.copyBean(testCase, data);
@ -96,18 +140,27 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
@Override @Override
public void saveData() { public void saveData() {
//无错误数据才插入数据 //excel中用例都有错误时就返回只要有用例可用于更新或者插入就不返回
if (!errList.isEmpty()) { if (!errList.isEmpty() && list.size() == 0 && updateList.size() == 0) {
return; return;
} }
Collections.reverse(list); if (!(list.size() == 0)){
Collections.reverse(list); //因为saveImportData里面是先分配最大的ID这个ID应该先发给list中最后的数据所以要reverse
List<TestCaseWithBLOBs> result = list.stream() List<TestCaseWithBLOBs> result = list.stream()
.map(item -> this.convert2TestCase(item)) .map(item -> this.convert2TestCase(item))
.collect(Collectors.toList()); .collect(Collectors.toList());
testCaseService.saveImportData(result, projectId); testCaseService.saveImportData(result, projectId);
}
if (!(updateList.size() == 0)) {
List<TestCaseWithBLOBs> result2 = updateList.stream()
.map(item -> this.convert2TestCaseForUpdate(item))
.collect(Collectors.toList());
testCaseService.updateImportDataCarryId(result2, projectId);
this.isUpdated = true;
updateList.clear();
}
} }
@ -137,6 +190,32 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
return testCase; return testCase;
} }
/**
* 将Excel中的数据对象转换为用于更新操作的用例数据对象
* @param data
* @return
*/
private TestCaseWithBLOBs convert2TestCaseForUpdate(TestCaseExcelData data) {
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
BeanUtils.copyBean(testCase, data);
testCase.setProjectId(this.projectId);
testCase.setUpdateTime(System.currentTimeMillis());
String nodePath = data.getNodePath();
if (!nodePath.startsWith("/")) {
nodePath = "/" + nodePath;
}
if (nodePath.endsWith("/")) {
nodePath = nodePath.substring(0, nodePath.length() - 1);
}
testCase.setNodePath(nodePath);
String steps = getSteps(data);
testCase.setSteps(steps);
return testCase;
}
public String getSteps(TestCaseExcelData data) { public String getSteps(TestCaseExcelData data) {
JSONArray jsonArray = new JSONArray(); JSONArray jsonArray = new JSONArray();
@ -189,4 +268,38 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
return jsonArray.toJSONString(); return jsonArray.toJSONString();
} }
@Override
public void invoke(TestCaseExcelData testCaseExcelData, AnalysisContext analysisContext) {
String errMsg;
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
String updateMsg = "update_testcase";
try {
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
errMsg = ExcelValidateHelper.validateEntity(testCaseExcelData);
//自定义校验规则
errMsg = validate(testCaseExcelData, errMsg);
} catch (NoSuchFieldException e) {
errMsg = Translator.get("parse_data_error");
LogUtil.error(e.getMessage(), e);
}
if (!StringUtils.isEmpty(errMsg)) {
//如果errMsg只有"update testcase"说明用例待更新
if (!errMsg.equals(updateMsg)){
ExcelErrData excelErrData = new ExcelErrData(testCaseExcelData, rowIndex,
Translator.get("number") + " " + rowIndex + " " + Translator.get("row") + Translator.get("error")
+ "" + errMsg);
errList.add(excelErrData);
}
} else {
list.add(testCaseExcelData);
}
if (list.size() > BATCH_COUNT) {
saveData();
list.clear();
}
}
} }

View File

@ -12,6 +12,7 @@ import java.util.Map;
public class ReportRequest { public class ReportRequest {
private String name; private String name;
private String workspaceId; private String workspaceId;
private String testId;
private String userId; private String userId;
private List<OrderRequest> orders; private List<OrderRequest> orders;
private Map<String, List<String>> filters; private Map<String, List<String>> filters;

View File

@ -127,6 +127,7 @@ public class DockerTestEngine extends AbstractEngine {
Integer port = node.getPort(); Integer port = node.getPort();
String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port); String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port);
try {
ResultHolder result = restTemplateWithTimeOut.getForObject(uri, ResultHolder.class); ResultHolder result = restTemplateWithTimeOut.getForObject(uri, ResultHolder.class);
if (result == null) { if (result == null) {
MSException.throwException(Translator.get("container_delete_fail")); MSException.throwException(Translator.get("container_delete_fail"));
@ -134,6 +135,11 @@ public class DockerTestEngine extends AbstractEngine {
if (!result.isSuccess()) { if (!result.isSuccess()) {
MSException.throwException(result.getMessage()); MSException.throwException(result.getMessage());
} }
} catch (MSException e) {
throw e;
} catch (Exception e) {
MSException.throwException("Please check node-controller status.");
}
}); });
} }

View File

@ -71,11 +71,14 @@ public class CsrfFilter extends AnonymousFilter {
csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv); csrfToken = CodingUtil.aesDecrypt(csrfToken, SessionUser.secret, SessionUser.iv);
String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|"); String[] signatureArray = StringUtils.split(StringUtils.trimToNull(csrfToken), "|");
if (signatureArray.length != 3) { if (signatureArray.length != 4) {
throw new RuntimeException("invalid token"); throw new RuntimeException("invalid token");
} }
if (!StringUtils.equals(SessionUtils.getUserId(), signatureArray[0])) { if (!StringUtils.equals(SessionUtils.getUserId(), signatureArray[0])) {
throw new RuntimeException("Please check csrf token."); throw new RuntimeException("Please check csrf token.");
} }
if (!StringUtils.equals(SessionUtils.getSessionId(), signatureArray[2])) {
throw new RuntimeException("Please check csrf token.");
}
} }
} }

View File

@ -7,13 +7,13 @@ import io.metersphere.base.mapper.TestResourceMapper;
import io.metersphere.commons.constants.ResourceStatusEnum; import io.metersphere.commons.constants.ResourceStatusEnum;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.controller.ResultHolder;
import io.metersphere.dto.NodeDTO; import io.metersphere.dto.NodeDTO;
import io.metersphere.dto.TestResourcePoolDTO; import io.metersphere.dto.TestResourcePoolDTO;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -71,12 +71,18 @@ public class NodeResourcePoolService {
private boolean validateNode(NodeDTO node) { private boolean validateNode(NodeDTO node) {
try { try {
ResponseEntity<String> entity = restTemplateWithTimeOut.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), String.class); ResponseEntity<ResultHolder> entity = restTemplateWithTimeOut.getForEntity(String.format(nodeControllerUrl, node.getIp(), node.getPort()), ResultHolder.class);
return HttpStatus.OK.equals(entity.getStatusCode()); ResultHolder body = entity.getBody();
} catch (Exception e) { if (body == null) {
LogUtil.error(e.getMessage(), e);
return false; return false;
} }
if (body.getData() != null && StringUtils.equalsIgnoreCase("OK", body.getData().toString())) {
return true;
}
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
}
return false;
} }
private void updateTestResource(TestResource testResource) { private void updateTestResource(TestResource testResource) {

View File

@ -14,13 +14,11 @@ import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.service.CheckPermissionService; import io.metersphere.service.CheckPermissionService;
import io.metersphere.service.FileService; import io.metersphere.service.FileService;
import io.metersphere.track.dto.TestCaseDTO; import io.metersphere.track.dto.TestCaseDTO;
import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.request.testcase.EditTestCaseRequest; import io.metersphere.track.request.testcase.EditTestCaseRequest;
import io.metersphere.track.request.testcase.QueryTestCaseRequest; import io.metersphere.track.request.testcase.QueryTestCaseRequest;
import io.metersphere.track.request.testcase.TestCaseBatchRequest; import io.metersphere.track.request.testcase.TestCaseBatchRequest;
import io.metersphere.track.request.testcase.TestCaseMinderEditRequest; import io.metersphere.track.request.testcase.TestCaseMinderEditRequest;
import io.metersphere.track.request.testplan.FileOperationRequest; import io.metersphere.track.request.testplan.FileOperationRequest;
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
import io.metersphere.track.service.TestCaseService; import io.metersphere.track.service.TestCaseService;
import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles; import org.apache.shiro.authz.annotation.RequiresRoles;
@ -126,8 +124,8 @@ public class TestCaseController {
@PostMapping(value = "/edit", consumes = {"multipart/form-data"}) @PostMapping(value = "/edit", consumes = {"multipart/form-data"})
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void editTestCase(@RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file") List<MultipartFile> files) { public String editTestCase(@RequestPart("request") EditTestCaseRequest request, @RequestPart(value = "file") List<MultipartFile> files) {
testCaseService.edit(request, files); return testCaseService.edit(request, files);
} }
@PostMapping("/delete/{testCaseId}") @PostMapping("/delete/{testCaseId}")

View File

@ -92,7 +92,7 @@ public class TestPlanController {
@PostMapping("/edit") @PostMapping("/edit")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void editTestPlan(@RequestBody TestPlanDTO testPlanDTO) { public void editTestPlan(@RequestBody TestPlanDTO testPlanDTO) {
testPlanService.editTestPlan(testPlanDTO); testPlanService.editTestPlan(testPlanDTO, true);
} }
@PostMapping("/edit/status/{planId}") @PostMapping("/edit/status/{planId}")

View File

@ -2,22 +2,20 @@ package io.metersphere.track.controller;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.TestCaseReport;
import io.metersphere.base.domain.TestPlanReport; import io.metersphere.base.domain.TestPlanReport;
import io.metersphere.commons.constants.ReportTriggerMode; import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.utils.PageUtils; import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager; import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.track.dto.TestCaseReportMetricDTO;
import io.metersphere.track.dto.TestPlanDTOWithMetric;
import io.metersphere.track.dto.TestPlanReportDTO; import io.metersphere.track.dto.TestPlanReportDTO;
import io.metersphere.track.request.report.QueryTestPlanReportRequest; import io.metersphere.track.request.report.QueryTestPlanReportRequest;
import io.metersphere.track.request.testcase.QueryTestPlanRequest; import io.metersphere.track.request.report.TestPlanReportSaveRequest;
import io.metersphere.track.service.TestPlanReportService; import io.metersphere.track.service.TestPlanReportService;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.UUID;
/** /**
* @author song.tianyang * @author song.tianyang
@ -62,14 +60,18 @@ public class TestPlanReportController {
@GetMapping("/apiExecuteFinish/{planId}/{userId}") @GetMapping("/apiExecuteFinish/{planId}/{userId}")
public void apiExecuteFinish(@PathVariable String planId,@PathVariable String userId) { public void apiExecuteFinish(@PathVariable String planId,@PathVariable String userId) {
TestPlanReport report = testPlanReportService.genTestPlanReport(planId,userId,ReportTriggerMode.API.name()); String reportId = UUID.randomUUID().toString();
TestPlanReportSaveRequest saveRequest = new TestPlanReportSaveRequest(reportId,planId,userId,ReportTriggerMode.API.name());
TestPlanReport report = testPlanReportService.genTestPlanReport(saveRequest);
testPlanReportService.countReportByTestPlanReportId(report.getId(),null, ReportTriggerMode.API.name()); testPlanReportService.countReportByTestPlanReportId(report.getId(),null, ReportTriggerMode.API.name());
} }
@GetMapping("/saveTestPlanReport/{planId}/{triggerMode}") @GetMapping("/saveTestPlanReport/{planId}/{triggerMode}")
public String saveTestPlanReport(@PathVariable String planId,@PathVariable String triggerMode) { public String saveTestPlanReport(@PathVariable String planId,@PathVariable String triggerMode) {
String userId = SessionUtils.getUser().getId(); String userId = SessionUtils.getUser().getId();
TestPlanReport report = testPlanReportService.genTestPlanReport(planId,userId,triggerMode); String reportId = UUID.randomUUID().toString();
TestPlanReportSaveRequest saveRequest = new TestPlanReportSaveRequest(reportId,planId,userId,triggerMode);
TestPlanReport report = testPlanReportService.genTestPlanReport(saveRequest);
testPlanReportService.countReportByTestPlanReportId(report.getId(),null, triggerMode); testPlanReportService.countReportByTestPlanReportId(report.getId(),null, triggerMode);
return "success"; return "success";
} }

View File

@ -0,0 +1,53 @@
package io.metersphere.track.request.report;
import lombok.Getter;
import lombok.Setter;
/**
* @author song.tianyang
* @Date 2021/1/8 4:36 下午
* @Description
*/
@Getter
@Setter
public class TestPlanReportSaveRequest {
private String reportID;
private String planId;
private String userId;
private String triggerMode;
private boolean countResources;
private boolean apiCaseIsExecuting;
private boolean scenarioIsExecuting;
private boolean performanceIsExecuting;
private String apiCaseIdListJSON;
private String scenarioIdListJSON;
private String performanceIdListJSON;
public TestPlanReportSaveRequest(String reportID, String planId, String userId, String triggerMode) {
this.reportID = reportID;
this.planId = planId;
this.userId = userId;
this.triggerMode = triggerMode;
this.countResources = true;
}
public TestPlanReportSaveRequest(String reportID, String planId, String userId, String triggerMode, boolean apiCaseIsExecuting, boolean scenarioIsExecuting, boolean performanceIsExecuting, String apiCaseIdListJSON, String scenarioIdListJSON, String performanceIdListJSON) {
this.reportID = reportID;
this.planId = planId;
this.userId = userId;
this.triggerMode = triggerMode;
this.countResources = false;
this.apiCaseIsExecuting = apiCaseIsExecuting;
this.scenarioIsExecuting = scenarioIsExecuting;
this.performanceIsExecuting = performanceIsExecuting;
this.apiCaseIdListJSON = apiCaseIdListJSON;
this.scenarioIdListJSON = scenarioIdListJSON;
this.performanceIdListJSON = performanceIdListJSON;
}
}

View File

@ -331,20 +331,6 @@ public class TestCaseReviewService {
} }
public void testReviewRelevance(ReviewRelevanceRequest request) { public void testReviewRelevance(ReviewRelevanceRequest request) {
String reviewId = request.getReviewId();
List<String> userIds = getTestCaseReviewerIds(reviewId);
String creator = "";
TestCaseReview review = testCaseReviewMapper.selectByPrimaryKey(reviewId);
if (review != null) {
creator = review.getCreator();
}
String currentId = SessionUtils.getUser().getId();
if (!userIds.contains(currentId) && !StringUtils.equals(creator, currentId)) {
MSException.throwException("没有权限,不能关联用例!");
}
List<String> testCaseIds = request.getTestCaseIds(); List<String> testCaseIds = request.getTestCaseIds();
if (testCaseIds.isEmpty()) { if (testCaseIds.isEmpty()) {

View File

@ -19,7 +19,6 @@ import io.metersphere.excel.domain.ExcelErrData;
import io.metersphere.excel.domain.ExcelResponse; import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.excel.domain.TestCaseExcelData; import io.metersphere.excel.domain.TestCaseExcelData;
import io.metersphere.excel.domain.TestCaseExcelDataFactory; import io.metersphere.excel.domain.TestCaseExcelDataFactory;
import io.metersphere.excel.listener.EasyExcelListener;
import io.metersphere.excel.listener.TestCaseDataListener; import io.metersphere.excel.listener.TestCaseDataListener;
import io.metersphere.excel.utils.EasyExcelExporter; import io.metersphere.excel.utils.EasyExcelExporter;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
@ -126,11 +125,26 @@ public class TestCaseService {
// 全部字段值相同才判断为用例存在 // 全部字段值相同才判断为用例存在
if (testCase != null) { if (testCase != null) {
/*
例如对于/模块5用户的输入可能为模块5或者/模块5/或者模块5/
不这样处理的话下面进行判断时就会用用户输入的错误格式进行判断而模块名为/模块5
模块5/模块5/模块5/它们应该被认为是同一个模块
数据库存储的node_path都是/模块5这种格式的
*/
String nodePath = testCase.getNodePath();
if (!nodePath.startsWith("/")) {
nodePath = "/" + nodePath;
}
if (nodePath.endsWith("/")) {
nodePath = nodePath.substring(0, nodePath.length() - 1);
}
TestCaseExample example = new TestCaseExample(); TestCaseExample example = new TestCaseExample();
TestCaseExample.Criteria criteria = example.createCriteria(); TestCaseExample.Criteria criteria = example.createCriteria();
criteria.andNameEqualTo(testCase.getName()) criteria.andNameEqualTo(testCase.getName())
.andProjectIdEqualTo(testCase.getProjectId()) .andProjectIdEqualTo(testCase.getProjectId())
.andNodePathEqualTo(testCase.getNodePath()) .andNodePathEqualTo(nodePath)
.andTypeEqualTo(testCase.getType()) .andTypeEqualTo(testCase.getType())
.andMaintainerEqualTo(testCase.getMaintainer()) .andMaintainerEqualTo(testCase.getMaintainer())
.andPriorityEqualTo(testCase.getPriority()); .andPriorityEqualTo(testCase.getPriority());
@ -165,7 +179,7 @@ public class TestCaseService {
String steps = tc.getSteps(); String steps = tc.getSteps();
String remark = tc.getRemark(); String remark = tc.getRemark();
if (StringUtils.equals(steps, testCase.getSteps()) && StringUtils.equals(remark, caseRemark)) { if (StringUtils.equals(steps, testCase.getSteps()) && StringUtils.equals(remark, caseRemark)) {
MSException.throwException(Translator.get("test_case_already_exists")); //MSException.throwException(Translator.get("test_case_already_exists"));
isExt = true; isExt = true;
} }
} }
@ -177,6 +191,26 @@ public class TestCaseService {
return null; return null;
} }
/**
* 根据id和pojectId查询id是否在数据库中存在
* 在数据库中单id的话是可重复的,id与projectId的组合是唯一的
*/
public Integer checkIdExist(Integer id, String projectId){
TestCaseExample example = new TestCaseExample();
TestCaseExample.Criteria criteria = example.createCriteria();
if (null != id) {
criteria.andNumEqualTo(id);
criteria.andProjectIdEqualTo(projectId);
long count = testCaseMapper.countByExample(example); //查询是否有包含此ID的数据
if(count == 0){ //如果ID不存在
return null;
}else { //有对应ID的数据
return id;
}
}
return null;
}
public int deleteTestCase(String testCaseId) { public int deleteTestCase(String testCaseId) {
TestPlanTestCaseExample example = new TestPlanTestCaseExample(); TestPlanTestCaseExample example = new TestPlanTestCaseExample();
example.createCriteria().andCaseIdEqualTo(testCaseId); example.createCriteria().andCaseIdEqualTo(testCaseId);
@ -286,6 +320,7 @@ public class TestCaseService {
public ExcelResponse testCaseImport(MultipartFile multipartFile, String projectId, String userId) { public ExcelResponse testCaseImport(MultipartFile multipartFile, String projectId, String userId) {
ExcelResponse excelResponse = new ExcelResponse(); ExcelResponse excelResponse = new ExcelResponse();
boolean isUpdated = false; //判断是否更新了用例
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId(); String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest(); QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
queryTestCaseRequest.setProjectId(projectId); queryTestCaseRequest.setProjectId(projectId);
@ -338,10 +373,15 @@ public class TestCaseService {
Set<String> userIds = userRoleMapper.selectByExample(userRoleExample).stream().map(UserRole::getUserId).collect(Collectors.toSet()); Set<String> userIds = userRoleMapper.selectByExample(userRoleExample).stream().map(UserRole::getUserId).collect(Collectors.toSet());
try { try {
//根据本地语言环境选择用哪种数据对象进行存放读取的数据
Class clazz = new TestCaseExcelDataFactory().getExcelDataByLocal(); Class clazz = new TestCaseExcelDataFactory().getExcelDataByLocal();
EasyExcelListener easyExcelListener = new TestCaseDataListener(clazz, projectId, testCaseNames, userIds);
TestCaseDataListener easyExcelListener = new TestCaseDataListener(clazz, projectId, testCaseNames, userIds);
//读取excel数据
EasyExcelFactory.read(multipartFile.getInputStream(), clazz, easyExcelListener).sheet().doRead(); EasyExcelFactory.read(multipartFile.getInputStream(), clazz, easyExcelListener).sheet().doRead();
errList = easyExcelListener.getErrList(); errList = easyExcelListener.getErrList();
isUpdated = easyExcelListener.isUpdated();
} catch (Exception e) { } catch (Exception e) {
LogUtil.error(e.getMessage(), e); LogUtil.error(e.getMessage(), e);
MSException.throwException(e.getMessage()); MSException.throwException(e.getMessage());
@ -352,6 +392,7 @@ public class TestCaseService {
if (!errList.isEmpty()) { if (!errList.isEmpty()) {
excelResponse.setSuccess(false); excelResponse.setSuccess(false);
excelResponse.setErrList(errList); excelResponse.setErrList(errList);
excelResponse.setIsUpdated(isUpdated);
} else { } else {
excelResponse.setSuccess(true); excelResponse.setSuccess(true);
} }
@ -400,6 +441,43 @@ public class TestCaseService {
sqlSession.flushStatements(); sqlSession.flushStatements();
} }
/**
* 把Excel中带ID的数据更新到数据库
* @param testCases
* @param projectId
*/
public void updateImportDataCarryId(List<TestCaseWithBLOBs> testCases, String projectId) {
Map<String, String> nodePathMap = testCaseNodeService.createNodeByTestCases(testCases, projectId);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class);
/*
获取用例的网页上所显示id数据库ID映射
*/
List<Integer> nums = testCases.stream()
.map(TestCase::getNum)
.collect(Collectors.toList());
TestCaseExample example = new TestCaseExample();
example.createCriteria().andNumIn(nums)
.andProjectIdEqualTo(projectId);
List<TestCase> testCasesList = testCaseMapper.selectByExample(example);
Map<Integer, String> numIdMap = testCasesList.stream()
.collect(Collectors.toMap(TestCase::getNum, TestCase::getId));
if (!testCases.isEmpty()) {
AtomicInteger sort = new AtomicInteger();
testCases.forEach(testcase -> {
testcase.setUpdateTime(System.currentTimeMillis());
testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
testcase.setSort(sort.getAndIncrement());
testcase.setId(numIdMap.get(testcase.getNum()));
mapper.updateByPrimaryKeySelective(testcase);
});
}
sqlSession.flushStatements();
}
public void testCaseTemplateExport(HttpServletResponse response) { public void testCaseTemplateExport(HttpServletResponse response) {
try { try {
@ -510,6 +588,7 @@ public class TestCaseService {
StringBuilder result = new StringBuilder(""); StringBuilder result = new StringBuilder("");
TestCaseList.forEach(t -> { TestCaseList.forEach(t -> {
TestCaseExcelData data = new TestCaseExcelData(); TestCaseExcelData data = new TestCaseExcelData();
data.setNum(t.getNum());
data.setName(t.getName()); data.setName(t.getName());
data.setNodePath(t.getNodePath()); data.setNodePath(t.getNodePath());
data.setPriority(t.getPriority()); data.setPriority(t.getPriority());
@ -533,12 +612,15 @@ public class TestCaseService {
} }
} }
if (CollectionUtils.isNotEmpty(jsonArray)) {
for (int j = 0; j < jsonArray.size(); j++) { for (int j = 0; j < jsonArray.size(); j++) {
int num = j + 1; int num = j + 1;
step.append(num + "." + jsonArray.getJSONObject(j).getString("desc") + "\r\n"); step.append(num + "." + jsonArray.getJSONObject(j).getString("desc") + "\r\n");
result.append(num + "." + jsonArray.getJSONObject(j).getString("result") + "\r\n"); result.append(num + "." + jsonArray.getJSONObject(j).getString("result") + "\r\n");
} }
}
data.setStepDesc(step.toString()); data.setStepDesc(step.toString());
data.setStepResult(result.toString()); data.setStepResult(result.toString());
step.setLength(0); step.setLength(0);

View File

@ -1,21 +1,13 @@
package io.metersphere.track.service; package io.metersphere.track.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest; import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.dto.definition.request.MsThreadGroup;
import io.metersphere.api.service.ApiDefinitionExecResultService; import io.metersphere.api.service.ApiDefinitionExecResultService;
import io.metersphere.api.service.ApiDefinitionService; import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.ApiTestCaseExample;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlanApiCase; import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.base.domain.TestPlanApiCaseExample; import io.metersphere.base.domain.TestPlanApiCaseExample;
import io.metersphere.base.mapper.TestPlanApiCaseMapper; import io.metersphere.base.mapper.TestPlanApiCaseMapper;
@ -25,15 +17,16 @@ import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils; import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest; import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest;
import org.apache.jmeter.testelement.TestElement;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.*; import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -150,4 +143,11 @@ public class TestPlanApiCaseService {
}); });
} }
} }
public String getState(String id) {
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria().andApiCaseIdEqualTo(id);
return testPlanApiCaseMapper.selectByExample(example).get(0).getStatus();
}
} }

View File

@ -2,9 +2,6 @@ package io.metersphere.track.service;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.definition.ApiDefinitionRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.jmeter.TestResult;
import io.metersphere.base.domain.*; import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper; import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper;
@ -21,15 +18,14 @@ import io.metersphere.track.Factory.ReportComponentFactory;
import io.metersphere.track.domain.ReportComponent; import io.metersphere.track.domain.ReportComponent;
import io.metersphere.track.dto.*; import io.metersphere.track.dto.*;
import io.metersphere.track.request.report.QueryTestPlanReportRequest; import io.metersphere.track.request.report.QueryTestPlanReportRequest;
import io.metersphere.track.request.report.TestPlanReportSaveRequest;
import io.metersphere.track.request.testcase.QueryTestPlanRequest; import io.metersphere.track.request.testcase.QueryTestPlanRequest;
import io.metersphere.track.request.testplan.LoadCaseRequest; import io.metersphere.track.request.testplan.LoadCaseRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.python.bouncycastle.pqc.math.linearalgebra.IntUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.lang.reflect.Array;
import java.util.*; import java.util.*;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -80,36 +76,25 @@ public class TestPlanReportService {
} }
/** /**
* 生成测试计划 * @param reportId 报告ID(外部传入
* @param planId * @param planId 测试计划ID
* @param userId * @param userId 用户ID
* @param triggerMode * @param triggerMode 执行方式
* @param countResources 是否统计资源-false的话 下面三个不同资源是否运行则由参数决定 true的话则由统计后的结果决定
* @param apiCaseIsExecuting 接口案例是否执行中
* @param scenarioIsExecuting 场景案例是否执行中
* @param performanceIsExecuting 性能案例是否执行中
* @return * @return
*/ */
public TestPlanReport genTestPlanReport(String planId, String userId,String triggerMode) { public TestPlanReport genTestPlanReport(TestPlanReportSaveRequest saveRequest) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId); TestPlan testPlan = testPlanMapper.selectByPrimaryKey(saveRequest.getPlanId());
testPlan.setExecutionTimes(1); testPlan.setExecutionTimes(1);
testPlan.setExecutionTimes(testPlan.getExecutionTimes() + 1); testPlan.setExecutionTimes(testPlan.getExecutionTimes() + 1);
testPlanMapper.updateByPrimaryKey(testPlan); testPlanMapper.updateByPrimaryKey(testPlan);
TestPlanApiCaseExample apiExample = new TestPlanApiCaseExample();
apiExample.createCriteria().andTestPlanIdEqualTo(planId);
List<String> apiCaseIdList = testPlanApiCaseMapper.selectByExample(apiExample)
.stream().map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList());
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample(); String testPlanReportID = saveRequest.getReportID();
example.createCriteria().andTestPlanIdEqualTo(planId);
List<String> scenarioIdList = testPlanScenarioCaseMapper.selectByExample(example)
.stream().map(TestPlanApiScenario::getApiScenarioId).collect(Collectors.toList());
LoadCaseRequest loadCaseRequest = new LoadCaseRequest();
loadCaseRequest.setTestPlanId(planId);
loadCaseRequest.setProjectId(testPlan.getProjectId());
List<String> performanceIdList = testPlanLoadCaseService.list(loadCaseRequest)
.stream().map(TestPlanLoadCaseDTO::getLoadCaseId).collect(Collectors.toList());
String testPlanReportID = UUID.randomUUID().toString();
TestPlanReport testPlanReport = new TestPlanReport(); TestPlanReport testPlanReport = new TestPlanReport();
testPlanReport.setTestPlanId(planId); testPlanReport.setTestPlanId(saveRequest.getPlanId());
testPlanReport.setId(testPlanReportID); testPlanReport.setId(testPlanReportID);
testPlanReport.setCreateTime(System.currentTimeMillis()); testPlanReport.setCreateTime(System.currentTimeMillis());
testPlanReport.setUpdateTime(System.currentTimeMillis()); testPlanReport.setUpdateTime(System.currentTimeMillis());
@ -117,41 +102,68 @@ public class TestPlanReportService {
testPlanReport.setName(testPlan.getName() + "-" + DateUtils.getTimeString(new Date())); testPlanReport.setName(testPlan.getName() + "-" + DateUtils.getTimeString(new Date()));
} catch (Exception e) { } catch (Exception e) {
} }
testPlanReport.setTriggerMode(triggerMode); testPlanReport.setTriggerMode(saveRequest.getTriggerMode());
testPlanReport.setCreator(userId); testPlanReport.setCreator(saveRequest.getUserId());
testPlanReport.setStartTime(System.currentTimeMillis()); testPlanReport.setStartTime(System.currentTimeMillis());
testPlanReport.setEndTime(System.currentTimeMillis()); testPlanReport.setEndTime(System.currentTimeMillis());
if (apiCaseIdList.isEmpty()) {
testPlanReport.setIsApiCaseExecuting(false);
} else {
testPlanReport.setIsApiCaseExecuting(true);
}
if (scenarioIdList.isEmpty()) {
testPlanReport.setIsScenarioExecuting(false);
} else {
testPlanReport.setIsScenarioExecuting(true);
}
if (performanceIdList.isEmpty()) {
testPlanReport.setIsPerformanceExecuting(false);
} else {
testPlanReport.setIsPerformanceExecuting(true);
}
testPlanReport.setPrincipal(testPlan.getPrincipal());
if(testPlanReport.getIsScenarioExecuting() || testPlanReport.getIsApiCaseExecuting() || testPlanReport.getIsPerformanceExecuting()){
testPlanReport.setStatus(APITestStatus.Starting.name());
}else {
testPlanReport.setStatus(APITestStatus.Completed.name());
}
testPlanReportMapper.insert(testPlanReport);
TestPlanReportDataWithBLOBs testPlanReportData = new TestPlanReportDataWithBLOBs(); TestPlanReportDataWithBLOBs testPlanReportData = new TestPlanReportDataWithBLOBs();
testPlanReportData.setId(UUID.randomUUID().toString()); testPlanReportData.setId(UUID.randomUUID().toString());
testPlanReportData.setTestPlanReportId(testPlanReportID); testPlanReportData.setTestPlanReportId(testPlanReportID);
if (saveRequest.isCountResources()) {
TestPlanApiCaseExample apiExample = new TestPlanApiCaseExample();
apiExample.createCriteria().andTestPlanIdEqualTo(saveRequest.getPlanId());
List<String> apiCaseIdList = testPlanApiCaseMapper.selectByExample(apiExample)
.stream().map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList());
if (apiCaseIdList.isEmpty()) {
testPlanReport.setIsApiCaseExecuting(false);
} else {
testPlanReport.setIsApiCaseExecuting(true);
}
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria().andTestPlanIdEqualTo(saveRequest.getPlanId());
List<String> scenarioIdList = testPlanScenarioCaseMapper.selectByExample(example)
.stream().map(TestPlanApiScenario::getApiScenarioId).collect(Collectors.toList());
if (scenarioIdList.isEmpty()) {
testPlanReport.setIsScenarioExecuting(false);
} else {
testPlanReport.setIsScenarioExecuting(true);
}
LoadCaseRequest loadCaseRequest = new LoadCaseRequest();
loadCaseRequest.setTestPlanId(saveRequest.getPlanId());
loadCaseRequest.setProjectId(testPlan.getProjectId());
List<String> performanceIdList = testPlanLoadCaseService.list(loadCaseRequest)
.stream().map(TestPlanLoadCaseDTO::getLoadCaseId).collect(Collectors.toList());
if (performanceIdList.isEmpty()) {
testPlanReport.setIsPerformanceExecuting(false);
} else {
testPlanReport.setIsPerformanceExecuting(true);
}
testPlanReportData.setApiCaseInfo(JSONArray.toJSONString(apiCaseIdList)); testPlanReportData.setApiCaseInfo(JSONArray.toJSONString(apiCaseIdList));
testPlanReportData.setScenarioInfo(JSONArray.toJSONString(scenarioIdList)); testPlanReportData.setScenarioInfo(JSONArray.toJSONString(scenarioIdList));
testPlanReportData.setPerformanceInfo(JSONArray.toJSONString(performanceIdList)); testPlanReportData.setPerformanceInfo(JSONArray.toJSONString(performanceIdList));
} else {
testPlanReport.setIsApiCaseExecuting(saveRequest.isApiCaseIsExecuting());
testPlanReport.setIsScenarioExecuting(saveRequest.isScenarioIsExecuting());
testPlanReport.setIsPerformanceExecuting(saveRequest.isPerformanceIsExecuting());
testPlanReportData.setApiCaseInfo(saveRequest.getApiCaseIdListJSON());
testPlanReportData.setScenarioInfo(saveRequest.getScenarioIdListJSON());
testPlanReportData.setPerformanceInfo(saveRequest.getPerformanceIdListJSON());
}
testPlanReport.setPrincipal(testPlan.getPrincipal());
if (testPlanReport.getIsScenarioExecuting() || testPlanReport.getIsApiCaseExecuting() || testPlanReport.getIsPerformanceExecuting()) {
testPlanReport.setStatus(APITestStatus.Starting.name());
} else {
testPlanReport.setStatus(APITestStatus.Completed.name());
}
testPlanReportMapper.insert(testPlanReport);
testPlanReportDataMapper.insert(testPlanReportData); testPlanReportDataMapper.insert(testPlanReportData);
//更新TestPlan状态改为进行中 //更新TestPlan状态改为进行中
@ -219,7 +231,6 @@ public class TestPlanReportService {
} }
/** /**
*
* @param planReportId 测试计划报告ID * @param planReportId 测试计划报告ID
* @param resourceRunMode 资源的运行模式,triggerMode非Scedule可以为null * @param resourceRunMode 资源的运行模式,triggerMode非Scedule可以为null
* @param triggerMode 触发方式 ReportTriggerMode.enum * @param triggerMode 触发方式 ReportTriggerMode.enum
@ -324,6 +335,7 @@ public class TestPlanReportService {
/** /**
* 计算测试计划的状态 * 计算测试计划的状态
*
* @param testPlanReport * @param testPlanReport
* @return * @return
*/ */

View File

@ -33,6 +33,7 @@ import io.metersphere.service.SystemParameterService;
import io.metersphere.track.Factory.ReportComponentFactory; import io.metersphere.track.Factory.ReportComponentFactory;
import io.metersphere.track.domain.ReportComponent; import io.metersphere.track.domain.ReportComponent;
import io.metersphere.track.dto.*; import io.metersphere.track.dto.*;
import io.metersphere.track.request.report.TestPlanReportSaveRequest;
import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest; import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest;
import io.metersphere.track.request.testcase.QueryTestPlanRequest; import io.metersphere.track.request.testcase.QueryTestPlanRequest;
import io.metersphere.track.request.testplan.AddTestPlanRequest; import io.metersphere.track.request.testplan.AddTestPlanRequest;
@ -128,6 +129,8 @@ public class TestPlanService {
private ApiScenarioMapper apiScenarioMapper; private ApiScenarioMapper apiScenarioMapper;
@Resource @Resource
private TestCaseTestMapper testCaseTestMapper; private TestCaseTestMapper testCaseTestMapper;
@Resource
private ApiScenarioReportMapper apiScenarioReportMapper;
public synchronized String addTestPlan(AddTestPlanRequest testPlan) { public synchronized String addTestPlan(AddTestPlanRequest testPlan) {
if (getTestPlanByName(testPlan.getName()).size() > 0) { if (getTestPlanByName(testPlan.getName()).size() > 0) {
@ -173,7 +176,7 @@ public class TestPlanService {
return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan()); return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan());
} }
public int editTestPlan(TestPlanDTO testPlan) { public int editTestPlan(TestPlanDTO testPlan, Boolean isSendMessage) {
checkTestPlanExist(testPlan); checkTestPlanExist(testPlan);
TestPlan res = testPlanMapper.selectByPrimaryKey(testPlan.getId()); // 先查一次库 TestPlan res = testPlanMapper.selectByPrimaryKey(testPlan.getId()); // 先查一次库
testPlan.setUpdateTime(System.currentTimeMillis()); testPlan.setUpdateTime(System.currentTimeMillis());
@ -211,7 +214,7 @@ public class TestPlanService {
extScheduleMapper.updateNameByResourceID(testPlan.getId(), testPlan.getName());// 同步更新该测试的定时任务的name extScheduleMapper.updateNameByResourceID(testPlan.getId(), testPlan.getName());// 同步更新该测试的定时任务的name
i = testPlanMapper.updateByPrimaryKeyWithBLOBs(testPlan); // 更新 i = testPlanMapper.updateByPrimaryKeyWithBLOBs(testPlan); // 更新
} }
if (!StringUtils.isBlank(testPlan.getStatus())) { if (!StringUtils.isBlank(testPlan.getStatus()) && isSendMessage) {
BeanUtils.copyBean(testPlans, getTestPlan(testPlan.getId())); BeanUtils.copyBean(testPlans, getTestPlan(testPlan.getId()));
String context = getTestPlanContext(testPlans, NoticeConstants.Event.UPDATE); String context = getTestPlanContext(testPlans, NoticeConstants.Event.UPDATE);
User user = userMapper.selectByPrimaryKey(testPlans.getCreator()); User user = userMapper.selectByPrimaryKey(testPlans.getCreator());
@ -397,7 +400,7 @@ public class TestPlanService {
testPlanDTO.setId(testPlanId); testPlanDTO.setId(testPlanId);
if(statusList.size() == 0) { // 原先status不是prepare, 但删除所有关联用例的情况 if(statusList.size() == 0) { // 原先status不是prepare, 但删除所有关联用例的情况
testPlanDTO.setStatus(TestPlanStatus.Prepare.name()); testPlanDTO.setStatus(TestPlanStatus.Prepare.name());
editTestPlan(testPlanDTO); editTestPlan(testPlanDTO, false);
return; return;
} }
int passNum = 0, prepareNum = 0, failNum = 0; int passNum = 0, prepareNum = 0, failNum = 0;
@ -406,7 +409,7 @@ public class TestPlanService {
|| StringUtils.equals(res, "success") || StringUtils.equals(res, "success")
|| StringUtils.equals(res, ScenarioStatus.Success.name())) { || StringUtils.equals(res, ScenarioStatus.Success.name())) {
passNum++; passNum++;
} else if (res == null) { } else if (res == null || StringUtils.equals(TestPlanStatus.Prepare.name(), res)) {
prepareNum++; prepareNum++;
} else { } else {
failNum++; failNum++;
@ -414,13 +417,13 @@ public class TestPlanService {
} }
if(passNum == statusList.size()) { // 全部通过 if(passNum == statusList.size()) { // 全部通过
testPlanDTO.setStatus(TestPlanStatus.Completed.name()); testPlanDTO.setStatus(TestPlanStatus.Completed.name());
this.editTestPlan(testPlanDTO); this.editTestPlan(testPlanDTO, false);
} else if(prepareNum == 0 && passNum + failNum == statusList.size()) { // 已结束 } else if(prepareNum == 0 && passNum + failNum == statusList.size()) { // 已结束
testPlanDTO.setStatus(TestPlanStatus.Finished.name()); testPlanDTO.setStatus(TestPlanStatus.Finished.name());
editTestPlan(testPlanDTO); editTestPlan(testPlanDTO, false);
} else if(prepareNum != 0) { // 进行中 } else if(prepareNum != 0) { // 进行中
testPlanDTO.setStatus(TestPlanStatus.Underway.name()); testPlanDTO.setStatus(TestPlanStatus.Underway.name());
editTestPlan(testPlanDTO); editTestPlan(testPlanDTO, false);
} }
} }
@ -863,12 +866,13 @@ public class TestPlanService {
* @return * @return
*/ */
public String runScenarioCase(SchedulePlanScenarioExecuteRequest request) { public String runScenarioCase(SchedulePlanScenarioExecuteRequest request) {
String returnId = "";
MsTestPlan testPlan = new MsTestPlan(); MsTestPlan testPlan = new MsTestPlan();
testPlan.setHashTree(new LinkedList<>()); testPlan.setHashTree(new LinkedList<>());
HashTree jmeterHashTree = new ListedHashTree(); HashTree jmeterHashTree = new ListedHashTree();
Map<String, Map<String, String>> testPlanScenarioIdMap = request.getTestPlanScenarioIDMap(); Map<String, Map<String, String>> testPlanScenarioIdMap = request.getTestPlanScenarioIDMap();
for (Map.Entry<String, Map<String, String>> entry : testPlanScenarioIdMap.entrySet()) { for (Map.Entry<String, Map<String, String>> entry : testPlanScenarioIdMap.entrySet()) {
String testPlanID = entry.getKey();
Map<String, String> planScenarioIdMap = entry.getValue(); Map<String, String> planScenarioIdMap = entry.getValue();
List<ApiScenarioWithBLOBs> apiScenarios = extApiScenarioMapper.selectIds(new ArrayList<>(planScenarioIdMap.keySet())); List<ApiScenarioWithBLOBs> apiScenarios = extApiScenarioMapper.selectIds(new ArrayList<>(planScenarioIdMap.keySet()));
try { try {
@ -918,23 +922,27 @@ public class TestPlanService {
scenarios.add(scenario); scenarios.add(scenario);
// 创建场景报告 // 创建场景报告
//不同的运行模式第二个参数入参不同 //不同的运行模式第二个参数入参不同
apiAutomationService.createScenarioReport(group.getName(), APIScenarioReportResult report = apiAutomationService.createScenarioReport(group.getName(),
planScenarioID + ":" + request.getTestPlanReportId(), planScenarioID + ":" + request.getTestPlanReportId(),
item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
request.getExecuteType(), item.getProjectId(), request.getReportUserID()); request.getExecuteType(), item.getProjectId(), request.getReportUserID());
group.setHashTree(scenarios); group.setHashTree(scenarios);
testPlan.getHashTree().add(group); testPlan.getHashTree().add(group);
apiScenarioReportMapper.insert(report);
returnId = request.getId();
} }
} catch (Exception ex) { } catch (Exception ex) {
MSException.throwException(ex.getMessage()); MSException.throwException(ex.getMessage());
} }
}
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig()); testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
String runMode = ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(); String runMode = ApiRunMode.SCHEDULE_SCENARIO_PLAN.name();
// 调用执行方法 // 调用执行方法
jMeterService.runDefinition(request.getId(), jmeterHashTree, request.getReportId(), runMode); jMeterService.runDefinition(request.getId(), jmeterHashTree, request.getReportId(), runMode);
return request.getId(); }
return returnId;
} }
public void run(String testPlanID, String projectID, String userId, String triggerMode) { public void run(String testPlanID, String projectID, String userId, String triggerMode) {
@ -966,37 +974,21 @@ public class TestPlanService {
LogUtil.info("-------------- start testplan schedule ----------"); LogUtil.info("-------------- start testplan schedule ----------");
TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class); TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class);
//首先创建testPlanReport然后返回的ID重新赋值为resourceID作为后续的参数
TestPlanReport testPlanReport = testPlanReportService.genTestPlanReport(testPlanID, userId, triggerMode);
//执行接口案例任务
for (Map.Entry<String, String> entry : apiTestCaseIdMap.entrySet()) {
String apiCaseID = entry.getKey();
String planCaseID = entry.getValue();
ApiTestCaseWithBLOBs blobs = apiTestCaseService.get(apiCaseID);
//需要更新这里来保证PlanCase的状态能正常更改
apiTestCaseService.run(blobs, UUID.randomUUID().toString(), testPlanReport.getId(), testPlanID, ApiRunMode.SCHEDULE_API_PLAN.name());
}
//执行场景执行任务 boolean apiCaseIsExcuting = false;
if (!planScenarioIdMap.isEmpty()) { boolean scenarioIsExcuting = false;
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------"); boolean performaceIsExcuting = false;
SchedulePlanScenarioExecuteRequest scenarioRequest = new SchedulePlanScenarioExecuteRequest(); String apiCaseIdArray = "";
String senarionReportID = UUID.randomUUID().toString(); String scenarioCaseIdArray = "";
scenarioRequest.setId(senarionReportID); String performanceCaseIdArray = "";
scenarioRequest.setReportId(senarionReportID); String planReportId = UUID.randomUUID().toString();
scenarioRequest.setProjectId(projectID); //创建测试报告然后返回的ID重新赋值为resourceID作为后续的参数
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name()); TestPlanReportSaveRequest saveRequest = new TestPlanReportSaveRequest(planReportId,testPlanID,userId,triggerMode,
scenarioRequest.setExecuteType(ExecuteType.Saved.name()); apiTestCaseIdMap.size()>0,planScenarioIdMap.size()>0,performanceIdMap.size()>0,
Map<String, Map<String, String>> testPlanScenarioIdMap = new HashMap<>(); JSONArray.toJSONString(new ArrayList<>(apiTestCaseIdMap.keySet())),JSONArray.toJSONString(new ArrayList<>(planScenarioIdMap.keySet())),JSONArray.toJSONString(new ArrayList<>(performanceIdMap.values())));
testPlanScenarioIdMap.put(testPlanID, planScenarioIdMap);
scenarioRequest.setTestPlanScenarioIDMap(testPlanScenarioIdMap); TestPlanReport testPlanReport = testPlanReportService.genTestPlanReport(saveRequest);
scenarioRequest.setReportUserID(userId);
scenarioRequest.setTestPlanID(testPlanID);
scenarioRequest.setRunMode(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
scenarioRequest.setTestPlanReportId(testPlanReport.getId());
this.runScenarioCase(scenarioRequest);
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
}
//执行性能测试任务 //执行性能测试任务
List<String> performaneReportIDList = new ArrayList<>(); List<String> performaneReportIDList = new ArrayList<>();
for (Map.Entry<String, String> entry : performanceIdMap.entrySet()) { for (Map.Entry<String, String> entry : performanceIdMap.entrySet()) {
@ -1016,20 +1008,76 @@ public class TestPlanService {
testPlanLoadCase.setId(performanceRequest.getTestPlanLoadId()); testPlanLoadCase.setId(performanceRequest.getTestPlanLoadId());
testPlanLoadCase.setLoadReportId(reportId); testPlanLoadCase.setLoadReportId(reportId);
testPlanLoadCaseService.update(testPlanLoadCase); testPlanLoadCaseService.update(testPlanLoadCase);
}
} catch (Exception e) {
e.printStackTrace();
}
//更新关联处的报告 //更新关联处的报告
TestPlanLoadCase loadCase = new TestPlanLoadCaseDTO(); TestPlanLoadCase loadCase = new TestPlanLoadCaseDTO();
loadCase.setId(id); loadCase.setId(id);
loadCase.setLoadReportId(reportId); loadCase.setLoadReportId(reportId);
testPlanLoadCaseService.update(loadCase); testPlanLoadCaseService.update(loadCase);
} }
} catch (Exception e) {
e.printStackTrace();
}
if(StringUtils.isEmpty(reportId)){
performaceIsExcuting = false;
}
}
if(performaceIsExcuting){
performanceCaseIdArray= JSONArray.toJSONString(new ArrayList<>(performanceIdMap.values()));
}
if (!performaneReportIDList.isEmpty()) { if (!performaneReportIDList.isEmpty()) {
//性能测试时保存性能测试报告ID在结果返回时用于捕捉并进行 //性能测试时保存性能测试报告ID在结果返回时用于捕捉并进行
testPlanReportService.updatePerformanceInfo(testPlanReport, performaneReportIDList, ReportTriggerMode.SCHEDULE.name()); testPlanReportService.updatePerformanceInfo(testPlanReport, performaneReportIDList, ReportTriggerMode.SCHEDULE.name());
} }
//执行接口案例任务
for (Map.Entry<String, String> entry : apiTestCaseIdMap.entrySet()) {
String apiCaseID = entry.getKey();
String planCaseID = entry.getValue();
ApiTestCaseWithBLOBs blobs = apiTestCaseService.get(apiCaseID);
//需要更新这里来保证PlanCase的状态能正常更改
apiTestCaseService.run(blobs, UUID.randomUUID().toString(), planReportId, testPlanID, ApiRunMode.SCHEDULE_API_PLAN.name());
apiCaseIsExcuting = true;
}
if(apiCaseIsExcuting){
apiCaseIdArray = JSONArray.toJSONString(new ArrayList<>(apiTestCaseIdMap.keySet()));
}
//执行场景执行任务
if (!planScenarioIdMap.isEmpty()) {
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------");
SchedulePlanScenarioExecuteRequest scenarioRequest = new SchedulePlanScenarioExecuteRequest();
String senarionReportID = UUID.randomUUID().toString();
scenarioRequest.setId(senarionReportID);
scenarioRequest.setReportId(senarionReportID);
scenarioRequest.setProjectId(projectID);
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
scenarioRequest.setExecuteType(ExecuteType.Saved.name());
Map<String, Map<String, String>> testPlanScenarioIdMap = new HashMap<>();
testPlanScenarioIdMap.put(testPlanID, planScenarioIdMap);
scenarioRequest.setTestPlanScenarioIDMap(testPlanScenarioIdMap);
scenarioRequest.setReportUserID(userId);
scenarioRequest.setTestPlanID(testPlanID);
scenarioRequest.setRunMode(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
scenarioRequest.setTestPlanReportId(planReportId);
String scenarioReportID = this.runScenarioCase(scenarioRequest);
if(StringUtils.isNotEmpty(scenarioReportID)){
scenarioIsExcuting = true;
scenarioCaseIdArray= JSONArray.toJSONString(new ArrayList<>(planScenarioIdMap.keySet()));
}
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
}
//如果report参数和预期不对某些原因执行失败则更新report
if(saveRequest.isApiCaseIsExecuting() != apiCaseIsExcuting ||saveRequest.isScenarioIsExecuting()!=scenarioIsExcuting ||saveRequest.isPerformanceIsExecuting() != performaceIsExcuting){
testPlanReport.setIsApiCaseExecuting(apiCaseIsExcuting);
testPlanReport.setIsScenarioExecuting(scenarioIsExcuting);
testPlanReport.setIsPerformanceExecuting(performaceIsExcuting);
testPlanReportService.update(testPlanReport);
}
} }
} }

View File

@ -67,7 +67,7 @@
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer"; import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
import MsMainContainer from "@/business/components/common/components/MsMainContainer"; import MsMainContainer from "@/business/components/common/components/MsMainContainer";
import MsApiScenarioList from "@/business/components/api/automation/scenario/ApiScenarioList"; import MsApiScenarioList from "@/business/components/api/automation/scenario/ApiScenarioList";
import {getUUID, downloadFile, checkoutTestManagerOrTestUser} from "@/common/js/utils"; import {getUUID, downloadFile, checkoutTestManagerOrTestUser,getCurrentUser} from "@/common/js/utils";
import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule"; import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule";
import MsEditApiScenario from "./scenario/EditApiScenario"; import MsEditApiScenario from "./scenario/EditApiScenario";
@ -127,7 +127,6 @@
'$route'(to, from) { // ctrl s '$route'(to, from) { // ctrl s
if (to.path.indexOf('/api/automation') == -1) { if (to.path.indexOf('/api/automation') == -1) {
if (this.$refs && this.$refs.autoScenarioConfig) { if (this.$refs && this.$refs.autoScenarioConfig) {
// console.log(this.$refs.autoScenarioConfig);
this.$refs.autoScenarioConfig.forEach(item => { this.$refs.autoScenarioConfig.forEach(item => {
item.removeListener(); item.removeListener();
}); });
@ -189,7 +188,16 @@
let label = this.$t('api_test.automation.add_scenario'); let label = this.$t('api_test.automation.add_scenario');
let name = getUUID().substring(0, 8); let name = getUUID().substring(0, 8);
this.activeName = name; this.activeName = name;
this.tabs.push({label: label, name: name, currentScenario: {apiScenarioModuleId: "", id: getUUID()}}); let currentScenario = {
status: "Underway", principal: getCurrentUser().id,
apiScenarioModuleId: "root", id: getUUID(),
modulePath: "/" + this.$t("commons.module_title")
};
if (this.nodeTree && this.nodeTree.length > 0) {
currentScenario.apiScenarioModuleId = this.nodeTree[0].id;
currentScenario.modulePath = this.nodeTree[0].path;
}
this.tabs.push({label: label, name: name, currentScenario: currentScenario});
} }
if (tab.name === 'edit') { if (tab.name === 'edit') {
let label = this.$t('api_test.automation.add_scenario'); let label = this.$t('api_test.automation.add_scenario');

View File

@ -69,7 +69,8 @@
<el-table-column v-if="item.id == 'tags'" prop="tags" min-width="120px" <el-table-column v-if="item.id == 'tags'" prop="tags" min-width="120px"
:label="$t('api_test.automation.tag')" :key="index"> :label="$t('api_test.automation.tag')" :key="index">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain" :content="itemName" :show-tooltip="true" tooltip style="margin-left: 0px; margin-right: 2px"/> <ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain" :content="itemName" :show-tooltip="true"
tooltip style="margin-left: 0px; margin-right: 2px"/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column v-if="item.id == 'userId'" prop="userId" min-width="120px" <el-table-column v-if="item.id == 'userId'" prop="userId" min-width="120px"
@ -175,20 +176,21 @@
import PriorityTableItem from "../../../track/common/tableItems/planview/PriorityTableItem"; import PriorityTableItem from "../../../track/common/tableItems/planview/PriorityTableItem";
import PlanStatusTableItem from "../../../track/common/tableItems/plan/PlanStatusTableItem"; import PlanStatusTableItem from "../../../track/common/tableItems/plan/PlanStatusTableItem";
import BatchEdit from "../../../track/case/components/BatchEdit"; import BatchEdit from "../../../track/case/components/BatchEdit";
import {API_SCENARIO_LIST, TEST_CASE_LIST, TEST_PLAN_LIST, WORKSPACE_ID} from "../../../../../common/js/constants"; import {API_SCENARIO_LIST, PROJECT_NAME, WORKSPACE_ID} from "../../../../../common/js/constants";
import {PROJECT_NAME} from "../../../../../common/js/constants";
import EnvironmentSelect from "../../definition/components/environment/EnvironmentSelect"; import EnvironmentSelect from "../../definition/components/environment/EnvironmentSelect";
import BatchMove from "../../../track/case/components/BatchMove"; import BatchMove from "../../../track/case/components/BatchMove";
import {_sort, getLabel, getSystemLabel} from "@/common/js/tableUtils";
import {Api_Scenario_List} from "@/business/components/common/model/JsonData";
import HeaderCustom from "@/business/components/common/head/HeaderCustom";
import { import {
_filter, _filter,
_handleSelect, _handleSelect,
_handleSelectAll, _handleSelectAll,
_sort,
getLabel,
getSelectDataCounts, getSelectDataCounts,
setUnSelectIds, toggleAllSelection setUnSelectIds,
toggleAllSelection
} from "@/common/js/tableUtils"; } from "@/common/js/tableUtils";
import {Api_Scenario_List} from "@/business/components/common/model/JsonData";
import HeaderCustom from "@/business/components/common/head/HeaderCustom";
import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate"; import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate";
export default { export default {
@ -343,11 +345,12 @@
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]}; this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
this.search(); this.search();
this.getPrincipalOptions([]); this.getPrincipalOptions([]);
getSystemLabel(this, this.type)
}, },
watch: { watch: {
selectNodeIds() { selectNodeIds() {
this.condition.selectAll = false;
this.condition.unSelectIds = [];
this.selectDataCounts = 0;
this.selectProjectId ? this.search(this.selectProjectId) : this.search(); this.selectProjectId ? this.search(this.selectProjectId) : this.search();
}, },
trashEnable() { trashEnable() {
@ -357,6 +360,10 @@
} else { } else {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]}; this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
} }
this.condition.selectAll = false;
this.condition.unSelectIds = [];
this.selectDataCounts = 0;
this.search(); this.search();
}, },
batchReportId() { batchReportId() {
@ -374,7 +381,6 @@
}, },
methods: { methods: {
customHeader() { customHeader() {
getLabel(this, API_SCENARIO_LIST);
this.$refs.headerCustom.open(this.tableLabel) this.$refs.headerCustom.open(this.tableLabel)
}, },
selectByParam() { selectByParam() {
@ -415,9 +421,13 @@
break; break;
} }
this.selection = []; this.selection = [];
this.selectAll = false;
this.unSelection = []; if (!this.condition.selectAll) {
this.condition.selectAll = false;
this.condition.unSelectIds = [];
this.selectDataCounts = 0; this.selectDataCounts = 0;
}
let url = "/api/automation/list/" + this.currentPage + "/" + this.pageSize; let url = "/api/automation/list/" + this.currentPage + "/" + this.pageSize;
if (this.condition.projectId) { if (this.condition.projectId) {
this.result.loading = true; this.result.loading = true;
@ -430,24 +440,57 @@
item.tags = JSON.parse(item.tags); item.tags = JSON.parse(item.tags);
} }
}); });
this.result.loading = false;
this.unSelection = data.listObject.map(s => s.id);
if (this.$refs.scenarioTable) { if (this.$refs.scenarioTable) {
this.$refs.scenarioTable.doLayout() setTimeout(() => {
this.$refs.scenarioTable.doLayout();
this.result.loading = false;
}, 500)
} }
if (!this.condition.selectAll) {
this.condition.unSelectIds = response.data.listObject.map(s => s.id);
}
this.$nextTick(function () {
this.checkTableRowIsSelect();
})
}); });
} }
getLabel(this, API_SCENARIO_LIST); getLabel(this, API_SCENARIO_LIST);
}, },
checkTableRowIsSelect() {
//
if (this.condition.selectAll) {
let unSelectIds = this.condition.unSelectIds;
this.tableData.forEach(row => {
if (unSelectIds.indexOf(row.id) < 0) {
this.$refs.scenarioTable.toggleRowSelection(row, true);
//selectRows
if (!this.selectRows.has(row)) {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
} else {
//selectRow
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
}
}
})
}
},
handleCommand(cmd) { handleCommand(cmd) {
let table = this.$refs.scenarioTable; let table = this.$refs.scenarioTable;
switch (cmd) { switch (cmd) {
case "table": case "table":
this.selectAll = false; this.condition.selectAll = false;
table.toggleAllSelection(); table.toggleAllSelection();
break; break;
case "all": case "all":
this.selectAll = true; this.condition.selectAll = true;
break break
} }
}, },
@ -464,6 +507,7 @@
moveSave(param) { moveSave(param) {
this.buildBatchParam(param); this.buildBatchParam(param);
param.apiScenarioModuleId = param.nodeId; param.apiScenarioModuleId = param.nodeId;
param.modulePath = param.nodePath;
this.$post('/api/automation/batch/edit', param, () => { this.$post('/api/automation/batch/edit', param, () => {
this.$success(this.$t('commons.save_success')); this.$success(this.$t('commons.save_success'));
this.$refs.testBatchMove.close(); this.$refs.testBatchMove.close();
@ -520,11 +564,6 @@
addTestPlan(params) { addTestPlan(params) {
let obj = {planIds: params[0], scenarioIds: this.selection}; let obj = {planIds: params[0], scenarioIds: this.selection};
// obj.projectId = getCurrentProjectID();
// obj.selectAllDate = this.isSelectAllDate;
// obj.unSelectIds = this.unSelection;
// obj = Object.assign(obj, this.condition);
// todo // todo
if (this.isSelectAllDate) { if (this.isSelectAllDate) {
this.$warning("暂不支持批量添加所有场景到测试计划!"); this.$warning("暂不支持批量添加所有场景到测试计划!");
@ -619,8 +658,10 @@
}, },
handleDeleteBatch(row) { handleDeleteBatch(row) {
if (this.trashEnable) { if (this.trashEnable) {
let ids = Array.from(this.selectRows).map(row => row.id); //let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/api/automation/deleteBatch/', ids, () => { let param = {};
this.buildBatchParam(param);
this.$post('/api/automation/deleteBatchByCondition/', param, () => {
this.$success(this.$t('commons.delete_success')); this.$success(this.$t('commons.delete_success'));
this.search(); this.search();
}); });
@ -630,8 +671,10 @@
confirmButtonText: this.$t('commons.confirm'), confirmButtonText: this.$t('commons.confirm'),
callback: (action) => { callback: (action) => {
if (action === 'confirm') { if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id); //let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/api/automation/removeToGc/', ids, () => { let param = {};
this.buildBatchParam(param);
this.$post('/api/automation/removeToGcByBatch/', param, () => {
this.$success(this.$t('commons.delete_success')); this.$success(this.$t('commons.delete_success'));
this.search(); this.search();
}); });
@ -686,8 +729,11 @@
confirmButtonText: this.$t('commons.confirm'), confirmButtonText: this.$t('commons.confirm'),
callback: (action) => { callback: (action) => {
if (action === 'confirm') { if (action === 'confirm') {
let ids = [row.id]; // let ids = [row.id];
this.$post('/api/automation/removeToGc/', ids, () => { let param = {};
this.buildBatchParam(param);
this.$post('/api/automation/removeToGcByBatch/', param, () => {
// this.$post('/api/automation/removeToGc/', ids, () => {
this.$success(this.$t('commons.delete_success')); this.$success(this.$t('commons.delete_success'));
this.search(); this.search();
}); });

View File

@ -16,32 +16,9 @@
ref="nodeTree"> ref="nodeTree">
<template v-slot:header> <template v-slot:header>
<el-input :placeholder="$t('test_track.module.search')" v-model="condition.filterText" size="small"> <ms-search-bar
<template v-slot:append> :condition="condition"
<el-dropdown v-if="!isReadOnly" size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')" :commands="operators"/>
v-tester
@command="handleCommand" trigger="click">
<el-button icon="el-icon-folder-add" @click="addScenario"></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="add-scenario">{{ $t('api_test.automation.add_scenario') }}</el-dropdown-item>
<el-dropdown-item command="import">{{ $t('api_test.api_import.label') }}</el-dropdown-item>
<el-dropdown-item command="exports">
<el-dropdown placement="right-start" @command="chooseExportType">
<span>
{{ $t('report.export') }} <i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<template>
<el-dropdown-item command="export">{{ $t('report.export_to_ms_format') }}</el-dropdown-item>
<el-dropdown-item command="exportJmx">{{ $t('report.export') }} JMETER 格式</el-dropdown-item>
</template>
</el-dropdown-menu>
</el-dropdown>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-input>
<module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/> <module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/>
</template> </template>
@ -52,7 +29,7 @@
@refresh="refresh" @refresh="refresh"
ref="basisScenario"/> ref="basisScenario"/>
<api-import ref="apiImport" :moduleOptions="moduleOptions" @refreshAll="$emit('refreshAll')"/> <api-import ref="apiImport" :moduleOptions="extendTreeNodes" @refreshAll="$emit('refreshAll')"/>
</div> </div>
</template> </template>
@ -61,13 +38,15 @@
import SelectMenu from "../../../track/common/SelectMenu"; import SelectMenu from "../../../track/common/SelectMenu";
import MsAddBasisScenario from "@/business/components/api/automation/scenario/AddBasisScenario"; import MsAddBasisScenario from "@/business/components/api/automation/scenario/AddBasisScenario";
import MsNodeTree from "../../../track/common/NodeTree"; import MsNodeTree from "../../../track/common/NodeTree";
import {buildNodePath} from "../../definition/model/NodeTree"; import {buildNodePath, buildTree} from "../../definition/model/NodeTree";
import ModuleTrashButton from "../../definition/components/module/ModuleTrashButton"; import ModuleTrashButton from "../../definition/components/module/ModuleTrashButton";
import ApiImport from "./common/ScenarioImport"; import ApiImport from "./common/ScenarioImport";
import MsSearchBar from "@/business/components/common/components/search/MsSearchBar";
export default { export default {
name: 'MsApiScenarioModule', name: 'MsApiScenarioModule',
components: { components: {
MsSearchBar,
ApiImport, ApiImport,
ModuleTrashButton, ModuleTrashButton,
MsNodeTree, MsNodeTree,
@ -103,8 +82,36 @@
trashEnable: false trashEnable: false
}, },
data: [], data: [],
extendTreeNodes: [],
currentModule: undefined, currentModule: undefined,
moduleOptions: [], moduleOptions: [],
operators: [
{
label: this.$t('api_test.automation.add_scenario'),
callback: this.addScenario
},
{
label: this.$t('api_test.api_import.label'),
callback: this.handleImport
},
{
label: this.$t('report.export'),
children: [
{
label: this.$t('report.export_to_ms_format'),
callback: () => {
this.$emit('exportAPI');
}
},
{
label: this.$t('report.export') + 'JMETER 格式',
callback: () => {
this.$emit('exportJmx');
}
}
]
}
]
} }
}, },
mounted() { mounted() {
@ -151,14 +158,19 @@
break; break;
} }
}, },
chooseExportType(e) { handleImport() {
switch (e) { if (this.projectId) {
case "export": this.result = this.$get("/api/automation/module/list/" + this.projectId, response => {
this.$emit('exportAPI'); if (response.data != undefined && response.data != null) {
break; this.data = response.data;
case "exportJmx": let moduleOptions = [];
this.$emit('exportJmx'); this.data.forEach(node => {
break; buildNodePath(node, {path: ''}, moduleOptions);
});
this.moduleOptions = moduleOptions
}
});
this.$refs.apiImport.open(this.currentModule);
} }
}, },
list(projectId) { list(projectId) {
@ -176,11 +188,17 @@
this.result = this.$get(url, response => { this.result = this.$get(url, response => {
if (response.data != undefined && response.data != null) { if (response.data != undefined && response.data != null) {
this.data = response.data; this.data = response.data;
let moduleOptions = []; this.extendTreeNodes = [];
this.data.forEach(node => { this.extendTreeNodes.unshift({
buildNodePath(node, {path: ''}, moduleOptions); "id": "root",
"name": this.$t('commons.module_title'),
"level": 0,
"children": this.data,
}); });
this.$emit('setModuleOptions', moduleOptions); this.extendTreeNodes.forEach(node => {
buildTree(node, {path: ''});
});
this.$emit('setModuleOptions', this.extendTreeNodes);
this.$emit('setNodeTree', this.data); this.$emit('setNodeTree', this.data);
if (this.$refs.nodeTree) { if (this.$refs.nodeTree) {
this.$refs.nodeTree.filter(this.condition.filterText); this.$refs.nodeTree.filter(this.condition.filterText);

View File

@ -22,9 +22,7 @@
</el-col> </el-col>
<el-col :span="7"> <el-col :span="7">
<el-form-item :label="$t('test_track.module.module')" prop="apiScenarioModuleId"> <el-form-item :label="$t('test_track.module.module')" prop="apiScenarioModuleId">
<el-select class="ms-scenario-input" size="small" v-model="currentScenario.apiScenarioModuleId"> <ms-select-tree size="small" :data="moduleOptions" :defaultKey="currentScenario.apiScenarioModuleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="7"> <el-col :span="7">
@ -201,7 +199,8 @@
@closePage="close" @unFullScreen="unFullScreen" @showAllBtn="showAllBtn" @runDebug="runDebug" @setProjectEnvMap="setProjectEnvMap" @showScenarioParameters="showScenarioParameters" @setCookieShare="setCookieShare" ref="maximizeHeader"/> @closePage="close" @unFullScreen="unFullScreen" @showAllBtn="showAllBtn" @runDebug="runDebug" @setProjectEnvMap="setProjectEnvMap" @showScenarioParameters="showScenarioParameters" @setCookieShare="setCookieShare" ref="maximizeHeader"/>
</template> </template>
<maximize-scenario :scenario-definition="scenarioDefinition" :envMap="projectEnvMap" :moduleOptions="moduleOptions" :currentScenario="currentScenario" :type="type" ref="maximizeScenario" @openScenario="openScenario"/> <maximize-scenario :scenario-definition="scenarioDefinition" :envMap="projectEnvMap" :moduleOptions="moduleOptions"
:currentScenario="currentScenario" :type="type" ref="maximizeScenario" @openScenario="openScenario"/>
</ms-drawer> </ms-drawer>
</div> </div>
@ -238,6 +237,7 @@
import MaximizeScenario from "./maximize/MaximizeScenario"; import MaximizeScenario from "./maximize/MaximizeScenario";
import ScenarioHeader from "./maximize/ScenarioHeader"; import ScenarioHeader from "./maximize/ScenarioHeader";
import MsDrawer from "../../../common/components/MsDrawer"; import MsDrawer from "../../../common/components/MsDrawer";
import MsSelectTree from "../../../common/select-tree/SelectTree";
let jsonPath = require('jsonpath'); let jsonPath = require('jsonpath');
export default { export default {
@ -260,7 +260,8 @@
EnvPopover, EnvPopover,
MaximizeScenario, MaximizeScenario,
ScenarioHeader, ScenarioHeader,
MsDrawer MsDrawer,
MsSelectTree
}, },
data() { data() {
return { return {
@ -268,6 +269,10 @@
label: "label", label: "label",
children: "hashTree" children: "hashTree"
}, },
moduleObj: {
id: 'id',
label: 'name',
},
rules: { rules: {
name: [ name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'}, {required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
@ -442,6 +447,10 @@
}, },
}, },
methods: { methods: {
setModule(id,data) {
this.currentScenario.apiScenarioModuleId = id;
this.currentScenario.modulePath = data.path;
},
setHideBtn() { setHideBtn() {
this.isBtnHide = false; this.isBtnHide = false;
}, },
@ -577,13 +586,14 @@
recursiveSorting(arr, scenarioProjectId) { recursiveSorting(arr, scenarioProjectId) {
for (let i in arr) { for (let i in arr) {
arr[i].index = Number(i) + 1; arr[i].index = Number(i) + 1;
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) { if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].loopType === "LOOP_COUNT" && arr[i].hashTree && arr[i].hashTree.length > 1) {
arr[i].countController.proceed = true; arr[i].countController.proceed = true;
} }
if (!arr[i].projectId) { if (!arr[i].projectId) {
// IDIDIDID // IDIDIDID
arr[i].projectId = scenarioProjectId ? scenarioProjectId : this.projectId; arr[i].projectId = scenarioProjectId ? scenarioProjectId : this.projectId;
} }
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) { if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
this.recursiveSorting(arr[i].hashTree, arr[i].projectId); this.recursiveSorting(arr[i].hashTree, arr[i].projectId);
} }
@ -606,6 +616,7 @@
if (!this.scenarioDefinition[i].projectId) { if (!this.scenarioDefinition[i].projectId) {
this.scenarioDefinition[i].projectId = this.projectId; this.scenarioDefinition[i].projectId = this.projectId;
} }
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) { if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId); this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId);
} }
@ -753,6 +764,7 @@
if (!sign) { if (!sign) {
return; return;
} }
this.$refs['currentScenario'].validate((valid) => { this.$refs['currentScenario'].validate((valid) => {
if (valid) { if (valid) {
Promise.all([ Promise.all([
@ -841,15 +853,6 @@
this.expandedNode.splice(this.expandedNode.indexOf(data.resourceId), 1); this.expandedNode.splice(this.expandedNode.indexOf(data.resourceId), 1);
} }
}, },
getPath(id) {
if (id === null) {
return null;
}
let path = this.moduleOptions.filter(function (item) {
return item.id === id ? item.path : "";
});
return path[0].path;
},
setFiles(item, bodyUploadFiles, obj) { setFiles(item, bodyUploadFiles, obj) {
if (item.body) { if (item.body) {
if (item.body.kvs) { if (item.body.kvs) {
@ -926,7 +929,7 @@
return bodyUploadFiles; return bodyUploadFiles;
}, },
editScenario() { editScenario() {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
document.getElementById("inputDelay").focus(); // input document.getElementById("inputDelay").focus(); // input
this.$refs['currentScenario'].validate((valid) => { this.$refs['currentScenario'].validate((valid) => {
if (valid) { if (valid) {
@ -1008,7 +1011,6 @@
setParameter() { setParameter() {
this.currentScenario.stepTotal = this.scenarioDefinition.length; this.currentScenario.stepTotal = this.scenarioDefinition.length;
this.currentScenario.projectId = this.projectId; this.currentScenario.projectId = this.projectId;
this.currentScenario.modulePath = this.getPath(this.currentScenario.apiScenarioModuleId);
// 便 // 便
let scenario = { let scenario = {
id: this.currentScenario.id, id: this.currentScenario.id,

View File

@ -3,6 +3,7 @@
v-model="visible" v-model="visible"
placement="bottom" placement="bottom"
width="400" width="400"
:disabled="isReadOnly"
@show="showPopover" @show="showPopover"
trigger="click"> trigger="click">
<env-select :project-ids="projectIds" :env-map="envMap" @close="visible = false" <env-select :project-ids="projectIds" :env-map="envMap" @close="visible = false"
@ -24,6 +25,12 @@ export default {
envMap: Map, envMap: Map,
projectIds: Set, projectIds: Set,
projectList: Array, projectList: Array,
isReadOnly: {
type: Boolean,
default() {
return false;
}
}
}, },
data() { data() {
return { return {

View File

@ -5,12 +5,12 @@
<el-option v-for="(environment, index) in pe.envs" :key="index" <el-option v-for="(environment, index) in pe.envs" :key="index"
:label="environment.name + (environment.config.httpConfig.socket ? (': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '')" :label="environment.name + (environment.config.httpConfig.socket ? (': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '')"
:value="environment.id"/> :value="environment.id"/>
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)"> <el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
{{ $t('api_test.environment.environment_config') }} {{ $t('api_test.environment.environment_config') }}
</el-button> </el-button>
<template v-slot:empty> <template v-slot:empty>
<div class="empty-environment"> <div class="empty-environment">
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)"> <el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
{{ $t('api_test.environment.environment_config') }} {{ $t('api_test.environment.environment_config') }}
</el-button> </el-button>
</div> </div>
@ -78,12 +78,12 @@ export default {
const project = this.projectList.find(p => p.id === id); const project = this.projectList.find(p => p.id === id);
return project ? project.name : ""; return project ? project.name : "";
}, },
openEnvironmentConfig(projectId) { openEnvironmentConfig(projectId, envId) {
if (!projectId) { if (!projectId) {
this.$error(this.$t('api_test.select_project')); this.$error(this.$t('api_test.select_project'));
return; return;
} }
this.$refs.environmentConfig.open(projectId); this.$refs.environmentConfig.open(projectId, envId);
}, },
handleConfirm() { handleConfirm() {
let map = new Map(); let map = new Map();

View File

@ -18,7 +18,7 @@ export const ELEMENTS = new Map([
['CustomizeReq', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], ['CustomizeReq', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
['MaxSamplerProxy', ["JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], ['MaxSamplerProxy', ["JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
['AllSamplerProxy', ["HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler"]], ['AllSamplerProxy', ["HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler"]],
['AllCanExecType', ["HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler", "JSR223Processor"]]
]) ])
export const ELEMENT_TYPE = { export const ELEMENT_TYPE = {

View File

@ -31,7 +31,7 @@
</el-form-item> </el-form-item>
<el-form-item :label="$t('test_track.module.module')" prop="moduleId"> <el-form-item :label="$t('test_track.module.module')" prop="moduleId">
<ms-select-tree size="small" :data="moduleOptions" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/> <ms-select-tree size="small" :data="moduleOptions" :defaultKey="httpForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('commons.description')" prop="description" style="margin-bottom: 29px"> <el-form-item :label="$t('commons.description')" prop="description" style="margin-bottom: 29px">
@ -64,6 +64,7 @@
import {createComponent, Request} from "../../../definition/components/jmeter/components"; import {createComponent, Request} from "../../../definition/components/jmeter/components";
import {getUUID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsSelectTree from "@/business/components/common/select-tree/SelectTree"; import MsSelectTree from "@/business/components/common/select-tree/SelectTree";
import {buildTree} from "../../../definition/model/NodeTree";
export default { export default {
@ -83,7 +84,7 @@
callback(); callback();
}; };
return { return {
httpForm: {environmentId: ""}, httpForm: {environmentId: "", moduleId: "root"},
moduleOptions: [], moduleOptions: [],
httpVisible: false, httpVisible: false,
currentModule: {}, currentModule: {},
@ -250,13 +251,23 @@
let url = "/api/module/list/" + getCurrentProjectID() + "/" + data.protocol; let url = "/api/module/list/" + getCurrentProjectID() + "/" + data.protocol;
this.result = this.$get(url, response => { this.result = this.$get(url, response => {
if (response.data != undefined && response.data != null) { if (response.data != undefined && response.data != null) {
this.moduleOptions = response.data; let data = response.data;
this.moduleOptions = [];
this.moduleOptions.unshift({
"id": "root",
"name": this.$t('commons.module_title'),
"level": 0,
"children": data,
});
this.moduleOptions.forEach(node => {
buildTree(node, {path: ''});
});
} }
}); });
}, },
setModule(id) { setModule(id, data) {
this.httpForm.moduleId = id; this.httpForm.moduleId = id;
//this.reload(); this.httpForm.modulePath = data.path;
}, },
reload() { reload() {
this.loading = true this.loading = true
@ -271,7 +282,7 @@
data.protocol = "DUBBO"; data.protocol = "DUBBO";
} }
data.id = getUUID(); data.id = getUUID();
this.httpForm = {id: data.id, name: data.name, protocol: data.protocol, path: data.path, method: api.method, userId: getCurrentUser().id, request: data}; this.httpForm = {id: data.id, name: data.name, protocol: data.protocol, path: data.path, method: api.method, userId: getCurrentUser().id, request: data, moduleId: "root"};
this.getMaintainerOptions(); this.getMaintainerOptions();
this.list(data); this.list(data);
this.httpVisible = true; this.httpVisible = true;

View File

@ -101,25 +101,25 @@ export default {
let requestObj = JSON.parse(item.request); let requestObj = JSON.parse(item.request);
if(requestObj.esbDataStruct != null ){ if(requestObj.esbDataStruct != null ){
//ESB // //ESB
let param = {}; // let param = {};
param.request = requestObj; // param.request = requestObj;
param.method = "ESB"; // param.method = "ESB";
param.esbDataStruct = JSON.stringify(requestObj.esbDataStruct); // param.esbDataStruct = JSON.stringify(requestObj.esbDataStruct);
if(requestObj.backEsbDataStruct != null){ // if(requestObj.backEsbDataStruct != null){
param.backEsbDataStruct = JSON.stringify(requestObj.backEsbDataStruct); // param.backEsbDataStruct = JSON.stringify(requestObj.backEsbDataStruct);
}else{ // }else{
param.backEsbDataStruct = ""; // param.backEsbDataStruct = "";
} // }
this.$post("/api/definition/updateEsbRequest", param, response => { // this.$post("/api/definition/updateEsbRequest", param, response => {
if(response.data!=null){ // if(response.data!=null){
if(response.data.request!=null){ // if(response.data.request!=null){
item.request = JSON.stringify(response.data.request); // item.request = JSON.stringify(response.data.request);
param.method = "TCP"; // param.method = "TCP";
} // }
} // }
}) // })
} }
}); });
this.$emit('save', apiCases, 'CASE', reference); this.$emit('save', apiCases, 'CASE', reference);

View File

@ -28,9 +28,12 @@
<div class="header-right" @click.stop> <div class="header-right" @click.stop>
<slot name="message"></slot> <slot name="message"></slot>
<el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top" v-if="showBtn"> <el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top" v-if="showBtn">
<el-switch v-model="data.enable" class="enable-switch" size="mini" :disabled="data.disabled && !data.root"/> <el-switch v-model="data.enable" class="enable-switch" size="mini" :disabled="data.disabled && !data.root" style="width: 30px"/>
</el-tooltip> </el-tooltip>
<slot name="button"></slot> <slot name="button"></slot>
<el-tooltip content="Copy" placement="top">
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" style="padding: 5px"/>
</el-tooltip>
<step-extend-btns style="display: contents" :data="data" @copy="copyRow" @remove="remove" @openScenario="openScenario" v-if="showBtn && (!data.disabled || data.root)"/> <step-extend-btns style="display: contents" :data="data" @copy="copyRow" @remove="remove" @openScenario="openScenario" v-if="showBtn && (!data.disabled || data.root)"/>
</div> </div>

View File

@ -22,9 +22,7 @@
<el-row> <el-row>
<el-col :span="11"> <el-col :span="11">
<el-form-item :label="$t('commons.import_module')"> <el-form-item :label="$t('commons.import_module')">
<el-select size="small" v-model="formData.moduleId" class="project-select" clearable> <ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="!isHar" :label="$t('commons.import_mode')"> <el-form-item v-if="!isHar" :label="$t('commons.import_mode')">
<el-select size="small" v-model="formData.modeId" class="project-select" clearable> <el-select size="small" v-model="formData.modeId" class="project-select" clearable>
@ -70,16 +68,17 @@
<script> <script>
import MsDialogFooter from "../../../../common/components/MsDialogFooter"; import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils"; import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import MsSelectTree from "../../../../common/select-tree/SelectTree";
export default { export default {
name: "ScenarioImport", name: "ScenarioImport",
components: {MsDialogFooter}, components: {MsDialogFooter, MsSelectTree},
props: { props: {
saved: { saved: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
moduleOptions: {} moduleOptions: Array,
}, },
data() { data() {
return { return {
@ -140,7 +139,11 @@
}, },
rules: {}, rules: {},
currentModule: {}, currentModule: {},
fileList: [] fileList: [],
moduleObj: {
id: 'id',
label: 'name',
},
} }
}, },
activated() { activated() {
@ -257,7 +260,11 @@
this.fileList = []; this.fileList = [];
removeGoBackListener(this.close); removeGoBackListener(this.close);
this.visible = false; this.visible = false;
} },
setModule(id, data) {
this.formData.moduleId = id;
this.formData.modulePath = data.path;
},
} }
} }
</script> </script>

View File

@ -23,7 +23,7 @@
<template v-slot:button> <template v-slot:button>
<el-tooltip :content="$t('api_test.run')" placement="top"> <el-tooltip :content="$t('api_test.run')" placement="top">
<el-button @click="run" icon="el-icon-video-play" class="ms-btn" size="mini" circle/> <el-button @click="run" icon="el-icon-video-play" style="padding: 5px" class="ms-btn" size="mini" circle/>
</el-tooltip> </el-tooltip>
</template> </template>
@ -289,6 +289,9 @@
}, },
active(item) { active(item) {
this.request.active = !this.request.active; this.request.active = !this.request.active;
if (this.node) {
this.node.expanded = this.request.active;
}
this.reload(); this.reload();
}, },
run() { run() {

View File

@ -105,11 +105,11 @@
remove() { remove() {
this.$emit('remove', this.scenario, this.node); this.$emit('remove', this.scenario, this.node);
}, },
active(item) { active() {
if (item && item.active) { if (this.node) {
item.active = !item.active; this.node.expanded = !this.node.expanded;
this.reload();
} }
this.reload();
}, },
copyRow() { copyRow() {
this.$emit('copyRow', this.scenario, this.node); this.$emit('copyRow', this.scenario, this.node);

View File

@ -61,6 +61,9 @@
}, },
active() { active() {
this.request.active = !this.request.active; this.request.active = !this.request.active;
if (this.node) {
this.node.expanded = this.request.active;
}
}, },
} }
} }

View File

@ -27,7 +27,7 @@
</template> </template>
<template v-slot:button> <template v-slot:button>
<el-button @click="runDebug" :tip="$t('api_test.run')" icon="el-icon-video-play" style="background-color: #409EFF;color: white;" size="mini" circle/> <el-button @click="runDebug" :tip="$t('api_test.run')" icon="el-icon-video-play" style="background-color: #409EFF;color: white;padding: 5px" size="mini" circle/>
</template> </template>
<div v-if="controller.loopType==='LOOP_COUNT'" draggable> <div v-if="controller.loopType==='LOOP_COUNT'" draggable>
<el-row> <el-row>
@ -246,6 +246,9 @@
}, },
active(item) { active(item) {
item.active = !item.active; item.active = !item.active;
if (this.node) {
this.node.expanded = item.active;
}
this.reload(); this.reload();
}, },
changeRadio() { changeRadio() {

View File

@ -5,7 +5,7 @@
<el-icon class="el-icon-more"></el-icon> <el-icon class="el-icon-more"></el-icon>
</el-link> </el-link>
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item command="copy">复制步骤</el-dropdown-item> <!--<el-dropdown-item command="copy">复制步骤</el-dropdown-item>-->
<el-dropdown-item command="remove" v-tester>删除步骤</el-dropdown-item> <el-dropdown-item command="remove" v-tester>删除步骤</el-dropdown-item>
<el-dropdown-item command="scenarioVar" v-tester v-if="data.type==='scenario'">查看场景变量</el-dropdown-item> <el-dropdown-item command="scenarioVar" v-tester v-if="data.type==='scenario'">查看场景变量</el-dropdown-item>
<el-dropdown-item command="openScenario" v-tester v-if="data.type==='scenario' && data.referenced==='REF'">打开场景</el-dropdown-item> <el-dropdown-item command="openScenario" v-tester v-if="data.type==='scenario' && data.referenced==='REF'">打开场景</el-dropdown-item>
@ -45,7 +45,7 @@
this.$emit('remove'); this.$emit('remove');
break; break;
case "scenarioVar": case "scenarioVar":
this.$refs.scenarioParameters.open(this.data.variables, this.data.headers, true); this.$refs.scenarioParameters.open(this.data.variables, this.data.headers, this.data.referenced === 'REF');
break; break;
case "openScenario": case "openScenario":
this.getScenario(); this.getScenario();

View File

@ -50,7 +50,8 @@
</div> </div>
</ms-aside-container> </ms-aside-container>
<ms-main-container v-if="!loading"> <ms-main-container v-loading="loading">
<div v-if="!loading">
<!-- 第一层当前节点内容--> <!-- 第一层当前节点内容-->
<ms-component-config <ms-component-config
:isMax="false" :isMax="false"
@ -85,6 +86,7 @@
v-if="selectedTreeNode && selectedNode"/> v-if="selectedTreeNode && selectedNode"/>
</div> </div>
</div> </div>
</div>
</ms-main-container> </ms-main-container>
</ms-container> </ms-container>
@ -474,6 +476,7 @@
} }
this.selectedTreeNode = data; this.selectedTreeNode = data;
this.selectedNode = node; this.selectedNode = node;
this.reload();
}, },
suggestClick(node) { suggestClick(node) {
this.response = {}; this.response = {};
@ -493,7 +496,7 @@
recursiveSorting(arr, scenarioProjectId) { recursiveSorting(arr, scenarioProjectId) {
for (let i in arr) { for (let i in arr) {
arr[i].index = Number(i) + 1; arr[i].index = Number(i) + 1;
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) { if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].loopType === "LOOP_COUNT" && arr[i].hashTree && arr[i].hashTree.length > 1) {
arr[i].countController.proceed = true; arr[i].countController.proceed = true;
} }
if (!arr[i].projectId) { if (!arr[i].projectId) {
@ -521,6 +524,7 @@
if (!this.scenarioDefinition[i].projectId) { if (!this.scenarioDefinition[i].projectId) {
this.scenarioDefinition[i].projectId = this.projectId; this.scenarioDefinition[i].projectId = this.projectId;
} }
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) { if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId); this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId);
} }

View File

@ -296,8 +296,12 @@
} }
let api = { let api = {
status: "Underway", method: "GET", userId: getCurrentUser().id, status: "Underway", method: "GET", userId: getCurrentUser().id,
url: "", protocol: this.currentProtocol, environmentId: "" url: "", protocol: this.currentProtocol, environmentId: "", moduleId: 'root', modulePath: "/" + this.$t("commons.module_title")
}; };
if (this.nodeTree && this.nodeTree.length > 0) {
api.moduleId = this.nodeTree[0].id;
api.modulePath = this.nodeTree[0].path;
}
this.handleTabsEdit(this.$t('api_test.definition.request.title'), e, api); this.handleTabsEdit(this.$t('api_test.definition.request.title'), e, api);
}, },
handleTabClose() { handleTabClose() {

View File

@ -185,9 +185,6 @@ export default {
} }
this.response.body = body; this.response.body = body;
} }
if (this.currentApi.moduleId && this.currentApi.moduleId === "root") {
this.currentApi.moduleId = "";
}
}, },
saveApi(data) { saveApi(data) {
this.setParameters(data); this.setParameters(data);

View File

@ -68,8 +68,8 @@
let projectId = ""; let projectId = "";
// envMap // envMap
if (!this.envMap) { if (!this.envMap || this.envMap.size === 0) {
projectId = getCurrentProjectID(); projectId = this.$store.state.projectId;
} else { } else {
// //
projectId = this.runData.projectId; projectId = this.runData.projectId;

View File

@ -275,19 +275,13 @@
} }
}, },
addModule(row) { addModule(row) {
let url = '/api/module/getModuleByName/' + getCurrentProjectID() + "/" + this.api.protocol; this.saveApi(row, "root");
this.$get(url, response => {
if (response.data) {
this.$emit('refreshModule');
this.saveApi(row, response.data);
}
});
}, },
saveApi(row, module) { saveApi(row, module) {
let data = this.api; let data = this.api;
data.name = this.apiCase.name; data.name = this.apiCase.name;
data.moduleId = module.id; data.moduleId = module;
data.modulePath = '/bug'; data.modulePath ="/"+ this.$t('commons.module_title');
this.setParameters(data); this.setParameters(data);
let bodyFiles = this.getBodyUploadFiles(data); let bodyFiles = this.getBodyUploadFiles(data);
this.$fileUpload("/api/definition/create", null, bodyFiles, data, () => { this.$fileUpload("/api/definition/create", null, bodyFiles, data, () => {

View File

@ -77,7 +77,7 @@
this.$error(this.$t('api_test.select_project')); this.$error(this.$t('api_test.select_project'));
return; return;
} }
this.$refs.environmentConfig.open(this.projectId); this.$refs.environmentConfig.open(this.projectId, this.environmentId);
}, },
environmentChange(value) { environmentChange(value) {
for (let i in this.environments) { for (let i in this.environments) {

View File

@ -10,20 +10,7 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item :label="$t('test_track.module.module')" prop="moduleId"> <el-form-item :label="$t('test_track.module.module')" prop="moduleId">
<el-select class="ms-http-input" size="small" v-model="basicForm.moduleId" style="width: 100%" @change="reload"> <ms-select-tree size="small" :data="moduleOptions" :defaultKey="basicForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
<div v-if="moduleOptions.length>0">
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</div>
<div v-else>
<el-option :key="0" :value="''">
<div style="margin-left: 40px">
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},
</span>
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>
</div>
</el-option>
</div>
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -76,10 +63,11 @@
import {API_STATUS} from "../../model/JsonData"; import {API_STATUS} from "../../model/JsonData";
import {WORKSPACE_ID} from '../../../../../../common/js/constants'; import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag"; import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsSelectTree from "../../../../common/select-tree/SelectTree";
export default { export default {
name: "MsBasisApi", name: "MsBasisApi",
components: {MsInputTag}, components: {MsInputTag, MsSelectTree},
props: { props: {
currentProtocol: { currentProtocol: {
type: String, type: String,
@ -98,6 +86,10 @@ export default {
httpVisible: false, httpVisible: false,
currentModule: {}, currentModule: {},
maintainerOptions: [], maintainerOptions: [],
moduleObj: {
id: 'id',
label: 'name',
},
loading: false, loading: false,
rule: { rule: {
name: [ name: [
@ -125,6 +117,10 @@ export default {
this.loading = false this.loading = false
}) })
}, },
setModule(id,data) {
this.basicForm.moduleId = id;
this.basisData.modulePath = data.path;
},
validate() { validate() {
this.$refs['basicForm'].validate((valid) => { this.$refs['basicForm'].validate((valid) => {
if (valid) { if (valid) {

View File

@ -49,20 +49,7 @@
</el-col> </el-col>
<el-col :span="7"> <el-col :span="7">
<el-form-item :label="$t('test_track.module.module')" prop="moduleId"> <el-form-item :label="$t('test_track.module.module')" prop="moduleId">
<el-select class="ms-http-select" size="small" v-model="httpForm.moduleId"> <ms-select-tree size="small" :data="moduleOptions" :defaultKey="httpForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
<div v-if="moduleOptions.length>0">
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</div>
<div v-else>
<el-option :key="0" :value="''">
<div style="margin-left: 40px">
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},
</span>
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>
</div>
</el-option>
</div>
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -116,10 +103,11 @@ import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
import {KeyValue} from "../../model/ApiTestModel"; import {KeyValue} from "../../model/ApiTestModel";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag"; import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor"; import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
import MsSelectTree from "../../../../common/select-tree/SelectTree";
export default { export default {
name: "MsAddCompleteHttpApi", name: "MsAddCompleteHttpApi",
components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag}, components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag, MsSelectTree},
data() { data() {
let validateURL = (rule, value, callback) => { let validateURL = (rule, value, callback) => {
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) { if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
@ -147,6 +135,11 @@ export default {
currentModule: {}, currentModule: {},
reqOptions: REQ_METHOD, reqOptions: REQ_METHOD,
options: API_STATUS, options: API_STATUS,
moduleObj: {
id: 'id',
label: 'name',
},
} }
}, },
props: {moduleOptions: {}, request: {}, response: {}, basisData: {}, syncTabs: Array}, props: {moduleOptions: {}, request: {}, response: {}, basisData: {}, syncTabs: Array},
@ -190,7 +183,6 @@ export default {
}); });
}, },
setParameter() { setParameter() {
this.httpForm.modulePath = this.getPath(this.httpForm.moduleId);
this.request.path = this.httpForm.path; this.request.path = this.httpForm.path;
this.request.method = this.httpForm.method; this.request.method = this.httpForm.method;
this.httpForm.request.useEnvironment = undefined; this.httpForm.request.useEnvironment = undefined;
@ -211,15 +203,6 @@ export default {
createModules() { createModules() {
this.$emit("createRootModelInTree"); this.$emit("createRootModelInTree");
}, },
getPath(id) {
if (id === null) {
return null;
}
let path = this.moduleOptions.filter(function (item) {
return item.id === id ? item.path : "";
});
return path[0].path;
},
urlChange() { urlChange() {
if (!this.httpForm.path || this.httpForm.path.indexOf('?') === -1) return; if (!this.httpForm.path || this.httpForm.path.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.httpForm.path)); let url = this.getURL(this.addProtocol(this.httpForm.path));
@ -248,6 +231,10 @@ export default {
this.$error(this.$t('api_test.request.url_invalid'), 2000); this.$error(this.$t('api_test.request.url_invalid'), 2000);
} }
}, },
setModule(id,data) {
this.httpForm.moduleId = id;
this.httpForm.modulePath = data.path;
},
}, },
created() { created() {
@ -255,7 +242,6 @@ export default {
if (!this.basisData.environmentId) { if (!this.basisData.environmentId) {
this.basisData.environmentId = ""; this.basisData.environmentId = "";
} }
this.httpForm = JSON.parse(JSON.stringify(this.basisData)); this.httpForm = JSON.parse(JSON.stringify(this.basisData));
} }
} }

View File

@ -16,20 +16,22 @@
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item :label="$t('test_track.module.module')" prop="moduleId"> <el-form-item :label="$t('test_track.module.module')" prop="moduleId">
<el-select class="ms-http-input" size="small" v-model="basicForm.moduleId" style="width: 100%" @change="reload"> <!--<el-select class="ms-http-input" size="small" v-model="basicForm.moduleId" style="width: 100%" @change="reload">-->
<div v-if="moduleOptions.length>0"> <!--<div v-if="moduleOptions.length>0">-->
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/> <!--<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>-->
</div> <!--</div>-->
<div v-else> <!--<div v-else>-->
<el-option :key="0" :value="''"> <!--<el-option :key="0" :value="''">-->
<div style="margin-left: 40px"> <!--<div style="margin-left: 40px">-->
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }}, <!--<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},-->
</span> <!--</span>-->
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link> <!--<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>-->
</div> <!--</div>-->
</el-option> <!--</el-option>-->
</div> <!--</div>-->
</el-select> <!--</el-select>-->
<ms-select-tree size="small" :data="moduleOptions" :defaultKey="basicForm.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -82,10 +84,11 @@
import {API_STATUS} from "../../model/JsonData"; import {API_STATUS} from "../../model/JsonData";
import {WORKSPACE_ID} from '../../../../../../common/js/constants'; import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag"; import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsSelectTree from "../../../../common/select-tree/SelectTree";
export default { export default {
name: "MsTcpBasicApi", name: "MsTcpBasicApi",
components: {MsInputTag}, components: {MsInputTag, MsSelectTree},
props: { props: {
currentProtocol: { currentProtocol: {
type: String, type: String,
@ -120,6 +123,11 @@ export default {
}, },
value: API_STATUS[0].id, value: API_STATUS[0].id,
options: API_STATUS, options: API_STATUS,
moduleObj: {
id: 'id',
label: 'name',
},
} }
}, },
methods: { methods: {
@ -148,6 +156,10 @@ export default {
methodChange() { methodChange() {
this.$emit("changeApiProtocol", this.basicForm.method); this.$emit("changeApiProtocol", this.basicForm.method);
}, },
setModule(id,data) {
this.basisData.modulePath = data.path;
this.basisData.moduleId = id;
},
} }
} }
</script> </script>

View File

@ -178,6 +178,10 @@
<div v-else-if="apiInfo.requestBodyParamType == 'JSON-SCHEMA'" style="margin-left: 10px"> <div v-else-if="apiInfo.requestBodyParamType == 'JSON-SCHEMA'" style="margin-left: 10px">
<ms-json-code-edit :body="apiInfo.jsonSchemaBody" ref="jsonCodeEdit"/> <ms-json-code-edit :body="apiInfo.jsonSchemaBody" ref="jsonCodeEdit"/>
</div> </div>
<!-- <div v-else-if="apiInfo.requestBodyParamType == 'XML'" style="margin-left: 10px">-->
<!-- <ms-json-code-edit :body="apiInfo.jsonSchemaBody" ref="jsonCodeEdit"/>-->
<!-- <editor v-model="formatData" :lang="mode" @init="editorInit" :theme="theme" :height="height"/>-->
<!-- </div>-->
<div v-else class="showDataDiv"> <div v-else class="showDataDiv">
<br/> <br/>
<p style="margin: 0px 20px;" <p style="margin: 0px 20px;"
@ -423,8 +427,13 @@ export default {
formatRowData(dataType, data) { formatRowData(dataType, data) {
var returnData = data; var returnData = data;
if (data) { if (data) {
if(dataType === 'XML'){
returnData = "<xmp>"+returnData+"</xmp>";
}else{
returnData = data.replace(/\n/g, '<br>'); returnData = data.replace(/\n/g, '<br>');
} }
}
return returnData; return returnData;
}, },
changeFixed(clientHeight) { changeFixed(clientHeight) {
@ -643,7 +652,7 @@ export default {
if(lastIndex < this.currentApiIndexInApiShowArray){ if(lastIndex < this.currentApiIndexInApiShowArray){
// //
if(this.needAsyncSelect){ // if(this.needAsyncSelect){
//apiShowArray 2 //apiShowArray 2
// apiStepIndex-1- 2 < apiInfoArray // apiStepIndex-1- 2 < apiInfoArray
let dataIndex = this.apiStepIndex -3; let dataIndex = this.apiStepIndex -3;
@ -657,11 +666,11 @@ export default {
if(this.apiShowArray.length > (this.currentApiIndexInApiShowArray+3)){ if(this.apiShowArray.length > (this.currentApiIndexInApiShowArray+3)){
this.apiShowArray.pop(); this.apiShowArray.pop();
} }
} // }
this.apiStepIndex --; this.apiStepIndex --;
}else if(lastIndex > this.currentApiIndexInApiShowArray){ }else if(lastIndex > this.currentApiIndexInApiShowArray){
// //
if(this.needAsyncSelect){ // if(this.needAsyncSelect){
//apiShowArray 2 //apiShowArray 2
// apiStepIndex+1+ 2 < apiInfoArray // apiStepIndex+1+ 2 < apiInfoArray
let dataIndex = this.apiStepIndex +3; let dataIndex = this.apiStepIndex +3;
@ -678,7 +687,7 @@ export default {
let itemHeight = this.$refs.apiDocInfoDivItem[0].offsetHeight+10; let itemHeight = this.$refs.apiDocInfoDivItem[0].offsetHeight+10;
this.$refs.apiDocInfoDiv.scrollTop = (apiDocDivScrollTop-itemHeight); this.$refs.apiDocInfoDiv.scrollTop = (apiDocDivScrollTop-itemHeight);
} }
} // }
this.apiStepIndex ++; this.apiStepIndex ++;
} }
} }

View File

@ -46,13 +46,15 @@
icon: 'el-icon-delete', icon: 'el-icon-delete',
func: this.deleteEnvironment func: this.deleteEnvironment
} }
] ],
selectEnvironmentId: ''
} }
}, },
methods: { methods: {
open: function (projectId) { open: function (projectId, envId) {
this.visible = true; this.visible = true;
this.projectId = projectId; this.projectId = projectId;
this.selectEnvironmentId = envId;
this.getEnvironments(); this.getEnvironments();
listenGoBack(this.close); listenGoBack(this.close);
}, },
@ -114,7 +116,16 @@
this.result = this.$get('/api/environment/list/' + this.projectId, response => { this.result = this.$get('/api/environment/list/' + this.projectId, response => {
this.environments = response.data; this.environments = response.data;
if (this.environments.length > 0) { if (this.environments.length > 0) {
if (this.selectEnvironmentId) {
const index = this.environments.findIndex(e => e.id === this.selectEnvironmentId);
if (index !== -1) {
this.$refs.environmentItems.itemSelected(index, this.environments[index]);
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]); this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else { } else {
let item = new Environment({ let item = new Environment({
projectId: this.projectId projectId: this.projectId

View File

@ -25,12 +25,10 @@
<el-row> <el-row>
<el-col :span="11"> <el-col :span="11">
<el-form-item :label="$t('commons.import_module')" prop="moduleId"> <el-form-item :label="$t('commons.import_module')" prop="moduleId">
<el-select size="small" v-model="formData.moduleId" class="project-select" clearable> <ms-select-tree size="small" :data="moduleOptions" :defaultKey="formData.moduleId" @getValue="setModule" :obj="moduleObj" clearable checkStrictly/>
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
</el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="!isScenarioModel&&showImportModel" :label="$t('commons.import_mode')" prop="modeId"> <el-form-item v-if="!isScenarioModel&&showImportModel" :label="$t('commons.import_mode')" prop="modeId">
<el-select size="small" v-model="formData.modeId" class="project-select" clearable> <el-select size="small" v-model="formData.modeId" clearable style="width: 100%">
<el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/> <el-option v-for="item in modeOptions" :key="item.id" :label="item.name" :value="item.id"/>
</el-select> </el-select>
</el-form-item> </el-form-item>
@ -101,16 +99,17 @@
import MsDialogFooter from "../../../../common/components/MsDialogFooter"; import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "@/common/js/utils"; import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
import ScheduleImport from "@/business/components/api/definition/components/import/ImportScheduleEdit"; import ScheduleImport from "@/business/components/api/definition/components/import/ImportScheduleEdit";
import MsSelectTree from "../../../../common/select-tree/SelectTree";
export default { export default {
name: "ApiImport", name: "ApiImport",
components: {ScheduleImport, MsDialogFooter}, components: {ScheduleImport, MsDialogFooter, MsSelectTree},
props: { props: {
saved: { saved: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
moduleOptions: {}, moduleOptions: Array,
propotal: String, propotal: String,
model: { model: {
type: String, type: String,
@ -123,6 +122,10 @@ export default {
swaggerUrlEnable: false, swaggerUrlEnable: false,
swaggerSynchronization: false, swaggerSynchronization: false,
showEnvironmentSelect: true, showEnvironmentSelect: true,
moduleObj: {
id: 'id',
label: 'name',
},
modeOptions: [{ modeOptions: [{
id: 'fullCoverage', id: 'fullCoverage',
name: this.$t('commons.cover') name: this.$t('commons.cover')
@ -185,9 +188,6 @@ export default {
modeId: [ modeId: [
{required: true, message: this.$t('commons.please_select_import_mode'), trigger: 'change'}, {required: true, message: this.$t('commons.please_select_import_mode'), trigger: 'change'},
], ],
moduleId: [
{required: true, message: this.$t('commons.please_select_import_module'), trigger: 'change'},
],
}, },
currentModule: {}, currentModule: {},
fileList: [] fileList: []
@ -324,6 +324,10 @@ export default {
} }
}); });
}, },
setModule(id, data) {
this.formData.moduleId = id;
this.formData.modulePath = data.path;
},
buildParam() { buildParam() {
let param = {}; let param = {};
Object.assign(param, this.formData); Object.assign(param, this.formData);
@ -332,11 +336,6 @@ export default {
param.model = this.model; param.model = this.model;
if (this.currentModule) { if (this.currentModule) {
param.moduleId = this.formData.moduleId param.moduleId = this.formData.moduleId
this.moduleOptions.filter(item => {
if (item.id === this.formData.moduleId) {
param.modulePath = item.path
}
})
param.modeId = this.formData.modeId param.modeId = this.formData.modeId
} }
param.projectId = this.projectId; param.projectId = this.projectId;

View File

@ -1,9 +1,11 @@
<template> <template>
<div> <div>
<div> <div>
<el-link type="primary" style="float:right;margin-top: 5px" @click="open">{{$t('commons.adv_search.title')}}</el-link> <el-link type="primary" style="float:right;margin-top: 5px" @click="open">{{ $t('commons.adv_search.title') }}
</el-link>
<el-input :placeholder="$t('commons.search_by_id_name_tag')" @blur="search" @keyup.enter.native="search" class="search-input" size="small" <el-input :placeholder="$t('commons.search_by_id_name_tag')" @blur="search" @keyup.enter.native="search"
class="search-input" size="small"
v-model="condition.name"/> v-model="condition.name"/>
<el-table v-loading="result.loading" <el-table v-loading="result.loading"
@ -71,7 +73,8 @@
<el-table-column v-if="item.id=='tags'" prop="tags" min-width="120px" :label="$t('commons.tag')" <el-table-column v-if="item.id=='tags'" prop="tags" min-width="120px" :label="$t('commons.tag')"
:key="index"> :key="index">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain" :content="itemName" style="margin-left: 0px; margin-right: 2px"/> <ms-tag v-for="(itemName,index) in scope.row.tags" :key="index" type="success" effect="plain"
:content="itemName" style="margin-left: 0px; margin-right: 2px"/>
</template> </template>
</el-table-column> </el-table-column>
@ -154,7 +157,7 @@ import {parseEnvironment} from "@/business/components/api/test/model/Environment
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover"; import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
import MsTableAdvSearchBar from "@/business/components/common/components/search/MsTableAdvSearchBar"; import MsTableAdvSearchBar from "@/business/components/common/components/search/MsTableAdvSearchBar";
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components"; import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
import {_filter, _handleSelect, _handleSelectAll, _sort, getLabel, getSystemLabel,} from "@/common/js/tableUtils"; import {_filter, _handleSelect, _handleSelectAll, _sort, getLabel} from "@/common/js/tableUtils";
import {API_CASE_LIST} from "@/common/js/constants"; import {API_CASE_LIST} from "@/common/js/constants";
import {Api_Case_List} from "@/business/components/common/model/JsonData"; import {Api_Case_List} from "@/business/components/common/model/JsonData";
import HeaderCustom from "@/business/components/common/head/HeaderCustom"; import HeaderCustom from "@/business/components/common/head/HeaderCustom";
@ -260,22 +263,28 @@ export default {
}, },
created: function () { created: function () {
this.initTable(); this.initTable();
getSystemLabel(this, this.type)
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.caseTable.bodyWrapper.scrollTop = 5 this.$refs.caseTable.bodyWrapper.scrollTop = 5
}) })
getLabel(this, API_CASE_LIST);
}, },
watch: { watch: {
selectNodeIds() { selectNodeIds() {
getLabel(this, API_CASE_LIST); this.selectAll = false;
this.unSelection = [];
this.selectDataCounts = 0;
this.initTable(); this.initTable();
}, },
currentProtocol() { currentProtocol() {
this.selectAll = false;
this.unSelection = [];
this.selectDataCounts = 0;
this.initTable(); this.initTable();
}, },
trashEnable() { trashEnable() {
if (this.trashEnable) { if (this.trashEnable) {
this.selectAll = false;
this.unSelection = [];
this.selectDataCounts = 0;
this.initTable(); this.initTable();
} }
}, },
@ -294,11 +303,9 @@ export default {
}, },
methods: { methods: {
customHeader() { customHeader() {
getLabel(this, API_CASE_LIST);
this.$refs.headerCustom.open(this.tableLabel) this.$refs.headerCustom.open(this.tableLabel)
}, },
initTable() { initTable() {
getLabel(this, API_CASE_LIST);
this.selectRows = new Set(); this.selectRows = new Set();
this.condition.status = ""; this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds; this.condition.moduleIds = this.selectNodeIds;
@ -306,9 +313,11 @@ export default {
this.condition.status = "Trash"; this.condition.status = "Trash";
this.condition.moduleIds = []; this.condition.moduleIds = [];
} }
if(!this.selectAll){
this.selectAll = false; this.selectAll = false;
this.unSelection = []; this.unSelection = [];
this.selectDataCounts = 0; this.selectDataCounts = 0;
}
this.condition.projectId = this.projectId; this.condition.projectId = this.projectId;
if (this.currentProtocol != null) { if (this.currentProtocol != null) {
@ -332,7 +341,10 @@ export default {
this.result = this.$post('/api/testcase/list/' + this.currentPage + "/" + this.pageSize, this.condition, response => { this.result = this.$post('/api/testcase/list/' + this.currentPage + "/" + this.pageSize, this.condition, response => {
this.total = response.data.itemCount; this.total = response.data.itemCount;
this.tableData = response.data.listObject; this.tableData = response.data.listObject;
if(!this.selectAll){
this.unSelection = response.data.listObject.map(s => s.id); this.unSelection = response.data.listObject.map(s => s.id);
}
this.tableData.forEach(item => { this.tableData.forEach(item => {
if (item.tags && item.tags.length > 0) { if (item.tags && item.tags.length > 0) {
@ -340,13 +352,43 @@ export default {
} }
}) })
if (this.$refs.caseTable) { if (this.$refs.caseTable) {
this.$refs.caseTable.doLayout() setTimeout(() => {
this.$refs.caseTable.doLayout();
this.result.loading = false;
}, 500)
} }
this.$nextTick(function(){
this.checkTableRowIsSelect();
})
}); });
} }
getLabel(this, API_CASE_LIST); getLabel(this, API_CASE_LIST);
}, },
checkTableRowIsSelect(){
//
if(this.selectAll){
let unSelectIds = this.unSelection;
this.tableData.forEach(row=>{
if(unSelectIds.indexOf(row.id)<0){
this.$refs.caseTable.toggleRowSelection(row,true);
//selectRows
if (!this.selectRows.has(row)) {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
}else{
//selectRow
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
}
}
})
}
},
open() { open() {
this.$refs.searchBar.open(); this.$refs.searchBar.open();
}, },
@ -414,7 +456,7 @@ export default {
if (action === 'confirm') { if (action === 'confirm') {
let obj = {}; let obj = {};
obj.projectId = this.projectId; obj.projectId = this.projectId;
obj.selectAllDate = this.isSelectAllDate; obj.selectAllDate = this.selectAll;
obj.unSelectIds = this.unSelection; obj.unSelectIds = this.unSelection;
obj.ids = Array.from(this.selectRows).map(row => row.id); obj.ids = Array.from(this.selectRows).map(row => row.id);
obj = Object.assign(obj, this.condition); obj = Object.assign(obj, this.condition);
@ -461,7 +503,7 @@ export default {
param[form.type] = form.value; param[form.type] = form.value;
param.ids = ids; param.ids = ids;
param.projectId = this.projectId; param.projectId = this.projectId;
param.selectAllDate = this.isSelectAllDate; param.selectAllDate = this.selectAll;
param.unSelectIds = this.unSelection; param.unSelectIds = this.unSelection;
param = Object.assign(param, this.condition); param = Object.assign(param, this.condition);
this.$post('/api/testcase/batch/editByParam', param, () => { this.$post('/api/testcase/batch/editByParam', param, () => {
@ -493,14 +535,14 @@ export default {
this.unSelection = allIDs.filter(function (val) { this.unSelection = allIDs.filter(function (val) {
return selectedIDs.indexOf(val) === -1 return selectedIDs.indexOf(val) === -1
}); });
if (this.isSelectAllDate) { if (this.selectAll) {
this.selectDataCounts = this.total - this.unSelection.length; this.selectDataCounts = this.total - this.unSelection.length;
} else { } else {
this.selectDataCounts = selection.size; this.selectDataCounts = selection.size;
} }
}, },
isSelectDataAll(dataType) { isSelectDataAll(dataType) {
this.isSelectAllDate = dataType; this.selectAll = dataType;
this.selectRowsCount(this.selectRows) this.selectRowsCount(this.selectRows)
// //
if (this.selectRows.size != this.tableData.length) { if (this.selectRows.size != this.tableData.length) {

View File

@ -235,7 +235,7 @@
import { import {
_handleSelect, _handleSelect,
_handleSelectAll, buildBatchParam, getLabel, _handleSelectAll, buildBatchParam, getLabel,
getSelectDataCounts, getSystemLabel, initCondition, getSelectDataCounts, initCondition,
setUnSelectIds, toggleAllSelection setUnSelectIds, toggleAllSelection
} from "@/common/js/tableUtils"; } from "@/common/js/tableUtils";
import {_filter, _sort} from "@/common/js/tableUtils"; import {_filter, _sort} from "@/common/js/tableUtils";
@ -380,16 +380,16 @@
} else { } else {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]}; this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
} }
this.getSystemLabel(this.type)
this.initTable(); this.initTable();
this.getMaintainerOptions(); this.getMaintainerOptions();
getLabel(this, API_LIST);
}, },
watch: { watch: {
selectNodeIds() { selectNodeIds() {
initCondition(this.condition,false);
this.initTable(); this.initTable();
}, },
currentProtocol() { currentProtocol() {
initCondition(this.condition,false);
this.initTable(); this.initTable();
}, },
trashEnable() { trashEnable() {
@ -399,21 +399,12 @@
} else { } else {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]}; this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
} }
initCondition(this.condition,false);
this.initTable(); this.initTable();
} }
}, },
methods: { methods: {
getSystemLabel(type) {
let param = {}
param.type = type
this.$post('/system/system/header', param, response => {
if (response.data != null) {
this.tableLabel = eval(response.data.props);
}
})
},
customHeader() { customHeader() {
getLabel(this, API_LIST);
this.$refs.headerCustom.open(this.tableLabel) this.$refs.headerCustom.open(this.tableLabel)
}, },
handleBatchMove() { handleBatchMove() {
@ -421,7 +412,7 @@
}, },
initTable() { initTable() {
this.selectRows = new Set(); this.selectRows = new Set();
initCondition(this.condition); initCondition(this.condition,this.condition.selectAll);
this.selectDataCounts = 0; this.selectDataCounts = 0;
this.condition.moduleIds = this.selectNodeIds; this.condition.moduleIds = this.selectNodeIds;
this.condition.projectId = this.projectId; this.condition.projectId = this.projectId;
@ -464,13 +455,42 @@
} }
}) })
if (this.$refs.apiDefinitionTable) { if (this.$refs.apiDefinitionTable) {
this.$refs.apiDefinitionTable.doLayout() setTimeout(() => {
this.$refs.apiDefinitionTable.doLayout();
this.result.loading = false;
}, 500)
} }
// nexttick:
this.$nextTick(function(){
this.checkTableRowIsSelect();
})
}); });
} }
getLabel(this, API_LIST); getLabel(this, API_LIST);
}, },
checkTableRowIsSelect(){
//
if(this.condition.selectAll){
let unSelectIds = this.condition.unSelectIds;
this.tableData.forEach(row=>{
if(unSelectIds.indexOf(row.id)<0){
this.$refs.apiDefinitionTable.toggleRowSelection(row,true);
//selectRows
if (!this.selectRows.has(row)) {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
}else{
//selectRow
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
}
}
})
}
},
genProtocalFilter(protocalType) { genProtocalFilter(protocalType) {
if (protocalType === "HTTP") { if (protocalType === "HTTP") {
this.methodFilters = [ this.methodFilters = [

View File

@ -20,6 +20,7 @@
:condition="condition" :condition="condition"
:current-module="currentModule" :current-module="currentModule"
:is-read-only="isReadOnly" :is-read-only="isReadOnly"
:moduleOptions="extendTreeNodes"
@exportAPI="exportAPI" @exportAPI="exportAPI"
@saveAsEdit="saveAsEdit" @saveAsEdit="saveAsEdit"
@refreshTable="$emit('refreshTable')" @refreshTable="$emit('refreshTable')"
@ -40,7 +41,7 @@ import {OPTIONS} from "../../model/JsonData";
import ApiImport from "../import/ApiImport"; import ApiImport from "../import/ApiImport";
import MsNodeTree from "../../../../track/common/NodeTree"; import MsNodeTree from "../../../../track/common/NodeTree";
import ApiModuleHeader from "./ApiModuleHeader"; import ApiModuleHeader from "./ApiModuleHeader";
import {buildNodePath} from "../../model/NodeTree"; import {buildNodePath, buildTree} from "../../model/NodeTree";
export default { export default {
name: 'MsApiModule', name: 'MsApiModule',
@ -61,6 +62,7 @@ export default {
}, },
data: [], data: [],
currentModule: {}, currentModule: {},
extendTreeNodes: [],
} }
}, },
props: { props: {
@ -129,11 +131,17 @@ export default {
this.result = this.$get(url, response => { this.result = this.$get(url, response => {
if (response.data != undefined && response.data != null) { if (response.data != undefined && response.data != null) {
this.data = response.data; this.data = response.data;
let moduleOptions = []; this.extendTreeNodes = [];
this.data.forEach(node => { this.extendTreeNodes.unshift({
buildNodePath(node, {path: ''}, moduleOptions); "id": "root",
"name": this.$t('commons.module_title'),
"level": 0,
"children": this.data,
}); });
this.$emit('setModuleOptions', moduleOptions); this.extendTreeNodes.forEach(node => {
buildTree(node, {path: ''});
});
this.$emit('setModuleOptions', this.extendTreeNodes);
this.$emit('setNodeTree', this.data); this.$emit('setNodeTree', this.data);
if (this.$refs.nodeTree) { if (this.$refs.nodeTree) {
this.$refs.nodeTree.filter(this.condition.filterText); this.$refs.nodeTree.filter(this.condition.filterText);

View File

@ -1,5 +1,7 @@
<template> <template>
<div> <div>
<el-row>
<el-col class="protocol-col" :span="9">
<el-select class="protocol-select" size="small" v-model="condition.protocol"> <el-select class="protocol-select" size="small" v-model="condition.protocol">
<el-option <el-option
v-for="item in options" v-for="item in options"
@ -9,38 +11,13 @@
:disabled="item.disabled"> :disabled="item.disabled">
</el-option> </el-option>
</el-select> </el-select>
<el-input class="filter-input" :class="{'read-only': isReadOnly}" :placeholder="$t('test_track.module.search')" v-model="condition.filterText" </el-col>
size="small"> <el-col :span="15">
<template v-slot:append> <ms-search-bar
<el-dropdown v-if="!isReadOnly" size="small" split-button type="primary" class="ms-api-button" @click="handleCommand('add-api')" :condition="condition"
v-tester :commands="operators"/>
@command="handleCommand" </el-col>
:hide-on-click='false' </el-row>
trigger="click">
<el-button icon="el-icon-folder-add" @click="addApi"></el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="add-api">{{ $t('api_test.definition.request.title') }}</el-dropdown-item>
<el-dropdown-item command="debug">{{ $t('api_test.definition.request.fast_debug') }}</el-dropdown-item>
<el-dropdown-item command="import">{{ $t('api_test.api_import.label') }}</el-dropdown-item>
<el-dropdown-item command="export">
<el-dropdown placement="right-start" @command="chooseExportType">
<span>
{{ $t('report.export') }} <i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<template>
<el-dropdown-item command="export-MS">{{ $t('report.export_to_ms_format') }}</el-dropdown-item>
<el-dropdown-item command="export-Swagger" v-show="condition.protocol=='HTTP'">
{{ $t('report.export_to_swagger3_format') }}
</el-dropdown-item>
</template>
</el-dropdown-menu>
</el-dropdown>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-input>
<module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/> <module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/>
@ -60,14 +37,52 @@ import ApiImport from "../import/ApiImport";
import ModuleTrashButton from "./ModuleTrashButton"; import ModuleTrashButton from "./ModuleTrashButton";
import {buildNodePath} from "@/business/components/api/definition/model/NodeTree"; import {buildNodePath} from "@/business/components/api/definition/model/NodeTree";
import TemplateComponent from "../../../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent"; import TemplateComponent from "../../../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import MsSearchBar from "@/business/components/common/components/search/MsSearchBar";
export default { export default {
name: "ApiModuleHeader", name: "ApiModuleHeader",
components: {TemplateComponent, ModuleTrashButton, ApiImport, MsAddBasisApi}, components: {MsSearchBar, TemplateComponent, ModuleTrashButton, ApiImport, MsAddBasisApi},
data() { data() {
return { return {
options: OPTIONS, options: OPTIONS,
moduleOptions: {} operators: [
{
label: this.$t('api_test.definition.request.title'),
callback: this.addApi
},
{
label: this.$t('api_test.definition.request.fast_debug'),
callback: () => {this.$emit('debug')}
},
{
label: this.$t('api_test.api_import.label'),
callback: this.handleImport
},
{
label: this.$t('report.export'),
children: [
{
label: this.$t('report.export_to_ms_format') ,
callback: () => {
if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
this.$emit('exportAPI', 'MS');
}
},
{
label: this.$t('report.export_to_swagger3_format'),
callback: () => {
if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
this.$emit('exportAPI', 'Swagger');
}
}
]
}
]
} }
}, },
props: { props: {
@ -77,6 +92,7 @@ export default {
return {} return {}
} }
}, },
moduleOptions: Array,
currentModule: { currentModule: {
type: Object, type: Object,
default() { default() {
@ -96,50 +112,13 @@ export default {
}, },
}, },
methods: { methods: {
handleImport() {
handleCommand(e) {
switch (e) {
case "debug":
this.$emit('debug');
break;
case "add-api":
this.addApi();
break;
case "add-module":
break;
case "import":
if (!this.projectId) { if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip')); this.$warning(this.$t('commons.check_project_tip'));
return; return;
} }
this.protocol = "HTTP"; this.protocol = "HTTP";
this.result = this.$get("/api/module/list/" + this.projectId + "/" + this.condition.protocol, response => {
if (response.data != undefined && response.data != null) {
this.data = response.data;
let moduleOptions = [];
this.data.forEach(node => {
buildNodePath(node, {path: ''}, moduleOptions);
});
this.moduleOptions = moduleOptions
}
this.$refs.apiImport.open(this.moduleOptions); this.$refs.apiImport.open(this.moduleOptions);
});
break;
}
},
chooseExportType(e) {
if (!this.projectId) {
this.$warning(this.$t('commons.check_project_tip'));
return;
}
switch (e) {
case "export-MS":
this.$emit('exportAPI', 'MS');
break;
case "export-Swagger":
this.$emit('exportAPI', 'Swagger');
break;
}
}, },
addApi() { addApi() {
if (!this.projectId) { if (!this.projectId) {
@ -162,19 +141,18 @@ export default {
</script> </script>
<style scoped> <style scoped>
.protocol-select { .protocol-select {
width: 92px; width: 92px;
height: 30px; height: 30px;
} }
.protocol-col {
min-width: 93px;
}
.read-only { .read-only {
width: 150px !important; width: 150px !important;
} }
.filter-input { .filter-input {
width: 174px; width: 174px;
padding-left: 3px; padding-left: 3px;
} }
</style> </style>

View File

@ -9,4 +9,16 @@ export function buildNodePath(node, option, moduleOptions) {
} }
} }
} }
// 构建树
export function buildTree(node, option) {
option.id = node.id;
option.name = node.name;
option.path = option.path + '/' + node.name;
node.path = option.path;
if (node.children) {
for (let i = 0; i < node.children.length; i++) {
buildTree(node.children[i], {path: option.path});
}
}
}

View File

@ -46,13 +46,15 @@
icon: 'el-icon-delete', icon: 'el-icon-delete',
func: this.deleteEnvironment func: this.deleteEnvironment
} }
] ],
selectEnvironmentId: ''
} }
}, },
methods: { methods: {
open: function (projectId) { open: function (projectId, envId) {
this.visible = true; this.visible = true;
this.projectId = projectId; this.projectId = projectId;
this.selectEnvironmentId = envId;
this.getEnvironments(); this.getEnvironments();
listenGoBack(this.close); listenGoBack(this.close);
}, },
@ -114,7 +116,16 @@
this.result = this.$get('/api/environment/list/' + this.projectId, response => { this.result = this.$get('/api/environment/list/' + this.projectId, response => {
this.environments = response.data; this.environments = response.data;
if (this.environments.length > 0) { if (this.environments.length > 0) {
if (this.selectEnvironmentId) {
const index = this.environments.findIndex(e => e.id === this.selectEnvironmentId);
if (index !== -1) {
this.$refs.environmentItems.itemSelected(index, this.environments[index]);
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]); this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else { } else {
let item = new Environment({ let item = new Environment({
projectId: this.projectId projectId: this.projectId

View File

@ -155,7 +155,7 @@ export default {
width: 100%; width: 100%;
background: white; background: white;
height: 100vh; height: 100vh;
z-index: 999999; z-index: 2;
} }
.full-screen >>> .minder-container { .full-screen >>> .minder-container {

View File

@ -0,0 +1,78 @@
<template>
<el-input class="ms-search-bar" :placeholder="$t('test_track.module.search')" v-model="condition.filterText" size="small">
<template v-slot:append>
<el-dropdown>
<el-button type="primary">
<span class="tip-font">{{ $t('commons.more_operator') }}</span>
<i class="el-icon-arrow-down el-icon--right"/>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item v-for="(item, index) in commands" :key="index" @click.native.stop="click(item)">
<span class="tip-font" v-if="!item.children">
{{ item.label }}
</span>
<el-dropdown placement="right-start" v-else>
<span class="tip-font">
{{ item.label }}
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<template>
<el-dropdown-item v-for="(child, index) in item.children" :key="index" @click.native.stop="click(child)">
<span class="tip-font">
{{child.label}}
</span>
</el-dropdown-item>
</template>
</el-dropdown-menu>
</el-dropdown>
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
</el-input>
</template>
<script>
export default {
name: "MsSearchBar",
props: {
condition: {
type: Object,
default() {
return {}
}
},
commands: {
type: Array,
default() {
return [
{
label: this.$t('api_test.api_import.label'),
callback: () => {}
}
]
}
}
},
methods: {
click(item) {
if (item.callback) {
item.callback();
}
}
}
}
</script>
<style scoped>
.tip-font {
font-size: 13px;
}
.el-dropdown .el-button {
padding: 8px;
}
</style>

View File

@ -52,6 +52,10 @@ export default {
this.defaultCheckedKeys.push(i.id) this.defaultCheckedKeys.push(i.id)
} }
) )
if(this.type==='api_list'||this.type==='api_case_list'||this.type==='api_scenario_list'||this.type==='test_plan_function_test_case'
||this.type==='test_plan_api_case'||this.type==='test_plan_load_case'||this.type==='test_plan_scenario_case'){
this.fieldSelected=items
}
}, },
saveHeader() { saveHeader() {
let param = { let param = {

View File

@ -81,7 +81,6 @@ export const Test_Case_Review_Case_List = [
{id: 'name', label: i18n.t('commons.name')}, {id: 'name', label: i18n.t('commons.name')},
{id: 'priority', label: i18n.t('test_track.case.priority')}, {id: 'priority', label: i18n.t('test_track.case.priority')},
{id: 'type', label: i18n.t('test_track.case.type')}, {id: 'type', label: i18n.t('test_track.case.type')},
{id: 'method', label: i18n.t('test_track.case.method')},
{id: 'nodePath', label: i18n.t('test_track.case.module')}, {id: 'nodePath', label: i18n.t('test_track.case.module')},
{id: 'projectName', label: i18n.t('test_track.review.review_project')}, {id: 'projectName', label: i18n.t('test_track.review.review_project')},
{id: 'reviewerName', label: i18n.t('test_track.review.reviewer')}, {id: 'reviewerName', label: i18n.t('test_track.review.reviewer')},
@ -107,7 +106,7 @@ export const Test_Plan_Function_Test_Case = [
//测试计划-api用例 //测试计划-api用例
export const Test_Plan_Api_Case = [ export const Test_Plan_Api_Case = [
{id: 'num', label: i18n.t('commons.id')}, {id: 'num', label: i18n.t('commons.id')},
{id: 'name', label: i18n.t('commons.name')}, {id: 'name', label: i18n.t('api_test.definition.api_name')},
{id: 'priority', label: i18n.t('test_track.case.priority')}, {id: 'priority', label: i18n.t('test_track.case.priority')},
{id: 'path', label: i18n.t('api_test.definition.api_path')}, {id: 'path', label: i18n.t('api_test.definition.api_path')},
{id: 'createUser', label: '创建人'}, {id: 'createUser', label: '创建人'},
@ -124,12 +123,12 @@ export const Test_Plan_Load_Case = [
{id: 'createTime', label: i18n.t('commons.create_time')}, {id: 'createTime', label: i18n.t('commons.create_time')},
{id: 'status', label: i18n.t('commons.status')}, {id: 'status', label: i18n.t('commons.status')},
{id: 'caseStatus', label: i18n.t('test_track.plan.load_case.execution_status')}, {id: 'caseStatus', label: i18n.t('test_track.plan.load_case.execution_status')},
{id: 'loadReportId', label: i18n.t('test_track.plan.load_case.view_report')}, {id: 'loadReportId', label: i18n.t('test_track.plan.load_case.report')},
] ]
//测试计划-场景用例 //测试计划-场景用例
export const Test_Plan_Scenario_Case = [ export const Test_Plan_Scenario_Case = [
{id: 'num', label: i18n.t('commons.id')}, {id: 'num', label: i18n.t('commons.id')},
{id: 'name', label: i18n.t('commons.name')}, {id: 'name', label: i18n.t('api_test.automation.scenario_name')},
{id: 'level', label: i18n.t('api_test.automation.case_level')}, {id: 'level', label: i18n.t('api_test.automation.case_level')},
{id: 'tagNames', label: i18n.t('api_test.automation.tag')}, {id: 'tagNames', label: i18n.t('api_test.automation.tag')},
{id: 'userId', label: i18n.t('api_test.automation.creator')}, {id: 'userId', label: i18n.t('api_test.automation.creator')},

View File

@ -152,6 +152,9 @@
return JSON.stringify(this.data).indexOf(this.obj.children) !== -1 ? this.data : this.switchTree(); return JSON.stringify(this.data).indexOf(this.obj.children) !== -1 ? this.data : this.switchTree();
}, },
}, },
mounted() {
this.init();
},
methods: { methods: {
outsideClick(e) { outsideClick(e) {
e.stopPropagation(); e.stopPropagation();
@ -233,7 +236,9 @@
setKey(thisKey) { setKey(thisKey) {
this.$refs.tree.setCurrentKey(thisKey); this.$refs.tree.setCurrentKey(thisKey);
let node = this.$refs.tree.getNode(thisKey); let node = this.$refs.tree.getNode(thisKey);
if (node && node.data) {
this.setData(node.data); this.setData(node.data);
}
}, },
// //
setData(data) { setData(data) {
@ -284,7 +289,7 @@
}, },
// //
popoverHide() { popoverHide() {
this.$emit('getValue', this.returnDataKeys, this.returnDatas); this.$emit('getValue', this.returnDataKeys, this.returnDatas ? this.returnDatas : {});
}, },
// //
clearSelectedNodes() { clearSelectedNodes() {
@ -340,11 +345,14 @@
// select // select
this.$refs.select.blur(); this.$refs.select.blur();
}, },
treeData() {//tree treeData: {//tree
handler: function () {
this.$nextTick(() => { this.$nextTick(() => {
this.init(); this.init();
}) })
}, },
deep: true
},
filterText(val) { filterText(val) {
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.tree.filter(val); this.$refs.tree.filter(val);

View File

@ -0,0 +1,40 @@
<template>
<ms-container>
<ms-main-container>
<el-card>
</el-card>
</ms-main-container>
</ms-container>
</template>
<script>
import MsContainer from "@/business/components/common/components/MsContainer";
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
export default {
name: "PerformanceReportCompare",
components: {MsMainContainer, MsContainer},
mounted() {
let reportId = this.$route.path.split('/')[4];
console.log(reportId);
let items = localStorage.getItem("compareReportIds");
console.log(JSON.parse(items));
},
computed: {
isReadOnly() {
return !checkoutTestManagerOrTestUser();
}
},
data() {
return {}
},
methods: {}
}
</script>
<style scoped>
</style>

View File

@ -28,10 +28,6 @@
<el-button :disabled="isReadOnly" type="warning" plain size="mini" @click="downloadJtl()"> <el-button :disabled="isReadOnly" type="warning" plain size="mini" @click="downloadJtl()">
{{ $t('report.downloadJtl') }} {{ $t('report.downloadJtl') }}
</el-button> </el-button>
<!--<el-button :disabled="isReadOnly" type="warning" plain size="mini">-->
<!--{{$t('report.compare')}}-->
<!--</el-button>-->
</el-row> </el-row>
</el-col> </el-col>
<el-col :span="8"> <el-col :span="8">
@ -83,6 +79,7 @@
</div> </div>
</el-dialog> </el-dialog>
</ms-main-container> </ms-main-container>
<same-test-reports ref="compareReports"/>
</ms-container> </ms-container>
</template> </template>
@ -99,11 +96,13 @@ import {checkoutTestManagerOrTestUser, exportPdf} from "@/common/js/utils";
import html2canvas from 'html2canvas'; import html2canvas from 'html2canvas';
import MsPerformanceReportExport from "./PerformanceReportExport"; import MsPerformanceReportExport from "./PerformanceReportExport";
import {Message} from "element-ui"; import {Message} from "element-ui";
import SameTestReports from "@/business/components/performance/report/components/SameTestReports";
export default { export default {
name: "PerformanceReportView", name: "PerformanceReportView",
components: { components: {
SameTestReports,
MsPerformanceReportExport, MsPerformanceReportExport,
MsReportErrorLog, MsReportErrorLog,
MsReportLogDetails, MsReportLogDetails,
@ -312,6 +311,9 @@ export default {
Message.error({message: JSON.parse(data).message || e.message, showClose: true}); Message.error({message: JSON.parse(data).message || e.message, showClose: true});
}); });
}); });
},
compareReports() {
this.$refs.compareReports.open(this.report);
} }
}, },
created() { created() {

View File

@ -25,37 +25,32 @@
<el-table-column <el-table-column
prop="name" prop="name"
:label="$t('commons.name')" :label="$t('commons.name')"
width="150"
show-overflow-tooltip> show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="testName" prop="testName"
:label="$t('report.test_name')" :label="$t('report.test_name')"
width="150"
show-overflow-tooltip> show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="projectName" prop="projectName"
:label="$t('report.project_name')" :label="$t('report.project_name')"
width="150"
show-overflow-tooltip> show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="userName" prop="userName"
:label="$t('report.user_name')" :label="$t('report.user_name')"
width="150"
show-overflow-tooltip> show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
prop="createTime" prop="createTime"
sortable sortable
width="250"
:label="$t('commons.create_time')"> :label="$t('commons.create_time')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span> <span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="triggerMode" width="150" :label="'触发方式'" column-key="triggerMode" <el-table-column prop="triggerMode" width="150" :label="$t('test_track.report.list.trigger_mode')" column-key="triggerMode"
:filters="triggerFilters"> :filters="triggerFilters">
<template v-slot:default="scope"> <template v-slot:default="scope">
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/> <report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
@ -85,6 +80,7 @@
:total="total"/> :total="total"/>
</el-card> </el-card>
</ms-main-container> </ms-main-container>
<same-test-reports ref="compareReports"/>
</ms-container> </ms-container>
</template> </template>
@ -101,11 +97,14 @@ import MsTableHeader from "../../common/components/MsTableHeader";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent"; import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
import ShowMoreBtn from "../../track/case/components/ShowMoreBtn"; import ShowMoreBtn from "../../track/case/components/ShowMoreBtn";
import {_filter, _sort} from "@/common/js/tableUtils"; import {_filter, _sort} from "@/common/js/tableUtils";
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import SameTestReports from "@/business/components/performance/report/components/SameTestReports";
export default { export default {
name: "PerformanceTestReportList", name: "PerformanceTestReportList",
components: { components: {
SameTestReports,
MsDialogFooter,
MsTableHeader, MsTableHeader,
ReportTriggerModeItem, ReportTriggerModeItem,
MsTableOperatorButton, MsTableOperatorButton,
@ -200,6 +199,9 @@ export default {
} }
}); });
}, },
handleDiff(report) {
this.$refs.compareReports.open(report);
},
_handleDeleteNoMsg(report) { _handleDeleteNoMsg(report) {
this.result = this.$post(this.deletePath + report.id, {}, () => { this.result = this.$post(this.deletePath + report.id, {}, () => {
this.initTableData(); this.initTableData();

View File

@ -1,16 +1,22 @@
<template> <template>
<div v-loading="result.loading" class="pressure-config-container"> <div v-loading="result.loading" class="pressure-config-container">
<el-row> <el-row>
<el-col :span="10"> <el-col :span="12">
<el-collapse v-model="activeNames" accordion> <el-collapse v-model="activeNames" accordion>
<el-collapse-item :name="index" <el-collapse-item :name="index"
v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')" v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')"
:key="index"> :key="index">
<template slot="title"> <template slot="title">
<div style="padding-right: 10px"> <el-row>
<el-col :span="14">
<el-tooltip :content="threadGroup.attributes.testname" placement="top">
<div style="padding-right:20px; font-size: 16px;" class="wordwrap">
{{ threadGroup.attributes.testname }} {{ threadGroup.attributes.testname }}
</div> </div>
</el-tooltip>
</el-col>
<el-col :span="10">
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'"> <el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'">
{{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }}, {{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }},
{{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }} {{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }}
@ -19,6 +25,8 @@
{{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }}, {{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }},
{{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }} {{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }}
</el-tag> </el-tag>
</el-col>
</el-row>
</template> </template>
<el-form :inline="true"> <el-form :inline="true">
<el-form-item :label="$t('load_test.thread_num')"> <el-form-item :label="$t('load_test.thread_num')">
@ -134,7 +142,7 @@
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</el-col> </el-col>
<el-col :span="14"> <el-col :span="12">
<ms-chart class="chart-container" ref="chart1" :options="options" :autoresize="true"></ms-chart> <ms-chart class="chart-container" ref="chart1" :options="options" :autoresize="true"></ms-chart>
</el-col> </el-col>
</el-row> </el-row>
@ -578,4 +586,9 @@ export default {
.title { .title {
margin-left: 60px; margin-left: 60px;
} }
.wordwrap {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style> </style>

View File

@ -37,7 +37,7 @@
<el-table-column <el-table-column
prop="ko" prop="ko"
label="KO" label="FAIL"
align="center" align="center"
/> />

View File

@ -0,0 +1,117 @@
<template>
<el-dialog :close-on-click-modal="false"
:destroy-on-close="true"
:title="$t('已完成的测试报告')" width="60%"
:visible.sync="loadReportVisible">
<el-table v-loading="reportLoadingResult.loading"
class="basic-config"
:data="compareReports"
@select-all="handleSelectAll"
@select="handleSelectionChange">
<el-table-column type="selection"/>
<el-table-column
prop="name"
:label="$t('commons.name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="userName"
:label="$t('report.user_name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column prop="triggerMode"
:label="$t('test_track.report.list.trigger_mode')">
<template v-slot:default="scope">
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
</template>
</el-table-column>
<el-table-column
:label="$t('commons.create_time')">
<template v-slot:default="scope">
<i class="el-icon-time"/>
<span class="last-modified">{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="getCompareReports" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<template v-slot:footer>
<ms-dialog-footer @cancel="close" @confirm="handleCompare"/>
</template>
</el-dialog>
</template>
<script>
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import ReportTriggerModeItem from "@/business/components/common/tableItem/ReportTriggerModeItem";
export default {
name: "SameTestReports",
components: {ReportTriggerModeItem, MsDialogFooter, MsTablePagination},
data() {
return {
loadReportVisible: false,
reportLoadingResult: {},
compareReports: [],
currentPage: 1,
pageSize: 10,
total: 0,
selectIds: new Set,
}
},
methods: {
open(report) {
this.getCompareReports(report.testId);
this.loadReportVisible = true;
},
close() {
this.loadReportVisible = false;
},
getCompareReports(testId) {
let condition = {
testId: testId,
filters: {status: ["Completed"]}
};
this.reportLoadingResult = this.$post('/performance/report/list/all/' + this.currentPage + "/" + this.pageSize, condition, res => {
let data = res.data;
this.total = data.itemCount;
this.compareReports = data.listObject;
})
},
handleCompare() {
let reportIds = [...this.selectIds];
localStorage.setItem("compareReportIds", JSON.stringify(reportIds));
this.close();
this.$router.push({path: '/performance/report/compare/' + reportIds[0]});
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.compareReports.forEach(item => {
this.selectIds.add(item.id);
});
} else {
this.compareReports.forEach(item => {
if (this.selectIds.has(item.id)) {
this.selectIds.delete(item.id);
}
});
}
},
handleSelectionChange(selection, row) {
if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id);
} else {
this.selectIds.add(row.id);
}
},
}
}
</script>
<style scoped>
</style>

View File

@ -1,5 +1,3 @@
import MsProject from "@/business/components/settings/project/MsProject";
const PerformanceTest = () => import('@/business/components/performance/PerformanceTest') const PerformanceTest = () => import('@/business/components/performance/PerformanceTest')
const PerformanceTestHome = () => import('@/business/components/performance/home/PerformanceTestHome') const PerformanceTestHome = () => import('@/business/components/performance/home/PerformanceTestHome')
const EditPerformanceTest = () => import('@/business/components/performance/test/EditPerformanceTest') const EditPerformanceTest = () => import('@/business/components/performance/test/EditPerformanceTest')
@ -7,6 +5,7 @@ const PerformanceTestList = () => import('@/business/components/performance/test
const PerformanceTestReportList = () => import('@/business/components/performance/report/PerformanceTestReportList') const PerformanceTestReportList = () => import('@/business/components/performance/report/PerformanceTestReportList')
const PerformanceChart = () => import('@/business/components/performance/report/components/PerformanceChart') const PerformanceChart = () => import('@/business/components/performance/report/components/PerformanceChart')
const PerformanceReportView = () => import('@/business/components/performance/report/PerformanceReportView') const PerformanceReportView = () => import('@/business/components/performance/report/PerformanceReportView')
const PerformanceReportCompare = () => import('@/business/components/performance/report/PerformanceReportCompare')
export default { export default {
path: "/performance", path: "/performance",
@ -62,6 +61,11 @@ export default {
path: "report/view/:reportId", path: "report/view/:reportId",
name: "perReportView", name: "perReportView",
component: PerformanceReportView component: PerformanceReportView
} },
{
path: "report/compare/:reportId",
name: "ReportCompare",
component: PerformanceReportCompare,
},
] ]
} }

View File

@ -157,7 +157,7 @@ export default {
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
let row = rows[i]; let row = rows[i];
if (this.tableData.filter(f => f.name === row.name).length > 0) { if (this.tableData.filter(f => f.name === row.name).length > 0) {
this.$error(this.$t('load_test.delete_file')); this.$error(this.$t('load_test.delete_file') + ', name: ' + row.name);
this.selectIds.clear(); this.selectIds.clear();
return; return;
} }
@ -216,7 +216,7 @@ export default {
} }
if (this.tableData.filter(f => f.name === file.name).length > 0) { if (this.tableData.filter(f => f.name === file.name).length > 0) {
this.$error(this.$t('load_test.delete_file')); this.$error(this.$t('load_test.delete_file') + ', name: ' + file.name);
return false; return false;
} }
}, },

View File

@ -121,7 +121,7 @@ export default {
for (let i = 0; i < rows.length; i++) { for (let i = 0; i < rows.length; i++) {
let row = rows[i]; let row = rows[i];
if (this.tableData.filter(f => f.name === row.name + ".jmx").length > 0) { if (this.tableData.filter(f => f.name === row.name + ".jmx").length > 0) {
this.$error(this.$t('load_test.delete_file')); this.$error(this.$t('load_test.delete_file') + ', name: ' + row.name);
return; return;
} }
} }

View File

@ -4,7 +4,7 @@
<el-col> <el-col>
<el-form :inline="true"> <el-form :inline="true">
<el-form-item :label="$t('load_test.select_resource_pool')"> <el-form-item :label="$t('load_test.select_resource_pool')">
<el-select v-model="resourcePool" :disabled="isReadOnly" size="mini"> <el-select v-model="resourcePool" :disabled="isReadOnly" size="mini" @change="resourcePoolChange">
<el-option <el-option
v-for="item in resourcePools" v-for="item in resourcePools"
:key="item.id" :key="item.id"
@ -23,9 +23,15 @@
v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')" v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')"
:key="index"> :key="index">
<template slot="title"> <template slot="title">
<div style="padding-right: 20px; font-size: 16px;"> <el-row>
<el-col :span="14">
<el-tooltip :content="threadGroup.attributes.testname" placement="top">
<div style="padding-right:20px; font-size: 16px;" class="wordwrap">
{{ threadGroup.attributes.testname }} {{ threadGroup.attributes.testname }}
</div> </div>
</el-tooltip>
</el-col>
<el-col :span="10">
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'"> <el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'DURATION'">
{{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }}, {{ $t('load_test.thread_num') }}{{ threadGroup.threadNumber }},
{{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }} {{ $t('load_test.duration') }}: {{ threadGroup.duration }} {{ getUnitLabel(threadGroup) }}
@ -34,6 +40,8 @@
{{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }}, {{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }},
{{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }} {{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }}
</el-tag> </el-tag>
</el-col>
</el-row>
</template> </template>
<el-form :inline="true"> <el-form :inline="true">
<el-form-item :label="$t('load_test.thread_num')"> <el-form-item :label="$t('load_test.thread_num')">
@ -42,6 +50,7 @@
v-model="threadGroup.threadNumber" v-model="threadGroup.threadNumber"
@change="calculateTotalChart(threadGroup)" @change="calculateTotalChart(threadGroup)"
:min="resourcePoolResourceLength" :min="resourcePoolResourceLength"
:max="maxThreadNumbers"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<br> <br>
@ -109,6 +118,7 @@
<el-input-number <el-input-number
:disabled="isReadOnly" :disabled="isReadOnly"
:min="1" :min="1"
:max="threadGroup.duration"
v-model="threadGroup.rampUpTime" v-model="threadGroup.rampUpTime"
@change="calculateTotalChart(threadGroup)" @change="calculateTotalChart(threadGroup)"
size="mini"/> size="mini"/>
@ -219,7 +229,8 @@ export default {
resourcePools: [], resourcePools: [],
activeNames: ["0"], activeNames: ["0"],
threadGroups: [], threadGroups: [],
resourcePoolResourceLength: 1 resourcePoolResourceLength: 1,
maxThreadNumbers: 5000,
} }
}, },
mounted() { mounted() {
@ -336,6 +347,22 @@ export default {
}); });
} }
}, },
resourcePoolChange() {
let result = this.resourcePools.filter(p => p.id === this.resourcePool);
if (result.length === 1) {
let threadNumber = 0;
result[0].resources.forEach(resource => {
threadNumber += JSON.parse(resource.configuration).maxConcurrency;
})
this.maxThreadNumbers = threadNumber;
this.threadGroups.forEach(tg => {
if (tg.threadNumber > threadNumber) {
this.$set(tg, "threadNumber", threadNumber);
}
})
this.calculateTotalChart();
}
},
calculateTotalChart() { calculateTotalChart() {
let handler = this; let handler = this;
if (handler.duration < handler.rampUpTime) { if (handler.duration < handler.rampUpTime) {
@ -344,6 +371,11 @@ export default {
if (handler.rampUpTime < handler.step) { if (handler.rampUpTime < handler.step) {
handler.step = handler.rampUpTime; handler.step = handler.rampUpTime;
} }
// 线
let resourcePool = this.resourcePools.filter(v => v.id === this.resourcePool)[0];
if (resourcePool) {
this.resourcePoolResourceLength = resourcePool.resources.length;
}
let color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3']; let color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'];
handler.options = { handler.options = {
color: color, color: color,
@ -682,4 +714,10 @@ export default {
.title { .title {
margin-left: 60px; margin-left: 60px;
} }
.wordwrap {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style> </style>

View File

@ -216,8 +216,36 @@
}) })
} }
this.total = data.itemCount; this.total = data.itemCount;
this.$nextTick(function(){
this.checkTableRowIsSelect();
});
}) })
}, },
checkTableRowIsSelect(){
//
if(this.condition.selectAll){
let unSelectIds = this.condition.unSelectIds;
this.tableData.forEach(row=>{
if(unSelectIds.indexOf(row.id)<0){
this.$refs.userTable.toggleRowSelection(row,true);
//selectRows
if (!this.selectRows.has(row)) {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
}else{
//selectRow
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
}
}
})
}
},
buildPagePath(path) { buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize; return path + "/" + this.currentPage + "/" + this.pageSize;
}, },

View File

@ -598,7 +598,7 @@ export default {
return; return;
} }
this.selectRows = new Set(); this.selectRows = new Set();
this.condition.selectAll = false; // this.condition.selectAll = false;
this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => { this.result = this.$post(this.buildPagePath(this.queryPath), this.condition, response => {
let data = response.data; let data = response.data;
this.total = data.itemCount; this.total = data.itemCount;
@ -615,8 +615,38 @@ export default {
}); });
} }
} }
this.$nextTick(function(){
this.checkTableRowIsSelect();
});
}) })
}, },
checkTableRowIsSelect(){
//
if(this.condition.selectAll){
let unSelectIds = this.condition.unSelectIds;
this.tableData.forEach(row=>{
if(unSelectIds.indexOf(row.id)<0){
this.$refs.userTable.toggleRowSelection(row,true);
//selectRows
if (!this.selectRows.has(row)) {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
}else{
//selectRow
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
}
}
})
}
},
handleClose() { handleClose() {
this.form = {roles: [{id: ''}]}; this.form = {roles: [{id: ''}]};
this.btnAddRole = false; this.btnAddRole = false;

Some files were not shown because too many files have changed in this diff Show More