Merge branch 'master' of https://github.com/metersphere/metersphere
Conflicts: backend/src/main/java/io/metersphere/api/parse/ApiImportAbstractParser.java
This commit is contained in:
commit
b3808d8c90
|
@ -7,9 +7,9 @@
|
|||
|
||||
> [English](README-EN.md) | 中文
|
||||
|
||||
| Developer Wanted |
|
||||
| 下载持续测试白皮书 |
|
||||
| ------------------------------------------------------------------------------------------------------------ |
|
||||
| 我们正在寻找开发者,欢迎加入我们共同打造更好用、更强大的 MeterSphere。联系我们: [metersphere@fit2cloud.com](mailto:metersphere@fit2cloud.com) |
|
||||
| 《持续测试白皮书 v1.0》由“软件质量报道”公众号和MeterSphere开源项目组共同编写而成。采用贴近企业实践的视角进行组织,编写团队结合自身对软件测试发展历程以及持续测试理念的理解,并积极听取行业内其他专家理念,在此基础上编写而成。全文从持续测试产生的背景和价值、企业如何实践持续测试以及持续测试成熟度模型这三个方面展开,分别介绍了持续测试产生的原因、持续测试落地的关键要素和持续测试成熟度模型的构成、评判等方面内容。下载链接: [https://jinshuju.net/f/KqFUhq](https://jinshuju.net/f/KqFUhq) |
|
||||
|
||||
MeterSphere 是一站式开源持续测试平台,涵盖测试跟踪、接口测试、性能测试、团队协作等功能,兼容JMeter 等开源标准,有效助力开发和测试团队充分利用云弹性进行高度可扩展的自动化测试,加速高质量软件的交付。
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@
|
|||
<dependency>
|
||||
<groupId>org.apache.zookeeper</groupId>
|
||||
<artifactId>zookeeper</artifactId>
|
||||
<version>3.4.13</version>
|
||||
<version>3.4.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.curator</groupId>
|
||||
|
@ -341,6 +341,11 @@
|
|||
<artifactId>jaxen</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.sourceforge.htmlcleaner</groupId>
|
||||
<artifactId>htmlcleaner</artifactId>
|
||||
<version>2.24</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
|
|
|
@ -21,7 +21,7 @@ public class ApiScenarioRequest {
|
|||
private String planId;
|
||||
private boolean recent = false;
|
||||
private List<OrderRequest> orders;
|
||||
private List<String> filters;
|
||||
private Map<String, List<String>> filters;
|
||||
private Map<String, Object> combine;
|
||||
private List<String> ids;
|
||||
private boolean isSelectThisWeedData;
|
||||
|
|
|
@ -45,7 +45,7 @@ public class RunScenarioRequest {
|
|||
*/
|
||||
private boolean isSelectAllDate;
|
||||
|
||||
private List<String> filters;
|
||||
private Map<String, List<String>> filters;
|
||||
|
||||
private String name;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
@ -23,7 +24,7 @@ public class SaveApiPlanRequest {
|
|||
*/
|
||||
private boolean isSelectAllDate;
|
||||
|
||||
private List<String> filters;
|
||||
private Map<String, List<String>> filters;
|
||||
|
||||
private String name;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
|
@ -47,7 +48,7 @@ public class SaveApiScenarioRequest {
|
|||
|
||||
private boolean isSelectAllDate;
|
||||
|
||||
private List<String> filters;
|
||||
private Map<String, List<String>> filters;
|
||||
|
||||
private List<String> moduleIds;
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ public class MsScenario extends MsTestElement {
|
|||
return;
|
||||
}
|
||||
config.setStep(this.name);
|
||||
|
||||
config.setStepType("SCENARIO");
|
||||
config.setEnableCookieShare(enableCookieShare);
|
||||
if (StringUtils.isNotEmpty(environmentId)) {
|
||||
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
|
||||
|
@ -121,6 +121,14 @@ public class MsScenario extends MsTestElement {
|
|||
variables.stream().filter(ScenarioVariable::isConstantValid).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
|
||||
List<ScenarioVariable> variableList = variables.stream().filter(ScenarioVariable::isListValid).collect(Collectors.toList());
|
||||
variableList.forEach(item -> {
|
||||
String[] arrays = item.getValue().split(",");
|
||||
for (int i = 0; i < arrays.length; i++) {
|
||||
arguments.addArgument(item.getName() + "_" + (i + 1), arrays[i], "=");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (config != null && config.getConfig() != null && config.getConfig().getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().getCommonConfig().getVariables())) {
|
||||
|
|
|
@ -17,6 +17,6 @@ public class ParameterConfig {
|
|||
// 步骤
|
||||
private String step;
|
||||
|
||||
private final String stepType = "STEP_GROUP";
|
||||
private String stepType;
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,11 @@ import lombok.Data;
|
|||
import lombok.EqualsAndHashCode;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.control.*;
|
||||
import org.apache.jmeter.control.ForeachController;
|
||||
import org.apache.jmeter.control.LoopController;
|
||||
import org.apache.jmeter.control.RunTime;
|
||||
import org.apache.jmeter.control.WhileController;
|
||||
import org.apache.jmeter.modifiers.CounterConfig;
|
||||
import org.apache.jmeter.reporters.ResultAction;
|
||||
import org.apache.jmeter.save.SaveService;
|
||||
import org.apache.jmeter.testelement.TestElement;
|
||||
|
@ -43,8 +47,21 @@ public class MsLoopController extends MsTestElement {
|
|||
if (!this.isEnable()) {
|
||||
return;
|
||||
}
|
||||
final HashTree groupTree = controller(tree);
|
||||
if (StringUtils.equals(this.loopType, LoopConstants.WHILE.name()) && this.whileController != null) {
|
||||
config.setStep("While 循环");
|
||||
}
|
||||
if (StringUtils.equals(this.loopType, LoopConstants.FOREACH.name()) && this.forEachController != null) {
|
||||
config.setStep("ForEach 循环");
|
||||
}
|
||||
if (StringUtils.equals(this.loopType, LoopConstants.LOOP_COUNT.name()) && this.countController != null) {
|
||||
config.setStep("次数循环");
|
||||
}
|
||||
|
||||
config.setStepType("LOOP");
|
||||
|
||||
final HashTree groupTree = controller(tree);
|
||||
// 循环下都增加一个计数器,用于结果统计
|
||||
groupTree.add(addCounterConfig());
|
||||
// 不打开执行成功后轮询功能,则成功后就停止循环
|
||||
if (StringUtils.equals(this.loopType, LoopConstants.LOOP_COUNT.name()) && this.countController != null && !countController.isProceed()) {
|
||||
ResultAction resultAction = new ResultAction();
|
||||
|
@ -65,6 +82,18 @@ public class MsLoopController extends MsTestElement {
|
|||
}
|
||||
}
|
||||
|
||||
private CounterConfig addCounterConfig() {
|
||||
CounterConfig counterConfig = new CounterConfig();
|
||||
counterConfig.setVarName("LoopCounterConfigXXX");
|
||||
counterConfig.setName("LoopCounterConfigXXX");
|
||||
counterConfig.setEnabled(true);
|
||||
counterConfig.setProperty(TestElement.TEST_CLASS, CounterConfig.class.getName());
|
||||
counterConfig.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("CounterConfigGui"));
|
||||
counterConfig.setStart(1L);
|
||||
counterConfig.setIncrement(1L);
|
||||
return counterConfig;
|
||||
}
|
||||
|
||||
private LoopController loopController() {
|
||||
LoopController loopController = new LoopController();
|
||||
loopController.setEnabled(true);
|
||||
|
|
|
@ -7,4 +7,6 @@ public class CountController {
|
|||
private int loops;
|
||||
private int interval;
|
||||
private boolean proceed;
|
||||
private Object requestResult;
|
||||
|
||||
}
|
||||
|
|
|
@ -7,4 +7,6 @@ public class MsForEachController {
|
|||
private String inputVal;
|
||||
private String returnVal;
|
||||
private String interval;
|
||||
private Object requestResult;
|
||||
|
||||
}
|
||||
|
|
|
@ -8,4 +8,5 @@ public class MsWhileController {
|
|||
private String operator;
|
||||
private String value;
|
||||
private int timeout;
|
||||
private Object requestResult;
|
||||
}
|
||||
|
|
|
@ -40,7 +40,11 @@ public class MsJSR223Processor extends MsTestElement {
|
|||
processor.setName("JSR223Processor");
|
||||
}
|
||||
if (config != null && StringUtils.isNotEmpty(config.getStep())) {
|
||||
processor.setName(this.getName() + "<->" + config.getStep());
|
||||
if ("SCENARIO".equals(config.getStepType())) {
|
||||
processor.setName(this.getName() + "<->" + config.getStep());
|
||||
} else {
|
||||
processor.setName(this.getName() + "<->" + config.getStep() + "-" + "${LoopCounterConfigXXX}");
|
||||
}
|
||||
}
|
||||
processor.setProperty(TestElement.TEST_CLASS, JSR223Sampler.class.getName());
|
||||
processor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
|
|
|
@ -78,7 +78,11 @@ public class MsDubboSampler extends MsTestElement {
|
|||
DubboSample sampler = new DubboSample();
|
||||
sampler.setName(this.getName());
|
||||
if (config != null && StringUtils.isNotEmpty(config.getStep())) {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep());
|
||||
if ("SCENARIO".equals(config.getStepType())) {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep());
|
||||
} else {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep() + "-" + "${LoopCounterConfigXXX}");
|
||||
}
|
||||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, DubboSample.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("DubboSampleGui"));
|
||||
|
|
|
@ -97,7 +97,11 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
sampler.setEnabled(true);
|
||||
sampler.setName(this.getName());
|
||||
if (config != null && StringUtils.isNotEmpty(config.getStep())) {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep());
|
||||
if ("SCENARIO".equals(config.getStepType())) {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep());
|
||||
} else {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep() + "-" + "${LoopCounterConfigXXX}");
|
||||
}
|
||||
}
|
||||
|
||||
sampler.setProperty(TestElement.TEST_CLASS, HTTPSamplerProxy.class.getName());
|
||||
|
|
|
@ -50,6 +50,8 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
private Object requestResult;
|
||||
@JSONField(ordinal = 28)
|
||||
private String dataSourceId;
|
||||
@JSONField(ordinal = 29)
|
||||
private String protocol="SQL";
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
|
||||
|
@ -107,7 +109,11 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
JDBCSampler sampler = new JDBCSampler();
|
||||
sampler.setName(this.getName());
|
||||
if (config != null && StringUtils.isNotEmpty(config.getStep())) {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep());
|
||||
if ("SCENARIO".equals(config.getStepType())) {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep());
|
||||
} else {
|
||||
sampler.setName(this.getName() + "<->" + config.getStep() + "-" + "${LoopCounterConfigXXX}");
|
||||
}
|
||||
}
|
||||
sampler.setProperty(TestElement.TEST_CLASS, JDBCSampler.class.getName());
|
||||
sampler.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||
|
|
|
@ -65,6 +65,8 @@ public class MsTCPSampler extends MsTestElement {
|
|||
private String useEnvironment;
|
||||
@JSONField(ordinal = 37)
|
||||
private MsJSR223PreProcessor tcpPreProcessor;
|
||||
@JSONField(ordinal = 38)
|
||||
private String protocol = "TCP";
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
|
||||
|
@ -99,7 +101,11 @@ public class MsTCPSampler extends MsTestElement {
|
|||
TCPSampler tcpSampler = new TCPSampler();
|
||||
tcpSampler.setName(this.getName());
|
||||
if (config != null && StringUtils.isNotEmpty(config.getStep())) {
|
||||
tcpSampler.setName(this.getName() + "<->" + config.getStep());
|
||||
if ("SCENARIO".equals(config.getStepType())) {
|
||||
tcpSampler.setName(this.getName() + "<->" + config.getStep());
|
||||
} else {
|
||||
tcpSampler.setName(this.getName() + "<->" + config.getStep() + "-" + "${LoopCounterConfigXXX}");
|
||||
}
|
||||
}
|
||||
|
||||
tcpSampler.setProperty(TestElement.TEST_CLASS, TCPSampler.class.getName());
|
||||
|
@ -128,10 +134,12 @@ public class MsTCPSampler extends MsTestElement {
|
|||
userParameters.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("UserParametersGui"));
|
||||
List<StringProperty> names = new ArrayList<>();
|
||||
List<StringProperty> threadValues = new ArrayList<>();
|
||||
this.parameters.forEach(item -> {
|
||||
names.add(new StringProperty(new Integer(new Random().nextInt(1000000)).toString(), item.getName()));
|
||||
threadValues.add(new StringProperty(new Integer(new Random().nextInt(1000000)).toString(), item.getValue()));
|
||||
});
|
||||
if (CollectionUtils.isNotEmpty(this.parameters)) {
|
||||
this.parameters.forEach(item -> {
|
||||
names.add(new StringProperty(new Integer(new Random().nextInt(1000000)).toString(), item.getName()));
|
||||
threadValues.add(new StringProperty(new Integer(new Random().nextInt(1000000)).toString(), item.getValue()));
|
||||
});
|
||||
}
|
||||
userParameters.setNames(new CollectionProperty(UserParameters.NAMES, names));
|
||||
List<CollectionProperty> collectionPropertyList = new ArrayList<>();
|
||||
collectionPropertyList.add(new CollectionProperty(new Integer(new Random().nextInt(1000000)).toString(), threadValues));
|
||||
|
|
|
@ -41,8 +41,7 @@ public class ScenarioVariable {
|
|||
private String maxNumber;
|
||||
|
||||
public boolean isConstantValid() {
|
||||
if ((StringUtils.equals(this.type, VariableTypeConstants.CONSTANT.name())
|
||||
|| StringUtils.equals(this.type, VariableTypeConstants.LIST.name())) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
|
||||
if (StringUtils.equals(this.type, VariableTypeConstants.CONSTANT.name()) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -55,6 +54,13 @@ public class ScenarioVariable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isListValid() {
|
||||
if (StringUtils.equals(this.type, VariableTypeConstants.LIST.name()) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value) && value.indexOf(",") != -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCounterValid() {
|
||||
if (StringUtils.equals(this.type, VariableTypeConstants.COUNTER.name()) && StringUtils.isNotEmpty(name)) {
|
||||
return true;
|
||||
|
|
|
@ -126,6 +126,34 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
|
|||
}
|
||||
|
||||
protected ApiDefinitionResult buildApiDefinition(String id, String name, String path, String method,ApiTestImportRequest importRequest) {
|
||||
protected void addBodyHeader(MsHTTPSamplerProxy request) {
|
||||
String contentType = "";
|
||||
if (request.getBody() != null && StringUtils.isNotBlank(request.getBody().getType())) {
|
||||
switch (request.getBody().getType()) {
|
||||
case Body.WWW_FROM:
|
||||
contentType = "application/x-www-form-urlencoded";
|
||||
break;
|
||||
case Body.JSON:
|
||||
contentType = "application/json";
|
||||
break;
|
||||
case Body.XML:
|
||||
contentType = "application/xml";
|
||||
break;
|
||||
case Body.BINARY:
|
||||
contentType = "application/octet-stream";
|
||||
break;
|
||||
}
|
||||
List<KeyValue> headers = request.getHeaders();
|
||||
if (headers == null) {
|
||||
headers = new ArrayList<>();
|
||||
request.setHeaders(headers);
|
||||
}
|
||||
addContentType(request.getHeaders(), contentType);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected ApiDefinitionResult buildApiDefinition(String id, String name, String path, String method) {
|
||||
ApiDefinitionResult apiDefinition = new ApiDefinitionResult();
|
||||
apiDefinition.setName(name);
|
||||
apiDefinition.setPath(formatPath(path));
|
||||
|
@ -172,8 +200,8 @@ public abstract class ApiImportAbstractParser implements ApiImportParser {
|
|||
return request;
|
||||
}
|
||||
|
||||
protected void addContentType(HttpRequest request, String contentType) {
|
||||
// addHeader(request, "Content-Type", contentType);
|
||||
protected void addContentType(List<KeyValue> headers, String contentType) {
|
||||
addHeader(headers, "Content-Type", contentType);
|
||||
}
|
||||
|
||||
protected void addCookie(List<KeyValue> headers, String key, String value) {
|
||||
|
|
|
@ -74,6 +74,7 @@ public class PostmanParser extends ApiImportAbstractParser {
|
|||
parseBody(request.getBody(), requestDesc);
|
||||
request.setArguments(parseKeyValue(url.getQuery()));
|
||||
request.setHeaders(parseKeyValue(requestDesc.getHeader()));
|
||||
addBodyHeader(request);
|
||||
apiDefinition.setRequest(JSON.toJSONString(request));
|
||||
return apiDefinition;
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ public class Swagger2Parser extends SwaggerAbstractParser {
|
|||
MsHTTPSamplerProxy request = buildRequest(operation, pathName, method.name());
|
||||
ApiDefinitionResult apiDefinition = buildApiDefinition(request.getId(), operation, pathName, method.name(),importRequest);
|
||||
parseParameters(operation, request);
|
||||
addBodyHeader(request);
|
||||
apiDefinition.setRequest(JSON.toJSONString(request));
|
||||
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation, operation.getResponses())));
|
||||
buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved());
|
||||
|
@ -275,7 +276,6 @@ public class Swagger2Parser extends SwaggerAbstractParser {
|
|||
} else {
|
||||
propertyList.add(new JSONObject());
|
||||
}
|
||||
|
||||
jsonObject.put(key, propertyList);
|
||||
} else {
|
||||
jsonObject.put(key, new ArrayList<>());
|
||||
|
|
|
@ -100,6 +100,7 @@ public class Swagger3Parser extends SwaggerAbstractParser {
|
|||
ApiDefinitionResult apiDefinition = buildApiDefinition(request.getId(), operation, pathName, method,importRequest);
|
||||
parseParameters(operation, request);
|
||||
parseRequestBody(operation.getRequestBody(), request.getBody());
|
||||
addBodyHeader(request);
|
||||
apiDefinition.setRequest(JSON.toJSONString(request));
|
||||
apiDefinition.setResponse(JSON.toJSONString(parseResponse(operation.getResponses())));
|
||||
buildModule(parentNode, apiDefinition, operation.getTags(), importRequest.isSaved());
|
||||
|
|
|
@ -464,7 +464,7 @@ public class ApiAutomationService {
|
|||
* @param unSelectIds 未勾选ID_前台没有勾选的ID
|
||||
* @return
|
||||
*/
|
||||
private List<String> getAllScenarioIdsByFontedSelect(List<String> moduleIds, String name, String projectId, List<String> filters, List<String> unSelectIds) {
|
||||
private List<String> getAllScenarioIdsByFontedSelect(List<String> moduleIds, String name, String projectId, Map<String, List<String>> filters, List<String> unSelectIds) {
|
||||
ApiScenarioRequest selectRequest = new ApiScenarioRequest();
|
||||
selectRequest.setModuleIds(moduleIds);
|
||||
selectRequest.setName(name);
|
||||
|
|
|
@ -65,6 +65,15 @@ public class HistoricalDataUpgradeService {
|
|||
}
|
||||
}
|
||||
|
||||
private MsScenario createScenarioByTest(ApiTest test) {
|
||||
MsScenario scenario = new MsScenario();
|
||||
scenario.setName(test.getName());
|
||||
scenario.setReferenced("Upgrade");
|
||||
scenario.setResourceId(UUID.randomUUID().toString());
|
||||
scenario.setId(test.getId());
|
||||
return scenario;
|
||||
}
|
||||
|
||||
private MsScenario createScenario(Scenario oldScenario) {
|
||||
MsScenario scenario = new MsScenario();
|
||||
scenario.setOldVariables(oldScenario.getVariables());
|
||||
|
@ -147,6 +156,9 @@ public class HistoricalDataUpgradeService {
|
|||
BeanUtils.copyBean(element, request1);
|
||||
((MsHTTPSamplerProxy) element).setProtocol(RequestType.HTTP);
|
||||
((MsHTTPSamplerProxy) element).setArguments(request1.getParameters());
|
||||
List<KeyValue> keyValues = new LinkedList<>();
|
||||
keyValues.add(new KeyValue("", ""));
|
||||
((MsHTTPSamplerProxy) element).setRest(keyValues);
|
||||
if (StringUtils.isEmpty(element.getName())) {
|
||||
element.setName(request1.getPath());
|
||||
}
|
||||
|
@ -185,6 +197,9 @@ public class HistoricalDataUpgradeService {
|
|||
CollectionUtils.isNotEmpty(request.getAssertions().getRegex()) || CollectionUtils.isNotEmpty(request.getAssertions().getXpath2()))) {
|
||||
String assertions = JSON.toJSONString(request.getAssertions());
|
||||
MsAssertions msAssertions = JSON.parseObject(assertions, MsAssertions.class);
|
||||
if (StringUtils.isEmpty(msAssertions.getName())) {
|
||||
msAssertions.setName("Assertions");
|
||||
}
|
||||
msAssertions.setType("Assertions");
|
||||
msAssertions.setIndex(index + "");
|
||||
msAssertions.setResourceId(UUID.randomUUID().toString());
|
||||
|
@ -196,6 +211,9 @@ public class HistoricalDataUpgradeService {
|
|||
CollectionUtils.isNotEmpty(request.getExtract().getRegex()) || CollectionUtils.isNotEmpty(request.getExtract().getXpath()))) {
|
||||
String extractJson = JSON.toJSONString(request.getExtract());
|
||||
MsExtract extract = JSON.parseObject(extractJson, MsExtract.class);
|
||||
if (StringUtils.isEmpty(extract.getName())) {
|
||||
extract.setName("Extract");
|
||||
}
|
||||
extract.setType("Extract");
|
||||
extract.setIndex(index + "");
|
||||
extract.setHashTree(new LinkedList<>());
|
||||
|
@ -206,6 +224,9 @@ public class HistoricalDataUpgradeService {
|
|||
if (request.getJsr223PreProcessor() != null && StringUtils.isNotEmpty(request.getJsr223PreProcessor().getScript())) {
|
||||
String preJson = JSON.toJSONString(request.getJsr223PreProcessor());
|
||||
MsJSR223PreProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PreProcessor.class);
|
||||
if (StringUtils.isEmpty(preProcessor.getName())) {
|
||||
preProcessor.setName("JSR223PreProcessor");
|
||||
}
|
||||
preProcessor.setType("JSR223PreProcessor");
|
||||
preProcessor.setIndex(index + "");
|
||||
preProcessor.setHashTree(new LinkedList<>());
|
||||
|
@ -216,6 +237,9 @@ public class HistoricalDataUpgradeService {
|
|||
if (request.getJsr223PostProcessor() != null && StringUtils.isNotEmpty(request.getJsr223PostProcessor().getScript())) {
|
||||
String preJson = JSON.toJSONString(request.getJsr223PostProcessor());
|
||||
MsJSR223PostProcessor preProcessor = JSON.parseObject(preJson, MsJSR223PostProcessor.class);
|
||||
if (StringUtils.isEmpty(preProcessor.getName())) {
|
||||
preProcessor.setName("JSR223PostProcessor");
|
||||
}
|
||||
preProcessor.setType("JSR223PostProcessor");
|
||||
preProcessor.setIndex(index + "");
|
||||
preProcessor.setHashTree(new LinkedList<>());
|
||||
|
@ -239,9 +263,9 @@ public class HistoricalDataUpgradeService {
|
|||
return scenario;
|
||||
}
|
||||
|
||||
private ApiScenarioWithBLOBs checkNameExist(Scenario oldScenario, String projectId, ApiScenarioMapper mapper) {
|
||||
private ApiScenarioWithBLOBs getScenario(String oldScenarioId, ApiScenarioMapper mapper) {
|
||||
ApiScenarioExample example = new ApiScenarioExample();
|
||||
example.createCriteria().andIdEqualTo(oldScenario.getId());
|
||||
example.createCriteria().andIdEqualTo(oldScenarioId);
|
||||
List<ApiScenarioWithBLOBs> list = mapper.selectByExampleWithBLOBs(example);
|
||||
if (list.size() > 0) {
|
||||
return list.get(0);
|
||||
|
@ -321,20 +345,20 @@ public class HistoricalDataUpgradeService {
|
|||
copyDir(dir, BODY_FILE_DIR);
|
||||
}
|
||||
|
||||
private void createApiScenarioWithBLOBs(SaveHistoricalDataUpgrade saveHistoricalDataUpgrade, Scenario oldScenario, String scenarioDefinition, ApiScenarioMapper mapper, int num) {
|
||||
if (StringUtils.isEmpty(oldScenario.getName())) {
|
||||
oldScenario.setName("默认名称-" + DateUtils.getTimeStr(System.currentTimeMillis()));
|
||||
private void createApiScenarioWithBLOBs(SaveHistoricalDataUpgrade saveHistoricalDataUpgrade, String id, String name, int total, String scenarioDefinition, ApiScenarioMapper mapper, int num) {
|
||||
if (StringUtils.isEmpty(name)) {
|
||||
name = "默认名称-" + DateUtils.getTimeStr(System.currentTimeMillis());
|
||||
}
|
||||
ApiScenarioWithBLOBs scenario = checkNameExist(oldScenario, saveHistoricalDataUpgrade.getProjectId(), mapper);
|
||||
ApiScenarioWithBLOBs scenario = getScenario(id, mapper);
|
||||
if (scenario != null) {
|
||||
scenario.setName(oldScenario.getName());
|
||||
scenario.setName(name);
|
||||
scenario.setProjectId(saveHistoricalDataUpgrade.getProjectId());
|
||||
scenario.setTags(scenario.getTags());
|
||||
scenario.setLevel("P0");
|
||||
scenario.setModulePath(saveHistoricalDataUpgrade.getModulePath());
|
||||
scenario.setApiScenarioModuleId(saveHistoricalDataUpgrade.getModuleId());
|
||||
scenario.setPrincipal(Objects.requireNonNull(SessionUtils.getUser()).getId());
|
||||
scenario.setStepTotal(oldScenario.getRequests().size());
|
||||
scenario.setStepTotal(total);
|
||||
scenario.setScenarioDefinition(scenarioDefinition);
|
||||
scenario.setUpdateTime(System.currentTimeMillis());
|
||||
scenario.setStatus(ScenarioStatus.Underway.name());
|
||||
|
@ -342,15 +366,15 @@ public class HistoricalDataUpgradeService {
|
|||
mapper.updateByPrimaryKeySelective(scenario);
|
||||
} else {
|
||||
scenario = new ApiScenarioWithBLOBs();
|
||||
scenario.setId(oldScenario.getId());
|
||||
scenario.setName(oldScenario.getName());
|
||||
scenario.setId(id);
|
||||
scenario.setName(name);
|
||||
scenario.setProjectId(saveHistoricalDataUpgrade.getProjectId());
|
||||
scenario.setTags(scenario.getTags());
|
||||
scenario.setLevel("P0");
|
||||
scenario.setModulePath(saveHistoricalDataUpgrade.getModulePath());
|
||||
scenario.setApiScenarioModuleId(saveHistoricalDataUpgrade.getModuleId());
|
||||
scenario.setPrincipal(Objects.requireNonNull(SessionUtils.getUser()).getId());
|
||||
scenario.setStepTotal(oldScenario.getRequests().size());
|
||||
scenario.setStepTotal(total);
|
||||
scenario.setScenarioDefinition(scenarioDefinition);
|
||||
scenario.setCreateTime(System.currentTimeMillis());
|
||||
scenario.setUpdateTime(System.currentTimeMillis());
|
||||
|
@ -374,7 +398,9 @@ public class HistoricalDataUpgradeService {
|
|||
for (ApiTest test : blobs) {
|
||||
// 附件迁移
|
||||
createBodyFiles(test.getId());
|
||||
|
||||
// 把test 生成一个场景,旧场景数据变成引用步骤
|
||||
MsScenario scenarioTest = createScenarioByTest(test);
|
||||
LinkedList<MsTestElement> listSteps = new LinkedList<>();
|
||||
List<Scenario> scenarios = JSON.parseArray(test.getScenarioDefinition(), Scenario.class);
|
||||
if (CollectionUtils.isNotEmpty(scenarios)) {
|
||||
// 批量处理
|
||||
|
@ -382,9 +408,20 @@ public class HistoricalDataUpgradeService {
|
|||
MsScenario scenario1 = createScenario(scenario);
|
||||
String scenarioDefinition = JSON.toJSONString(scenario1);
|
||||
num++;
|
||||
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario, scenarioDefinition, mapper, num);
|
||||
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario.getId(), scenario.getName(), scenario.getRequests().size(), scenarioDefinition, mapper, num);
|
||||
MsScenario step = new MsScenario();
|
||||
step.setId(scenario1.getId());
|
||||
step.setName(scenario1.getName());
|
||||
step.setType("scenario");
|
||||
step.setResourceId(UUID.randomUUID().toString());
|
||||
step.setReferenced("REF");
|
||||
listSteps.add(step);
|
||||
}
|
||||
}
|
||||
num++;
|
||||
scenarioTest.setHashTree(listSteps);
|
||||
String scenarioDefinition = JSON.toJSONString(scenarioTest);
|
||||
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenarioTest.getId(), scenarioTest.getName(), listSteps.size(), scenarioDefinition, mapper, num);
|
||||
}
|
||||
sqlSession.flushStatements();
|
||||
return null;
|
||||
|
|
|
@ -150,9 +150,35 @@
|
|||
</foreach>
|
||||
</if>
|
||||
<if test="request.filters != null and request.filters.size() > 0">
|
||||
and api_scenario.status in
|
||||
<foreach collection="request.filters" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
<foreach collection="request.filters.entrySet()" index="key" item="values">
|
||||
<if test="values != null and values.size() > 0">
|
||||
<choose>
|
||||
<when test="key=='status'">
|
||||
and api_scenario.status in
|
||||
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
</foreach>
|
||||
</when>
|
||||
<when test="key=='user_id'">
|
||||
and api_scenario.user_id in
|
||||
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
</foreach>
|
||||
</when>
|
||||
<when test="key=='level'">
|
||||
and api_scenario.level in
|
||||
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
</foreach>
|
||||
</when>
|
||||
<when test="key=='last_result'">
|
||||
and api_scenario.last_result in
|
||||
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
</foreach>
|
||||
</when>
|
||||
</choose>
|
||||
</if>
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="request.executeStatus == 'unExecute'">
|
||||
|
|
|
@ -11,4 +11,5 @@ public interface ExtTestPlanLoadCaseMapper {
|
|||
List<String> selectIdsNotInPlan(@Param("projectId") String projectId, @Param("planId") String planId);
|
||||
List<TestPlanLoadCaseDTO> selectTestPlanLoadCaseList(@Param("request") LoadCaseRequest request);
|
||||
void updateCaseStatus(@Param("reportId") String reportId, @Param("status") String status);
|
||||
List<String> getStatusByTestPlanId(@Param("planId") String planId);
|
||||
}
|
||||
|
|
|
@ -40,6 +40,29 @@
|
|||
<if test="request.name != null">
|
||||
and (lt.name like CONCAT('%', #{request.name},'%') or lt.num like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.filters != null and request.filters.size() > 0">
|
||||
<foreach collection="request.filters.entrySet()" index="key" item="values">
|
||||
<if test="values != null and values.size() > 0">
|
||||
<choose>
|
||||
<when test="key=='status'">
|
||||
and lt.status in
|
||||
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
</foreach>
|
||||
</when>
|
||||
</choose>
|
||||
</if>
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
<if test="request.orders != null and request.orders.size() > 0">
|
||||
order by
|
||||
<foreach collection="request.orders" separator="," item="order">
|
||||
tplc.${order.name} ${order.type}
|
||||
</foreach>
|
||||
</if>
|
||||
</select>
|
||||
<select id="getStatusByTestPlanId" resultType="java.lang.String">
|
||||
select status from test_plan_load_case tplc where tplc.test_plan_id = #{planId}
|
||||
</select>
|
||||
</mapper>
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.base.mapper.ext;
|
||||
|
||||
import io.metersphere.base.domain.TestPlan;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTOWithMetric;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
|
@ -32,4 +33,6 @@ public interface ExtTestPlanMapper {
|
|||
String findScheduleCreateUserById(String testPlanId);
|
||||
|
||||
List<String> findIdByPerformanceReportId(String reportId);
|
||||
|
||||
List<TestPlan> listRecent(@Param("userId") String userId, @Param("projectId") String currentProjectId);
|
||||
}
|
||||
|
|
|
@ -240,5 +240,16 @@
|
|||
WHERE reportData.performance_info like CONCAT('%', #{0},'%')
|
||||
AND report.is_performance_executing = true;
|
||||
</select>
|
||||
<select id="listRecent" resultType="io.metersphere.base.domain.TestPlan">
|
||||
select distinct test_plan.*
|
||||
from test_plan
|
||||
<where>
|
||||
<if test="projectId != null">
|
||||
and test_plan.project_id = #{projectId}
|
||||
</if>
|
||||
and (test_plan.creator = #{userId} or test_plan.principal = #{userId})
|
||||
</where>
|
||||
order by test_plan.update_time desc
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
<select id="list" resultType="io.metersphere.track.dto.TestPlanReportDTO"
|
||||
parameterType="io.metersphere.track.request.report.QueryTestPlanReportRequest">
|
||||
SELECT tpr.id AS id, tpr.`name` AS `name`, tp.`name` AS testPlanName, tpr.creator AS creator, tpr.create_time AS createTime,tpr.trigger_Mode AS triggerMode
|
||||
SELECT tpr.id AS id, tpr.`name` AS `name`, tp.`name` AS testPlanName, u.name AS creator, tpr.create_time AS createTime,tpr.trigger_Mode AS triggerMode,tpr.status AS status
|
||||
FROM test_plan tp
|
||||
INNER JOIN test_plan_report tpr on tp.id = tpr.test_plan_id
|
||||
INNER JOIN user u on tpr.creator = u.id
|
||||
<where>
|
||||
<if test="name != null">
|
||||
and tpr.name like CONCAT('%', #{name},'%')
|
||||
|
|
|
@ -27,6 +27,7 @@ public class ShiroUtils {
|
|||
filterChainDefinitionMap.put("/jmeter/download/**", "anon");
|
||||
filterChainDefinitionMap.put("/authsource/list/allenable", "anon");
|
||||
filterChainDefinitionMap.put("/sso/signin", "anon");
|
||||
filterChainDefinitionMap.put("/sso/callback", "anon");
|
||||
// for swagger
|
||||
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
|
||||
filterChainDefinitionMap.put("/swagger-ui/**", "anon");
|
||||
|
|
|
@ -66,9 +66,8 @@ public class TestPlanController {
|
|||
|
||||
@GetMapping("recent/{count}")
|
||||
public List<TestPlan> recentTestPlans(@PathVariable int count) {
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
PageHelper.startPage(1, count, true);
|
||||
return testPlanService.recentTestPlans(currentWorkspaceId);
|
||||
return testPlanService.recentTestPlans();
|
||||
}
|
||||
|
||||
@PostMapping("/get/{testPlanId}")
|
||||
|
|
|
@ -54,6 +54,12 @@ public class TestPlanReportController {
|
|||
testPlanReportService.delete(testPlanReportIdList);
|
||||
}
|
||||
|
||||
@PostMapping("/deleteBatchByParams")
|
||||
public void deleteBatchByParams(@RequestBody QueryTestPlanReportRequest request) {
|
||||
testPlanReportService.delete(request);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/apiExecuteFinish/{planId}/{userId}")
|
||||
public void apiExecuteFinish(@PathVariable String planId,@PathVariable String userId) {
|
||||
TestPlanReport report = testPlanReportService.genTestPlanReport(planId,userId,ReportTriggerMode.API.name());
|
||||
|
|
|
@ -20,6 +20,7 @@ public class TestPlanReportDTO {
|
|||
private String creator;
|
||||
private long createTime;
|
||||
private String triggerMode;
|
||||
private String status;
|
||||
|
||||
private TestCaseReportAdvanceStatusResultDTO executeResult;
|
||||
private List<TestCaseReportModuleResultDTO> moduleExecuteResult;
|
||||
|
|
|
@ -25,5 +25,11 @@ public class QueryTestPlanReportRequest {
|
|||
|
||||
private List<OrderRequest> orders;
|
||||
private Map<String, List<String>> filters;
|
||||
// private Map<String, Object> combine;
|
||||
|
||||
/**
|
||||
* 批量操作的参数,用于判断是前台表格的当前页数据还是全库数据
|
||||
*/
|
||||
private boolean isSelectAllDate;
|
||||
private List<String> unSelectIds;
|
||||
private List<String> dataIds;
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package io.metersphere.track.request.testplan;
|
||||
|
||||
import io.metersphere.base.domain.TestPlanLoadCase;
|
||||
import io.metersphere.controller.request.OrderRequest;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
|
@ -12,4 +14,6 @@ public class LoadCaseRequest extends TestPlanLoadCase {
|
|||
private String projectId;
|
||||
private List<String> caseIds;
|
||||
private String name;
|
||||
private Map<String, List<String>> filters;
|
||||
private List<OrderRequest> orders;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,8 @@ public class LoadReportStatusEvent implements LoadTestFinishEvent {
|
|||
|
||||
@Override
|
||||
public void execute(LoadTestReport loadTestReport) {
|
||||
if (StringUtils.equals(ReportTriggerMode.CASE.name(), loadTestReport.getTriggerMode())) {
|
||||
if (StringUtils.equals(ReportTriggerMode.CASE.name(), loadTestReport.getTriggerMode())
|
||||
|| StringUtils.equals(ReportTriggerMode.TEST_PLAN_SCHEDULE.name(), loadTestReport.getTriggerMode())) {
|
||||
if (StringUtils.equalsAny(loadTestReport.getStatus(),
|
||||
PerformanceTestStatus.Completed.name(), PerformanceTestStatus.Error.name())) {
|
||||
updateLoadCaseStatus(loadTestReport);
|
||||
|
|
|
@ -5,6 +5,7 @@ import io.metersphere.base.mapper.LoadTestMapper;
|
|||
import io.metersphere.base.mapper.LoadTestReportMapper;
|
||||
import io.metersphere.base.mapper.TestPlanLoadCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper;
|
||||
import io.metersphere.controller.request.OrderRequest;
|
||||
import io.metersphere.performance.service.PerformanceTestService;
|
||||
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
|
||||
import io.metersphere.track.request.testplan.LoadCaseReportRequest;
|
||||
|
@ -50,6 +51,15 @@ public class TestPlanLoadCaseService {
|
|||
}
|
||||
|
||||
public List<TestPlanLoadCaseDTO> list(LoadCaseRequest request) {
|
||||
List<OrderRequest> orders = request.getOrders();
|
||||
if (orders == null || orders.size() < 1) {
|
||||
OrderRequest orderRequest = new OrderRequest();
|
||||
orderRequest.setName("create_time");
|
||||
orderRequest.setType("desc");
|
||||
orders = new ArrayList<>();
|
||||
orders.add(orderRequest);
|
||||
}
|
||||
request.setOrders(orders);
|
||||
return extTestPlanLoadCaseMapper.selectTestPlanLoadCaseList(request);
|
||||
}
|
||||
|
||||
|
@ -129,4 +139,8 @@ public class TestPlanLoadCaseService {
|
|||
testPlanLoadCaseMapper.updateByPrimaryKeySelective(testPlanLoadCase);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getStatus(String planId) {
|
||||
return extTestPlanLoadCaseMapper.getStatusByTestPlanId(planId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@ package io.metersphere.track.service;
|
|||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
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.mapper.*;
|
||||
|
@ -109,7 +111,6 @@ public class TestPlanReportService {
|
|||
TestPlanReport testPlanReport = new TestPlanReport();
|
||||
testPlanReport.setTestPlanId(planId);
|
||||
testPlanReport.setId(testPlanReportID);
|
||||
testPlanReport.setStatus(APITestStatus.Starting.name());
|
||||
testPlanReport.setCreateTime(System.currentTimeMillis());
|
||||
testPlanReport.setUpdateTime(System.currentTimeMillis());
|
||||
try {
|
||||
|
@ -136,6 +137,12 @@ public class TestPlanReportService {
|
|||
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();
|
||||
|
@ -327,6 +334,7 @@ public class TestPlanReportService {
|
|||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}else {
|
||||
}
|
||||
testPlanReportMapper.updateByPrimaryKey(report);
|
||||
}
|
||||
|
@ -407,4 +415,33 @@ public class TestPlanReportService {
|
|||
testPlanReportDataMapper.deleteByExample(example);
|
||||
}
|
||||
}
|
||||
|
||||
public void delete(QueryTestPlanReportRequest request) {
|
||||
List<String> deleteReportIds = request.getDataIds();
|
||||
if (request.isSelectAllDate()) {
|
||||
deleteReportIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getProjectId(), request.getUnSelectIds());
|
||||
}
|
||||
TestPlanReportExample deleteReportExample = new TestPlanReportExample();
|
||||
deleteReportExample.createCriteria().andIdIn(deleteReportIds);
|
||||
testPlanReportMapper.deleteByExample(deleteReportExample);
|
||||
|
||||
TestPlanReportDataExample example = new TestPlanReportDataExample();
|
||||
example.createCriteria().andTestPlanReportIdIn(deleteReportIds);
|
||||
testPlanReportDataMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
private List<String> getAllApiIdsByFontedSelect(Map<String, List<String>> filters, String name, String projectId, List<String> unSelectIds) {
|
||||
QueryTestPlanReportRequest request = new QueryTestPlanReportRequest();
|
||||
request.setFilters(filters);
|
||||
request.setName(name);
|
||||
request.setProjectId(projectId);
|
||||
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
List<TestPlanReportDTO> resList = extTestPlanReportMapper.list(request);
|
||||
List<String> ids = new ArrayList<>(0);
|
||||
if (!resList.isEmpty()) {
|
||||
List<String> allIds = resList.stream().map(TestPlanReportDTO::getId).collect(Collectors.toList());
|
||||
ids = allIds.stream().filter(id -> !unSelectIds.contains(id)).collect(Collectors.toList());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,7 @@ import io.metersphere.api.jmeter.JMeterService;
|
|||
import io.metersphere.api.service.ApiAutomationService;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.*;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
|
@ -103,6 +100,12 @@ public class TestPlanService {
|
|||
private JMeterService jMeterService;
|
||||
@Resource
|
||||
private ApiAutomationService apiAutomationService;
|
||||
@Resource
|
||||
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
|
||||
@Resource
|
||||
private ExtTestPlanLoadCaseMapper extTestPlanLoadCaseMapper;
|
||||
@Resource
|
||||
private ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
|
||||
|
||||
public synchronized void addTestPlan(AddTestPlanRequest testPlan) {
|
||||
if (getTestPlanByName(testPlan.getName()).size() > 0) {
|
||||
|
@ -379,7 +382,17 @@ public class TestPlanService {
|
|||
}
|
||||
});
|
||||
|
||||
testPlan.setTotal(apiExecResults.size() + scenarioExecResults.size() + functionalExecResults.size());
|
||||
List<String> loadResults = testPlanLoadCaseService.getStatus(testPlan.getId());
|
||||
loadResults.forEach(item -> {
|
||||
if (StringUtils.isNotBlank(item)) {
|
||||
testPlan.setTested(testPlan.getTested() + 1);
|
||||
if (StringUtils.equals(item, "success")) {
|
||||
testPlan.setPassed(testPlan.getPassed() + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
testPlan.setTotal(apiExecResults.size() + scenarioExecResults.size() + functionalExecResults.size() + loadResults.size());
|
||||
|
||||
testPlan.setPassRate(MathUtils.getPercentWithDecimal(testPlan.getTested() == 0 ? 0 : testPlan.getPassed() * 1.0 / testPlan.getTested()));
|
||||
testPlan.setTestRate(MathUtils.getPercentWithDecimal(testPlan.getTotal() == 0 ? 0 : testPlan.getTested() * 1.0 / testPlan.getTotal()));
|
||||
|
@ -454,26 +467,8 @@ public class TestPlanService {
|
|||
}
|
||||
}
|
||||
|
||||
public List<TestPlan> recentTestPlans(String currentWorkspaceId) {
|
||||
if (StringUtils.isBlank(currentWorkspaceId)) {
|
||||
return null;
|
||||
}
|
||||
if (StringUtils.isNotBlank(SessionUtils.getCurrentProjectId())) {
|
||||
TestPlanExample testPlanExample = new TestPlanExample();
|
||||
TestPlanExample.Criteria criteria = testPlanExample.createCriteria();
|
||||
criteria.andProjectIdEqualTo(SessionUtils.getCurrentProjectId());
|
||||
List<TestPlan> testPlans = testPlanMapper.selectByExample(testPlanExample);
|
||||
if (!CollectionUtils.isEmpty(testPlans)) {
|
||||
List<String> testPlanIds = testPlans.stream().map(TestPlan::getId).collect(Collectors.toList());
|
||||
TestPlanExample testPlanTestCaseExample = new TestPlanExample();
|
||||
testPlanTestCaseExample.createCriteria().andWorkspaceIdEqualTo(currentWorkspaceId)
|
||||
.andIdIn(testPlanIds)
|
||||
.andPrincipalEqualTo(SessionUtils.getUserId());
|
||||
testPlanTestCaseExample.setOrderByClause("update_time desc");
|
||||
return testPlanMapper.selectByExample(testPlanTestCaseExample);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>();
|
||||
public List<TestPlan> recentTestPlans() {
|
||||
return extTestPlanMapper.listRecent(SessionUtils.getUserId(), SessionUtils.getCurrentProjectId());
|
||||
}
|
||||
|
||||
public List<TestPlan> listTestAllPlan(String currentWorkspaceId) {
|
||||
|
@ -550,18 +545,10 @@ public class TestPlanService {
|
|||
}
|
||||
|
||||
public void editTestPlanStatus(String planId) {
|
||||
List<String> statusList = extTestPlanTestCaseMapper.getStatusByPlanId(planId);
|
||||
TestPlan testPlan = new TestPlan();
|
||||
testPlan.setId(planId);
|
||||
for (String status : statusList) {
|
||||
if (StringUtils.equals(status, TestPlanTestCaseStatus.Prepare.name())
|
||||
|| StringUtils.equals(status, TestPlanTestCaseStatus.Underway.name())) {
|
||||
testPlan.setStatus(TestPlanStatus.Underway.name());
|
||||
testPlanMapper.updateByPrimaryKeySelective(testPlan);
|
||||
return;
|
||||
}
|
||||
}
|
||||
testPlan.setStatus(TestPlanStatus.Completed.name());
|
||||
String status = calcTestPlanStatus(planId);
|
||||
testPlan.setStatus(status);
|
||||
testPlanMapper.updateByPrimaryKeySelective(testPlan);
|
||||
TestPlan testPlans = getTestPlan(planId);
|
||||
List<String> userIds = new ArrayList<>();
|
||||
|
@ -590,6 +577,44 @@ public class TestPlanService {
|
|||
|
||||
}
|
||||
|
||||
|
||||
private String calcTestPlanStatus(String planId) {
|
||||
// test-plan-functional-case status
|
||||
List<String> funcStatusList = extTestPlanTestCaseMapper.getStatusByPlanId(planId);
|
||||
for (String funcStatus : funcStatusList) {
|
||||
if (StringUtils.equals(funcStatus, TestPlanTestCaseStatus.Prepare.name())
|
||||
|| StringUtils.equals(funcStatus, TestPlanTestCaseStatus.Underway.name())) {
|
||||
return TestPlanStatus.Underway.name();
|
||||
}
|
||||
}
|
||||
|
||||
// test-plan-api-case status
|
||||
List<String> apiStatusList = extTestPlanApiCaseMapper.getStatusByTestPlanId(planId);
|
||||
for (String apiStatus : apiStatusList) {
|
||||
if (apiStatus == null) {
|
||||
return TestPlanStatus.Underway.name();
|
||||
}
|
||||
}
|
||||
|
||||
// test-plan-scenario-case status
|
||||
List<String> scenarioStatusList = extTestPlanScenarioCaseMapper.getExecResultByPlanId(planId);
|
||||
for (String scenarioStatus : scenarioStatusList) {
|
||||
if (scenarioStatus == null) {
|
||||
return TestPlanStatus.Underway.name();
|
||||
}
|
||||
}
|
||||
|
||||
// test-plan-load-case status
|
||||
List<String> loadStatusList = extTestPlanLoadCaseMapper.getStatusByTestPlanId(planId);
|
||||
for (String loadStatus : loadStatusList) {
|
||||
if (loadStatus == null) {
|
||||
return TestPlanStatus.Underway.name();
|
||||
}
|
||||
}
|
||||
|
||||
return TestPlanStatus.Completed.name();
|
||||
}
|
||||
|
||||
public String getProjectNameByPlanId(String testPlanId) {
|
||||
List<String> projectIds = testPlanProjectService.getProjectIdsByPlanId(testPlanId);
|
||||
ProjectExample projectExample = new ProjectExample();
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 8d175b5363274672ff33a5883b730e8aaa823f8d
|
||||
Subproject commit e4521190f0f1be113c8b84fbdcdf8ff273bf274e
|
|
@ -110,7 +110,6 @@
|
|||
if (this.isNotRunning) {
|
||||
try {
|
||||
this.content = JSON.parse(this.report.content);
|
||||
console.log(this.content)
|
||||
if (!this.content) {
|
||||
this.content = {scenarios: []};
|
||||
}
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
:show-create="false"/>
|
||||
</template>
|
||||
|
||||
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table ms-select-all" @select-all="select" @select="select"
|
||||
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table ms-select-all"
|
||||
@sort-change="sort"
|
||||
@filter-change="filter"
|
||||
@select-all="select" @select="select"
|
||||
v-loading="loading">
|
||||
|
||||
<el-table-column type="selection" width="50"/>
|
||||
|
@ -24,36 +27,53 @@
|
|||
</el-table-column>
|
||||
|
||||
<el-table-column prop="num" label="ID"
|
||||
sortable="custom"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column prop="name" :label="$t('api_test.automation.scenario_name')"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
|
||||
show-overflow-tooltip>
|
||||
<el-table-column prop="name"
|
||||
sortable="custom"
|
||||
:label="$t('api_test.automation.scenario_name')"
|
||||
show-overflow-tooltip
|
||||
min-width="100px"/>
|
||||
<el-table-column prop="level"
|
||||
sortable="custom"
|
||||
column-key="level"
|
||||
:filters="levelFilters"
|
||||
:label="$t('api_test.automation.case_level')"
|
||||
show-overflow-tooltip min-width="120px">
|
||||
<template v-slot:default="scope">
|
||||
<priority-table-item :value="scope.row.level"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('test_track.plan.plan_status')"
|
||||
show-overflow-tooltip>
|
||||
sortable="custom"
|
||||
column-key="status"
|
||||
:filters="statusFilters"
|
||||
show-overflow-tooltip min-width="120px">
|
||||
<template v-slot:default="scope">
|
||||
<plan-status-table-item :value="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="tags" :label="$t('api_test.automation.tag')" width="200px">
|
||||
<el-table-column prop="tags" :label="$t('api_test.automation.tag')">
|
||||
<template v-slot:default="scope">
|
||||
<div v-for="(itemName,index) in scope.row.tags" :key="index">
|
||||
<ms-tag type="success" effect="plain" :content="itemName"/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="userId" :label="$t('api_test.automation.creator')" show-overflow-tooltip/>
|
||||
<el-table-column prop="updateTime" :label="$t('api_test.automation.update_time')" width="180">
|
||||
<el-table-column prop="userId" :label="$t('api_test.automation.creator')"
|
||||
:filters="userFilters"
|
||||
column-key="user_id"
|
||||
sortable="custom"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column prop="updateTime" :label="$t('api_test.automation.update_time')" sortable="custom" width="180">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="stepTotal" :label="$t('api_test.automation.step')" show-overflow-tooltip/>
|
||||
<el-table-column prop="lastResult" :label="$t('api_test.automation.last_result')">
|
||||
<el-table-column prop="lastResult" :label="$t('api_test.automation.last_result')"
|
||||
:filters="resultFilters"
|
||||
sortable="custom" column-key="last_result" min-width="120px">
|
||||
<template v-slot:default="{row}">
|
||||
<el-link type="success" @click="showReport(row)" v-if="row.lastResult === 'Success'">
|
||||
{{ $t('api_test.automation.success') }}
|
||||
|
@ -112,436 +132,473 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
|
||||
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
|
||||
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
|
||||
import MsTag from "../../../common/components/MsTag";
|
||||
import {getUUID, getCurrentProjectID} from "@/common/js/utils";
|
||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
||||
import MsTableMoreBtn from "./TableMoreBtn";
|
||||
import MsScenarioExtendButtons from "@/business/components/api/automation/scenario/ScenarioExtendBtns";
|
||||
import MsTestPlanList from "./testplan/TestPlanList";
|
||||
import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll";
|
||||
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
|
||||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||
import PriorityTableItem from "../../../track/common/tableItems/planview/PriorityTableItem";
|
||||
import PlanStatusTableItem from "../../../track/common/tableItems/plan/PlanStatusTableItem";
|
||||
import BatchEdit from "../../../track/case/components/BatchEdit";
|
||||
import {WORKSPACE_ID} from "../../../../../common/js/constants";
|
||||
import EnvironmentSelect from "../../definition/components/environment/EnvironmentSelect";
|
||||
import BatchMove from "../../../track/case/components/BatchMove";
|
||||
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
|
||||
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
|
||||
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
|
||||
import MsTag from "../../../common/components/MsTag";
|
||||
import {_filter, _sort, getCurrentProjectID, getUUID} from "@/common/js/utils";
|
||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
||||
import MsTableMoreBtn from "./TableMoreBtn";
|
||||
import MsScenarioExtendButtons from "@/business/components/api/automation/scenario/ScenarioExtendBtns";
|
||||
import MsTestPlanList from "./testplan/TestPlanList";
|
||||
import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll";
|
||||
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
|
||||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||
import PriorityTableItem from "../../../track/common/tableItems/planview/PriorityTableItem";
|
||||
import PlanStatusTableItem from "../../../track/common/tableItems/plan/PlanStatusTableItem";
|
||||
import BatchEdit from "../../../track/case/components/BatchEdit";
|
||||
import {WORKSPACE_ID} from "../../../../../common/js/constants";
|
||||
import EnvironmentSelect from "../../definition/components/environment/EnvironmentSelect";
|
||||
import BatchMove from "../../../track/case/components/BatchMove";
|
||||
|
||||
export default {
|
||||
name: "MsApiScenarioList",
|
||||
components: {
|
||||
BatchMove,
|
||||
EnvironmentSelect,
|
||||
BatchEdit,
|
||||
PlanStatusTableItem,
|
||||
PriorityTableItem,
|
||||
MsTableSelectAll,
|
||||
MsTablePagination,
|
||||
MsTableMoreBtn,
|
||||
ShowMoreBtn,
|
||||
MsTableHeader,
|
||||
MsTag,
|
||||
MsApiReportDetail,
|
||||
MsScenarioExtendButtons,
|
||||
MsTestPlanList,
|
||||
MsTableOperatorButton
|
||||
export default {
|
||||
name: "MsApiScenarioList",
|
||||
components: {
|
||||
BatchMove,
|
||||
EnvironmentSelect,
|
||||
BatchEdit,
|
||||
PlanStatusTableItem,
|
||||
PriorityTableItem,
|
||||
MsTableSelectAll,
|
||||
MsTablePagination,
|
||||
MsTableMoreBtn,
|
||||
ShowMoreBtn,
|
||||
MsTableHeader,
|
||||
MsTag,
|
||||
MsApiReportDetail,
|
||||
MsScenarioExtendButtons,
|
||||
MsTestPlanList,
|
||||
MsTableOperatorButton
|
||||
},
|
||||
props: {
|
||||
referenced: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
props: {
|
||||
referenced: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
selectNodeIds: Array,
|
||||
trashEnable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
moduleTree: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
moduleOptions: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
}
|
||||
selectNodeIds: Array,
|
||||
trashEnable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
condition: {
|
||||
components: API_CASE_CONFIGS
|
||||
},
|
||||
currentScenario: {},
|
||||
schedule: {},
|
||||
selection: [],
|
||||
tableData: [],
|
||||
selectDataRange: 'all',
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
reportId: "",
|
||||
batchReportId: "",
|
||||
content: {},
|
||||
infoDb: false,
|
||||
runVisible: false,
|
||||
planVisible: false,
|
||||
projectId: "",
|
||||
runData: [],
|
||||
report: {},
|
||||
selectDataSize: 0,
|
||||
selectAll: false,
|
||||
buttons: [
|
||||
{
|
||||
name: this.$t('api_test.automation.batch_add_plan'), handleClick: this.handleBatchAddCase
|
||||
},
|
||||
{
|
||||
name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute
|
||||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit
|
||||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
|
||||
}
|
||||
],
|
||||
isSelectAllDate: false,
|
||||
unSelection: [],
|
||||
selectDataCounts: 0,
|
||||
typeArr: [
|
||||
{id: 'level', name: this.$t('test_track.case.priority')},
|
||||
{id: 'status', name: this.$t('test_track.plan.plan_status')},
|
||||
{id: 'principal', name: this.$t('api_test.definition.request.responsible'), optionMethod: this.getPrincipalOptions},
|
||||
{id: 'environmentId', name: this.$t('api_test.definition.request.run_env'), optionMethod: this.getEnvsOptions},
|
||||
],
|
||||
valueArr: {
|
||||
level: [
|
||||
{name: 'P0', id: 'P0'},
|
||||
{name: 'P1', id: 'P1'},
|
||||
{name: 'P2', id: 'P2'},
|
||||
{name: 'P3', id: 'P3'}
|
||||
],
|
||||
status: [
|
||||
{name: this.$t('test_track.plan.plan_status_prepare'), id: 'Prepare'},
|
||||
{name: this.$t('test_track.plan.plan_status_running'), id: 'Underway'},
|
||||
{name: this.$t('test_track.plan.plan_status_completed'), id: 'Completed'}
|
||||
],
|
||||
principal: [],
|
||||
environmentId: []
|
||||
},
|
||||
}
|
||||
moduleTree: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.projectId = getCurrentProjectID();
|
||||
this.search();
|
||||
},
|
||||
watch: {
|
||||
selectNodeIds() {
|
||||
this.search();
|
||||
},
|
||||
trashEnable() {
|
||||
if (this.trashEnable) {
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
batchReportId() {
|
||||
this.loading = true;
|
||||
this.getReport();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isNotRunning() {
|
||||
return "Running" !== this.report.status;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectByParam() {
|
||||
this.changeSelectDataRangeAll();
|
||||
this.search();
|
||||
},
|
||||
search() {
|
||||
this.condition.filters = ["Prepare", "Underway", "Completed"];
|
||||
this.condition.moduleIds = this.selectNodeIds;
|
||||
if (this.trashEnable) {
|
||||
this.condition.filters = ["Trash"];
|
||||
this.condition.moduleIds = [];
|
||||
}
|
||||
|
||||
if (this.projectId != null) {
|
||||
this.condition.projectId = this.projectId;
|
||||
}
|
||||
|
||||
//检查是否只查询本周数据
|
||||
this.condition.selectThisWeedData = false;
|
||||
this.condition.executeStatus = null;
|
||||
this.isSelectThissWeekData();
|
||||
switch (this.selectDataRange) {
|
||||
case 'thisWeekCount':
|
||||
this.condition.selectThisWeedData = true;
|
||||
break;
|
||||
case 'unExecute':
|
||||
this.condition.executeStatus = 'unExecute';
|
||||
break;
|
||||
case 'executeFailed':
|
||||
this.condition.executeStatus = 'executeFailed';
|
||||
break;
|
||||
case 'executePass':
|
||||
this.condition.executeStatus = 'executePass';
|
||||
break;
|
||||
}
|
||||
this.selection = [];
|
||||
this.selectAll = false;
|
||||
this.unSelection = [];
|
||||
this.selectDataCounts = 0;
|
||||
let url = "/api/automation/list/" + this.currentPage + "/" + this.pageSize;
|
||||
if (this.condition.projectId) {
|
||||
this.loading = true;
|
||||
this.$post(url, this.condition, response => {
|
||||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
this.tableData = data.listObject;
|
||||
this.tableData.forEach(item => {
|
||||
if (item.tags && item.tags.length > 0) {
|
||||
item.tags = JSON.parse(item.tags);
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
this.unSelection = data.listObject.map(s => s.id);
|
||||
});
|
||||
}
|
||||
},
|
||||
handleCommand(cmd) {
|
||||
let table = this.$refs.scenarioTable;
|
||||
switch (cmd) {
|
||||
case "table":
|
||||
this.selectAll = false;
|
||||
table.toggleAllSelection();
|
||||
break;
|
||||
case "all":
|
||||
this.selectAll = true;
|
||||
break
|
||||
}
|
||||
},
|
||||
handleBatchAddCase() {
|
||||
this.planVisible = true;
|
||||
},
|
||||
handleBatchEdit() {
|
||||
this.$refs.batchEdit.open(this.selectDataCounts);
|
||||
},
|
||||
handleBatchMove() {
|
||||
this.$refs.testBatchMove.open(this.moduleTree, [], this.moduleOptions);
|
||||
},
|
||||
moveSave(param) {
|
||||
this.buildBatchParam(param);
|
||||
param.apiScenarioModuleId = param.nodeId;
|
||||
this.$post('/api/automation/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.testBatchMove.close();
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
batchEdit(form) {
|
||||
let param = {};
|
||||
param[form.type] = form.value;
|
||||
this.buildBatchParam(param);
|
||||
this.$post('/api/automation/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
getPrincipalOptions(option) {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
option.push(...response.data);
|
||||
});
|
||||
},
|
||||
getEnvsOptions(option) {
|
||||
this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
option.push(...response.data);
|
||||
option.forEach(environment => {
|
||||
if (!(environment.config instanceof Object)) {
|
||||
environment.config = JSON.parse(environment.config);
|
||||
}
|
||||
environment.name = environment.name + (environment.config.httpConfig.socket ?
|
||||
(': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '');
|
||||
});
|
||||
});
|
||||
},
|
||||
addTestPlan(plans) {
|
||||
let obj = {planIds: plans, scenarioIds: this.selection};
|
||||
|
||||
obj.projectId = getCurrentProjectID();
|
||||
obj.selectAllDate = this.isSelectAllDate;
|
||||
obj.unSelectIds = this.unSelection;
|
||||
obj = Object.assign(obj, this.condition);
|
||||
|
||||
this.planVisible = false;
|
||||
this.$post("/api/automation/scenario/plan", obj, response => {
|
||||
this.$success(this.$t("commons.save_success"));
|
||||
});
|
||||
},
|
||||
getReport() {
|
||||
if (this.batchReportId) {
|
||||
let url = "/api/scenario/report/get/" + this.batchReportId;
|
||||
this.$get(url, response => {
|
||||
this.report = response.data || {};
|
||||
if (response.data) {
|
||||
if (this.isNotRunning) {
|
||||
try {
|
||||
this.content = JSON.parse(this.report.content);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
this.loading = false;
|
||||
this.$success("批量执行成功,请到报告页面查看详情!");
|
||||
} else {
|
||||
setTimeout(this.getReport, 2000)
|
||||
}
|
||||
} else {
|
||||
this.loading = false;
|
||||
this.$error(this.$t('api_report.not_exist'));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
buildBatchParam(param) {
|
||||
param.scenarioIds = this.selection;
|
||||
param.projectId = getCurrentProjectID();
|
||||
param.selectAllDate = this.isSelectAllDate;
|
||||
param.unSelectIds = this.unSelection;
|
||||
param = Object.assign(param, this.condition);
|
||||
},
|
||||
handleBatchExecute() {
|
||||
this.infoDb = false;
|
||||
let url = "/api/automation/run/batch";
|
||||
let run = {};
|
||||
run.id = getUUID();
|
||||
this.buildBatchParam(run);
|
||||
this.$post(url, run, response => {
|
||||
let data = response.data;
|
||||
this.runVisible = false;
|
||||
this.batchReportId = run.id;
|
||||
});
|
||||
},
|
||||
select(selection) {
|
||||
this.selection = selection.map(s => s.id);
|
||||
|
||||
//统计应当展示选择了多少行
|
||||
this.selectRowsCount(this.selection)
|
||||
|
||||
this.$emit('selection', selection);
|
||||
},
|
||||
isSelect(row) {
|
||||
return this.selection.includes(row.id)
|
||||
},
|
||||
edit(row) {
|
||||
let data = JSON.parse(JSON.stringify(row));
|
||||
this.$emit('edit', data);
|
||||
},
|
||||
reductionApi(row) {
|
||||
row.scenarioDefinition = null;
|
||||
row.tags = null;
|
||||
let rows = [row];
|
||||
this.$post("/api/automation/reduction", rows, response => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.search();
|
||||
})
|
||||
},
|
||||
execute(row) {
|
||||
this.infoDb = false;
|
||||
let url = "/api/automation/run";
|
||||
let run = {};
|
||||
let scenarioIds = [];
|
||||
scenarioIds.push(row.id);
|
||||
run.id = getUUID();
|
||||
run.projectId = getCurrentProjectID();
|
||||
run.scenarioIds = scenarioIds;
|
||||
this.$post(url, run, response => {
|
||||
let data = response.data;
|
||||
this.runVisible = true;
|
||||
this.reportId = run.id;
|
||||
});
|
||||
},
|
||||
copy(row) {
|
||||
let rowParam = JSON.parse(JSON.stringify(row));
|
||||
rowParam.copy = true;
|
||||
rowParam.name = 'copy_' + rowParam.name;
|
||||
this.$emit('edit', rowParam);
|
||||
},
|
||||
showReport(row) {
|
||||
this.runVisible = true;
|
||||
this.infoDb = true;
|
||||
this.reportId = row.reportId;
|
||||
},
|
||||
//是否选择了全部数据
|
||||
isSelectDataAll(dataType) {
|
||||
this.isSelectAllDate = dataType;
|
||||
this.selectRowsCount(this.selection);
|
||||
//如果已经全选,不需要再操作了
|
||||
if (this.selection.length != this.tableData.length) {
|
||||
this.$refs.scenarioTable.toggleAllSelection(true);
|
||||
}
|
||||
},
|
||||
//选择数据数量统计
|
||||
selectRowsCount(selection) {
|
||||
let selectedIDs = selection;
|
||||
let allIDs = this.tableData.map(s => s.id);
|
||||
this.unSelection = allIDs.filter(function (val) {
|
||||
return selectedIDs.indexOf(val) === -1
|
||||
});
|
||||
if (this.isSelectAllDate) {
|
||||
this.selectDataCounts = this.total - this.unSelection.length;
|
||||
} else {
|
||||
this.selectDataCounts = this.selection.length;
|
||||
}
|
||||
},
|
||||
//判断是否只显示本周的数据。 从首页跳转过来的请求会带有相关参数
|
||||
isSelectThissWeekData() {
|
||||
let dataRange = this.$route.params.dataSelectRange;
|
||||
this.selectDataRange = dataRange;
|
||||
},
|
||||
changeSelectDataRangeAll() {
|
||||
this.$emit("changeSelectDataRangeAll");
|
||||
},
|
||||
remove(row) {
|
||||
if (this.trashEnable) {
|
||||
this.$get('/api/automation/delete/' + row.id, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + row.name + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let ids = [row.id];
|
||||
this.$post('/api/automation/removeToGc/', ids, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
moduleOptions: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
condition: {
|
||||
components: API_CASE_CONFIGS
|
||||
},
|
||||
currentScenario: {},
|
||||
schedule: {},
|
||||
selection: [],
|
||||
tableData: [],
|
||||
selectDataRange: 'all',
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
reportId: "",
|
||||
batchReportId: "",
|
||||
content: {},
|
||||
infoDb: false,
|
||||
runVisible: false,
|
||||
planVisible: false,
|
||||
projectId: "",
|
||||
runData: [],
|
||||
report: {},
|
||||
selectDataSize: 0,
|
||||
selectAll: false,
|
||||
userFilters: [],
|
||||
buttons: [
|
||||
{
|
||||
name: this.$t('api_test.automation.batch_add_plan'), handleClick: this.handleBatchAddCase
|
||||
},
|
||||
{
|
||||
name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute
|
||||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit
|
||||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
|
||||
}
|
||||
],
|
||||
isSelectAllDate: false,
|
||||
unSelection: [],
|
||||
selectDataCounts: 0,
|
||||
typeArr: [
|
||||
{id: 'level', name: this.$t('test_track.case.priority')},
|
||||
{id: 'status', name: this.$t('test_track.plan.plan_status')},
|
||||
{id: 'principal', name: this.$t('api_test.definition.request.responsible'), optionMethod: this.getPrincipalOptions},
|
||||
{id: 'environmentId', name: this.$t('api_test.definition.request.run_env'), optionMethod: this.getEnvsOptions},
|
||||
],
|
||||
statusFilters: [
|
||||
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
|
||||
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
|
||||
{text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'},
|
||||
{text: this.$t('test_track.plan.plan_status_trash'), value: 'Trash'},
|
||||
],
|
||||
levelFilters: [
|
||||
{text: 'P0', value: 'P0'},
|
||||
{text: 'P1', value: 'P1'},
|
||||
{text: 'P2', value: 'P2'},
|
||||
{text: 'P3', value: 'P3'}
|
||||
],
|
||||
resultFilters: [
|
||||
{text: 'Fail', value: 'Fail'},
|
||||
{text: 'Success', value: 'Success'}
|
||||
],
|
||||
valueArr: {
|
||||
level: [
|
||||
{name: 'P0', id: 'P0'},
|
||||
{name: 'P1', id: 'P1'},
|
||||
{name: 'P2', id: 'P2'},
|
||||
{name: 'P3', id: 'P3'}
|
||||
],
|
||||
status: [
|
||||
{name: this.$t('test_track.plan.plan_status_prepare'), id: 'Prepare'},
|
||||
{name: this.$t('test_track.plan.plan_status_running'), id: 'Underway'},
|
||||
{name: this.$t('test_track.plan.plan_status_completed'), id: 'Completed'}
|
||||
],
|
||||
principal: [],
|
||||
environmentId: []
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
|
||||
this.projectId = getCurrentProjectID();
|
||||
this.search();
|
||||
this.getPrincipalOptions([]);
|
||||
},
|
||||
watch: {
|
||||
selectNodeIds() {
|
||||
this.search();
|
||||
},
|
||||
trashEnable() {
|
||||
if (this.trashEnable) {
|
||||
this.condition.filters = {status: ["Trash"]};
|
||||
this.condition.moduleIds = [];
|
||||
} else {
|
||||
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
|
||||
}
|
||||
this.search();
|
||||
},
|
||||
batchReportId() {
|
||||
this.loading = true;
|
||||
this.getReport();
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isNotRunning() {
|
||||
return "Running" !== this.report.status;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectByParam() {
|
||||
this.changeSelectDataRangeAll();
|
||||
this.search();
|
||||
},
|
||||
search() {
|
||||
this.condition.moduleIds = this.selectNodeIds;
|
||||
if (this.trashEnable) {
|
||||
this.condition.filters = {status: ["Trash"]};
|
||||
this.condition.moduleIds = [];
|
||||
}
|
||||
|
||||
if (this.projectId != null) {
|
||||
this.condition.projectId = this.projectId;
|
||||
}
|
||||
|
||||
//检查是否只查询本周数据
|
||||
this.condition.selectThisWeedData = false;
|
||||
this.condition.executeStatus = null;
|
||||
this.isSelectThissWeekData();
|
||||
switch (this.selectDataRange) {
|
||||
case 'thisWeekCount':
|
||||
this.condition.selectThisWeedData = true;
|
||||
break;
|
||||
case 'unExecute':
|
||||
this.condition.executeStatus = 'unExecute';
|
||||
break;
|
||||
case 'executeFailed':
|
||||
this.condition.executeStatus = 'executeFailed';
|
||||
break;
|
||||
case 'executePass':
|
||||
this.condition.executeStatus = 'executePass';
|
||||
break;
|
||||
}
|
||||
this.selection = [];
|
||||
this.selectAll = false;
|
||||
this.unSelection = [];
|
||||
this.selectDataCounts = 0;
|
||||
let url = "/api/automation/list/" + this.currentPage + "/" + this.pageSize;
|
||||
if (this.condition.projectId) {
|
||||
this.loading = true;
|
||||
this.$post(url, this.condition, response => {
|
||||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
this.tableData = data.listObject;
|
||||
this.tableData.forEach(item => {
|
||||
if (item.tags && item.tags.length > 0) {
|
||||
item.tags = JSON.parse(item.tags);
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
this.unSelection = data.listObject.map(s => s.id);
|
||||
});
|
||||
}
|
||||
},
|
||||
handleCommand(cmd) {
|
||||
let table = this.$refs.scenarioTable;
|
||||
switch (cmd) {
|
||||
case "table":
|
||||
this.selectAll = false;
|
||||
table.toggleAllSelection();
|
||||
break;
|
||||
case "all":
|
||||
this.selectAll = true;
|
||||
break
|
||||
}
|
||||
},
|
||||
handleBatchAddCase() {
|
||||
this.planVisible = true;
|
||||
},
|
||||
handleBatchEdit() {
|
||||
this.$refs.batchEdit.open(this.selectDataCounts);
|
||||
},
|
||||
handleBatchMove() {
|
||||
this.$refs.testBatchMove.open(this.moduleTree, [], this.moduleOptions);
|
||||
},
|
||||
moveSave(param) {
|
||||
this.buildBatchParam(param);
|
||||
param.apiScenarioModuleId = param.nodeId;
|
||||
this.$post('/api/automation/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.testBatchMove.close();
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
batchEdit(form) {
|
||||
let param = {};
|
||||
param[form.type] = form.value;
|
||||
this.buildBatchParam(param);
|
||||
this.$post('/api/automation/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
getPrincipalOptions(option) {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
option.push(...response.data);
|
||||
this.userFilters = response.data.map(u => {
|
||||
return {text: u.name, value: u.id}
|
||||
});
|
||||
});
|
||||
},
|
||||
getEnvsOptions(option) {
|
||||
this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
option.push(...response.data);
|
||||
option.forEach(environment => {
|
||||
if (!(environment.config instanceof Object)) {
|
||||
environment.config = JSON.parse(environment.config);
|
||||
}
|
||||
environment.name = environment.name + (environment.config.httpConfig.socket ?
|
||||
(': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '');
|
||||
});
|
||||
});
|
||||
},
|
||||
addTestPlan(plans) {
|
||||
let obj = {planIds: plans, scenarioIds: this.selection};
|
||||
|
||||
obj.projectId = getCurrentProjectID();
|
||||
obj.selectAllDate = this.isSelectAllDate;
|
||||
obj.unSelectIds = this.unSelection;
|
||||
obj = Object.assign(obj, this.condition);
|
||||
|
||||
this.planVisible = false;
|
||||
this.$post("/api/automation/scenario/plan", obj, response => {
|
||||
this.$success(this.$t("commons.save_success"));
|
||||
});
|
||||
},
|
||||
getReport() {
|
||||
if (this.batchReportId) {
|
||||
let url = "/api/scenario/report/get/" + this.batchReportId;
|
||||
this.$get(url, response => {
|
||||
this.report = response.data || {};
|
||||
if (response.data) {
|
||||
if (this.isNotRunning) {
|
||||
try {
|
||||
this.content = JSON.parse(this.report.content);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
this.loading = false;
|
||||
this.$success("批量执行成功,请到报告页面查看详情!");
|
||||
} else {
|
||||
setTimeout(this.getReport, 2000)
|
||||
}
|
||||
} else {
|
||||
this.loading = false;
|
||||
this.$error(this.$t('api_report.not_exist'));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
buildBatchParam(param) {
|
||||
param.scenarioIds = this.selection;
|
||||
param.projectId = getCurrentProjectID();
|
||||
param.selectAllDate = this.isSelectAllDate;
|
||||
param.unSelectIds = this.unSelection;
|
||||
param = Object.assign(param, this.condition);
|
||||
},
|
||||
handleBatchExecute() {
|
||||
this.infoDb = false;
|
||||
let url = "/api/automation/run/batch";
|
||||
let run = {};
|
||||
run.id = getUUID();
|
||||
this.buildBatchParam(run);
|
||||
this.$post(url, run, response => {
|
||||
let data = response.data;
|
||||
this.runVisible = false;
|
||||
this.batchReportId = run.id;
|
||||
});
|
||||
},
|
||||
select(selection) {
|
||||
this.selection = selection.map(s => s.id);
|
||||
|
||||
//统计应当展示选择了多少行
|
||||
this.selectRowsCount(this.selection)
|
||||
|
||||
this.$emit('selection', selection);
|
||||
},
|
||||
isSelect(row) {
|
||||
return this.selection.includes(row.id)
|
||||
},
|
||||
edit(row) {
|
||||
let data = JSON.parse(JSON.stringify(row));
|
||||
this.$emit('edit', data);
|
||||
},
|
||||
reductionApi(row) {
|
||||
row.scenarioDefinition = null;
|
||||
row.tags = null;
|
||||
let rows = [row];
|
||||
this.$post("/api/automation/reduction", rows, response => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.search();
|
||||
})
|
||||
},
|
||||
execute(row) {
|
||||
this.infoDb = false;
|
||||
let url = "/api/automation/run";
|
||||
let run = {};
|
||||
let scenarioIds = [];
|
||||
scenarioIds.push(row.id);
|
||||
run.id = getUUID();
|
||||
run.projectId = getCurrentProjectID();
|
||||
run.scenarioIds = scenarioIds;
|
||||
this.$post(url, run, response => {
|
||||
let data = response.data;
|
||||
this.runVisible = true;
|
||||
this.reportId = run.id;
|
||||
});
|
||||
},
|
||||
copy(row) {
|
||||
let rowParam = JSON.parse(JSON.stringify(row));
|
||||
rowParam.copy = true;
|
||||
rowParam.name = 'copy_' + rowParam.name;
|
||||
this.$emit('edit', rowParam);
|
||||
},
|
||||
showReport(row) {
|
||||
this.runVisible = true;
|
||||
this.infoDb = true;
|
||||
this.reportId = row.reportId;
|
||||
},
|
||||
//是否选择了全部数据
|
||||
isSelectDataAll(dataType) {
|
||||
this.isSelectAllDate = dataType;
|
||||
this.selectRowsCount(this.selection);
|
||||
//如果已经全选,不需要再操作了
|
||||
if (this.selection.length != this.tableData.length) {
|
||||
this.$refs.scenarioTable.toggleAllSelection(true);
|
||||
}
|
||||
},
|
||||
//选择数据数量统计
|
||||
selectRowsCount(selection) {
|
||||
let selectedIDs = selection;
|
||||
let allIDs = this.tableData.map(s => s.id);
|
||||
this.unSelection = allIDs.filter(function (val) {
|
||||
return selectedIDs.indexOf(val) === -1
|
||||
});
|
||||
if (this.isSelectAllDate) {
|
||||
this.selectDataCounts = this.total - this.unSelection.length;
|
||||
} else {
|
||||
this.selectDataCounts = this.selection.length;
|
||||
}
|
||||
},
|
||||
//判断是否只显示本周的数据。 从首页跳转过来的请求会带有相关参数
|
||||
isSelectThissWeekData() {
|
||||
let dataRange = this.$route.params.dataSelectRange;
|
||||
this.selectDataRange = dataRange;
|
||||
},
|
||||
changeSelectDataRangeAll() {
|
||||
this.$emit("changeSelectDataRangeAll");
|
||||
},
|
||||
remove(row) {
|
||||
if (this.trashEnable) {
|
||||
this.$get('/api/automation/delete/' + row.id, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + row.name + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let ids = [row.id];
|
||||
this.$post('/api/automation/removeToGc/', ids, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.search();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
sort(column) {
|
||||
// 每次只对一个字段排序
|
||||
if (this.condition.orders) {
|
||||
this.condition.orders = [];
|
||||
}
|
||||
_sort(column, this.condition);
|
||||
this.search();
|
||||
},
|
||||
filter(filters) {
|
||||
_filter(filters, this.condition);
|
||||
this.search();
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/deep/ .el-drawer__header {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
/deep/ .el-drawer__header {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
/deep/ .run-button {
|
||||
background-color: #409EFF;
|
||||
border-color: #409EFF;
|
||||
}
|
||||
/deep/ .run-button {
|
||||
background-color: #409EFF;
|
||||
border-color: #409EFF;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
highlight-current
|
||||
@node-expand="nodeExpand"
|
||||
@node-collapse="nodeCollapse"
|
||||
:allow-drop="allowDrop" @node-drag-end="allowDrag" @node-click="nodeClick" v-if="!loading" draggable>
|
||||
:allow-drop="allowDrop" @node-drag-end="allowDrag" @node-click="nodeClick" v-if="!loading" draggable class="ms-is-leaf">
|
||||
<span class="custom-tree-node father" slot-scope="{ node, data}" style="width: 96%">
|
||||
<!-- 步骤组件-->
|
||||
<ms-component-config :type="data.type" :scenario="data" :response="response" :currentScenario="currentScenario"
|
||||
|
@ -873,6 +873,7 @@
|
|||
}
|
||||
this.enableCookieShare = obj.enableCookieShare;
|
||||
this.scenarioDefinition = obj.hashTree;
|
||||
console.log(this.scenarioDefinition)
|
||||
}
|
||||
}
|
||||
if (this.currentScenario.copy) {
|
||||
|
@ -1035,7 +1036,7 @@
|
|||
color: #7C3985;
|
||||
}
|
||||
|
||||
/deep/ .is-leaf {
|
||||
.ms-is-leaf >>> .is-leaf {
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -24,10 +24,17 @@
|
|||
</span>
|
||||
|
||||
<div class="header-right" @click.stop>
|
||||
<el-switch v-model="data.enable" class="enable-switch"/>
|
||||
<slot name="message"></slot>
|
||||
<el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top">
|
||||
<el-switch v-model="data.enable" class="enable-switch"/>
|
||||
</el-tooltip>
|
||||
<slot name="button"></slot>
|
||||
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow"/>
|
||||
<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove"/>
|
||||
<el-tooltip content="Copy" placement="top">
|
||||
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow"/>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="$t('commons.remove')" placement="top">
|
||||
<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove"/>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
</template>
|
||||
|
||||
<template v-slot:button>
|
||||
<el-button @click="run" :tip="$t('api_test.run')" icon="el-icon-video-play" style="background-color: #409EFF;color: white;" size="mini" circle/>
|
||||
<el-tooltip :content="$t('api_test.run')" placement="top">
|
||||
<el-button @click="run" icon="el-icon-video-play" style="background-color: #409EFF;color: white;" size="mini" circle/>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
<div v-if="request.protocol === 'HTTP'">
|
||||
|
@ -36,13 +38,13 @@
|
|||
</el-input>
|
||||
</div>
|
||||
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
|
||||
<ms-api-request-form :referenced="true" :headers="request.headers " :request="request" v-if="request.protocol==='HTTP' || request.type==='HTTPSamplerProxy'"/>
|
||||
<ms-api-request-form :isShowEnable="true" :referenced="true" :headers="request.headers " :request="request" v-if="request.protocol==='HTTP' || request.type==='HTTPSamplerProxy'"/>
|
||||
<ms-tcp-basis-parameters :request="request" v-if="request.protocol==='TCP'|| request.type==='TCPSampler'"/>
|
||||
<ms-sql-basis-parameters :request="request" v-if="request.protocol==='SQL'|| request.type==='JDBCSampler'" :showScript="false"/>
|
||||
<ms-dubbo-basis-parameters :request="request" v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'" :showScript="false"/>
|
||||
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
<api-response-component :result="request.requestResult"/>
|
||||
<api-response-component :currentProtocol="request.protocol" :result="request.requestResult"/>
|
||||
|
||||
<!-- 保存操作 -->
|
||||
<el-button type="primary" size="small" style="margin: 20px; float: right" @click="saveTestCase(item)" v-if="!request.referenced">
|
||||
|
@ -86,7 +88,7 @@
|
|||
reqOptions: REQ_METHOD,
|
||||
reportId: "",
|
||||
runData: [],
|
||||
isShowInput: false
|
||||
isShowInput: false,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<el-collapse-transition>
|
||||
<div v-if="isActive">
|
||||
<el-divider></el-divider>
|
||||
<ms-request-result-tail :show-metric="false" :response="response"/>
|
||||
<ms-request-result-tail :currentProtocol="currentProtocol" :show-metric="false" :response="response"/>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</el-card>
|
||||
|
@ -24,7 +24,7 @@
|
|||
export default {
|
||||
name: "ApiResponseComponent",
|
||||
components: {ElCollapseTransition, MsRequestResultTail, ApiBaseComponent, MsRequestMetric},
|
||||
props: {apiItem: {}, result: {}},
|
||||
props: {apiItem: {}, result: {}, currentProtocol: String},
|
||||
data() {
|
||||
return {
|
||||
isActive: false,
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
title: this.$t('api_test.automation.customize_script'),
|
||||
title: "",
|
||||
titleColor: "",
|
||||
backgroundColor: "",
|
||||
}
|
||||
|
|
|
@ -1,85 +1,112 @@
|
|||
<template>
|
||||
<div>
|
||||
<ms-run :debug="true" :environment="currentEnvironmentId" :reportId="reportId" :run-data="debugData"
|
||||
@runRefresh="runRefresh" ref="runTest"/>
|
||||
<api-base-component
|
||||
@copy="copyRow"
|
||||
@remove="remove"
|
||||
:data="controller"
|
||||
:draggable="true"
|
||||
color="#02A7F0"
|
||||
background-color="#F4F4F5"
|
||||
:title="$t('api_test.automation.loop_controller')" v-loading="loading">
|
||||
|
||||
<api-base-component
|
||||
@copy="copyRow"
|
||||
@remove="remove"
|
||||
:data="controller"
|
||||
:draggable="true"
|
||||
color="#02A7F0"
|
||||
background-color="#F4F4F5"
|
||||
:title="$t('api_test.automation.loop_controller')">
|
||||
<template v-slot:headerLeft>
|
||||
<i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}" @click="active(controller)" style="margin-right: 10px"/>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="LOOP_COUNT">{{$t('loop.loops_title')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="FOREACH">{{$t('loop.foreach')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio>
|
||||
</template>
|
||||
|
||||
<template v-slot:headerLeft>
|
||||
<i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}" @click="active(controller)" style="margin-right: 10px"/>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="LOOP_COUNT">{{$t('loop.loops_title')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="FOREACH">{{$t('loop.foreach')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio>
|
||||
</template>
|
||||
<template v-slot:message>
|
||||
<span v-if="requestResult && requestResult.scenarios && requestResult.scenarios.length > 0 " style="color: #8c939d;margin-right: 10px">
|
||||
循环{{requestResult.scenarios.length}}次 成功{{success}}次 失败{{error}}次
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<div v-if="controller.loopType==='LOOP_COUNT'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.loops')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.loops" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">次</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.proceed')}}</span>
|
||||
<el-tooltip class="item" effect="dark" content="默认为开启,当循环下只有一个请求时,可以开启/关闭;当循环下超过一个请求时,则只能开启。" placement="top">>
|
||||
<el-switch v-model="controller.countController.proceed" @change="switchChange"/>
|
||||
<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/>
|
||||
</template>
|
||||
<div v-if="controller.loopType==='LOOP_COUNT'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.loops')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.loops" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">次</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.proceed')}}</span>
|
||||
<el-tooltip class="item" effect="dark" content="默认为开启,当循环下只有一个请求时,可以开启/关闭;当循环下超过一个请求时,则只能开启。" placement="top">>
|
||||
<el-switch v-model="controller.countController.proceed" @change="switchChange"/>
|
||||
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div v-else-if="controller.loopType==='FOREACH'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-input :placeholder="$t('api_test.request.condition_variable')" v-model="controller.forEachController.inputVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="1" style="margin-top: 6px">
|
||||
<span style="margin:10px 10px 10px">in</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-input :placeholder="$t('api_test.request.condition_variable')" v-model="controller.forEachController.returnVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.forEachController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else draggable>
|
||||
<el-input size="small" v-model="controller.whileController.variable" style="width: 20%" :placeholder="$t('api_test.request.condition_variable')"/>
|
||||
<div v-else-if="controller.loopType==='FOREACH'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-input placeholder="输出变量名称" v-model="controller.forEachController.returnVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="1" style="margin-top: 6px">
|
||||
<span style="margin:10px 10px 10px">in</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-input placeholder="输入变量前缀" v-model="controller.forEachController.inputVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.forEachController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else draggable>
|
||||
<el-input size="small" v-model="controller.whileController.variable" style="width: 20%" :placeholder="$t('api_test.request.condition_variable')"/>
|
||||
|
||||
<el-select v-model="controller.whileController.operator" :placeholder="$t('commons.please_select')" size="small"
|
||||
@change="change" style="width: 10%;margin-left: 10px">
|
||||
<el-option v-for="o in operators" :key="o.value" :label="$t(o.label)" :value="o.value"/>
|
||||
</el-select>
|
||||
<el-input size="small" v-model="controller.whileController.value" :placeholder="$t('api_test.value')" v-if="!hasEmptyOperator" style="width: 20%;margin-left: 20px"/>
|
||||
<span class="ms-span ms-radio">{{$t('loop.timeout')}}</span>
|
||||
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="1" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</div>
|
||||
<el-select v-model="controller.whileController.operator" :placeholder="$t('commons.please_select')" size="small"
|
||||
@change="change" style="width: 10%;margin-left: 10px">
|
||||
<el-option v-for="o in operators" :key="o.value" :label="$t(o.label)" :value="o.value"/>
|
||||
</el-select>
|
||||
<el-input size="small" v-model="controller.whileController.value" :placeholder="$t('api_test.value')" v-if="!hasEmptyOperator" style="width: 20%;margin-left: 20px"/>
|
||||
<span class="ms-span ms-radio">{{$t('loop.timeout')}}</span>
|
||||
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="1" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</div>
|
||||
|
||||
</api-base-component>
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in requestResult.scenarios" :key="index">
|
||||
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
|
||||
<api-response-component :result="result"/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
</api-base-component>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ApiBaseComponent from "../common/ApiBaseComponent";
|
||||
import ApiResponseComponent from "./ApiResponseComponent";
|
||||
import MsRun from "../DebugRun";
|
||||
import {getUUID, getCurrentProjectID} from "@/common/js/utils";
|
||||
|
||||
export default {
|
||||
name: "MsLoopController",
|
||||
components: {ApiBaseComponent},
|
||||
components: {ApiBaseComponent, ApiResponseComponent, MsRun},
|
||||
props: {
|
||||
controller: {},
|
||||
currentEnvironmentId: String,
|
||||
currentScenario: {},
|
||||
node: {},
|
||||
index: Object,
|
||||
draggable: {
|
||||
|
@ -87,9 +114,19 @@
|
|||
default: false,
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.initResult();
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
activeName: "first",
|
||||
requestResult: {responseResult: {}},
|
||||
success: 0,
|
||||
error: 0,
|
||||
debugData: {},
|
||||
report: [],
|
||||
reportId: "",
|
||||
operators: {
|
||||
EQ: {
|
||||
label: "commons.adv_search.operators.equals",
|
||||
|
@ -127,6 +164,25 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
initResult() {
|
||||
if (this.controller) {
|
||||
switch (this.controller.loopType) {
|
||||
case "LOOP_COUNT":
|
||||
this.requestResult = this.controller.countController && this.controller.countController.requestResult ? this.controller.countController.requestResult : {};
|
||||
break;
|
||||
case "FOREACH":
|
||||
this.requestResult = this.controller.forEachController && this.controller.forEachController.requestResult ? this.controller.forEachController.requestResult : {};
|
||||
break;
|
||||
case "WHILE":
|
||||
this.requestResult = this.controller.whileController && this.controller.whileController.requestResult ? this.controller.whileController.requestResult : {};
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.getFails();
|
||||
this.activeName = this.requestResult && this.requestResult.scenarios && this.requestResult.scenarios.length > 0 ? this.requestResult.scenarios[0].name : "";
|
||||
},
|
||||
switchChange() {
|
||||
if (this.controller.hashTree && this.controller.hashTree.length > 1) {
|
||||
this.$warning("当前循环下超过一个请求,不能关闭状态")
|
||||
|
@ -134,6 +190,26 @@
|
|||
return;
|
||||
}
|
||||
},
|
||||
runDebug() {
|
||||
/*触发执行操作*/
|
||||
if (!this.currentEnvironmentId) {
|
||||
this.$error(this.$t('api_test.environment.select_environment'));
|
||||
return;
|
||||
}
|
||||
if (!this.controller.hashTree || this.controller.hashTree.length < 1) {
|
||||
this.$warning("当前循环下没有请求,不能执行")
|
||||
return;
|
||||
}
|
||||
this.controller.active = true;
|
||||
this.loading = true;
|
||||
this.debugData = {
|
||||
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
|
||||
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare,
|
||||
environmentId: this.currentEnvironmentId, hashTree: [this.controller]
|
||||
};
|
||||
this.reportId = getUUID().substring(0, 8);
|
||||
},
|
||||
|
||||
remove() {
|
||||
this.$emit('remove', this.controller, this.node);
|
||||
},
|
||||
|
@ -146,6 +222,7 @@
|
|||
},
|
||||
changeRadio() {
|
||||
this.controller.active = true;
|
||||
this.initResult();
|
||||
this.reload();
|
||||
},
|
||||
change(value) {
|
||||
|
@ -159,11 +236,77 @@
|
|||
this.loading = false
|
||||
})
|
||||
},
|
||||
runRefresh() {
|
||||
this.getReport();
|
||||
},
|
||||
getFails() {
|
||||
this.error = 0;
|
||||
this.success = 0;
|
||||
if (this.requestResult.scenarios) {
|
||||
this.requestResult.scenarios.forEach((scenario) => {
|
||||
if (scenario.requestResults) {
|
||||
scenario.requestResults.forEach(item => {
|
||||
if (item.error > 0) {
|
||||
this.error++;
|
||||
return;
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
this.success = this.requestResult.scenarios.length - this.error;
|
||||
}
|
||||
},
|
||||
getReport() {
|
||||
if (this.reportId) {
|
||||
let url = "/api/scenario/report/get/" + this.reportId;
|
||||
this.$get(url, response => {
|
||||
this.report = response.data || {};
|
||||
if (response.data) {
|
||||
if (this.isNotRunning) {
|
||||
try {
|
||||
this.requestResult = JSON.parse(this.report.content);
|
||||
this.controller.requestResult = this.requestResult;
|
||||
switch (this.controller.loopType) {
|
||||
case "LOOP_COUNT":
|
||||
this.controller.countController.requestResult = this.requestResult;
|
||||
break;
|
||||
case "FOREACH":
|
||||
this.controller.forEachController.requestResult = this.requestResult;
|
||||
break;
|
||||
case "WHILE":
|
||||
this.controller.whileController.requestResult = this.requestResult;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.getFails();
|
||||
if (!this.requestResult) {
|
||||
this.requestResult = {scenarios: []};
|
||||
}
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
this.loading = false;
|
||||
this.activeName = this.requestResult && this.requestResult.scenarios ? this.requestResult.scenarios[0].name : "";
|
||||
} else {
|
||||
setTimeout(this.getReport, 2000)
|
||||
}
|
||||
} else {
|
||||
this.loading = false;
|
||||
this.$error(this.$t('api_report.not_exist'));
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasEmptyOperator() {
|
||||
return !!this.controller.operator && this.controller.operator.indexOf("empty") > 0;
|
||||
},
|
||||
isNotRunning() {
|
||||
return "Running" !== this.report.status;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -180,6 +323,14 @@
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
.tip {
|
||||
padding: 3px 5px;
|
||||
font-size: 16px;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #783887;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.icon.is-active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<el-col :span="20">
|
||||
<el-autocomplete
|
||||
size="small"
|
||||
class="inline-input"
|
||||
style="width: 100%"
|
||||
v-model="editData.encoding"
|
||||
:fetch-suggestions="querySearch"
|
||||
:placeholder="$t('commons.input_content')"
|
||||
|
@ -110,7 +110,7 @@
|
|||
},
|
||||
|
||||
handleClick() {
|
||||
let config = {complete: this.complete, step: this.step};
|
||||
let config = {complete: this.complete, step: this.step, delimiter: this.editData.delimiter ? this.editData.delimiter : ","};
|
||||
this.allDatas = [];
|
||||
// 本地文件
|
||||
if (this.editData.files && this.editData.files.length > 0 && this.editData.files[0].file) {
|
||||
|
@ -152,5 +152,7 @@
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
ms-is-leaf >>> .is-leaf {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -107,6 +107,9 @@
|
|||
},
|
||||
addParameters(v) {
|
||||
v.id = getUUID();
|
||||
if (v.type === 'CSV') {
|
||||
v.delimiter = ",";
|
||||
}
|
||||
this.variables.push(v);
|
||||
let index = 1;
|
||||
this.variables.forEach(item => {
|
||||
|
|
|
@ -1,53 +1,50 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-loading="loading">
|
||||
<span class="kv-description" v-if="description">
|
||||
{{ description }}
|
||||
</span>
|
||||
<ms-draggable element="ul" @update="endChange"
|
||||
v-model="keyValues" v-bind="{draggable:'.item'}">
|
||||
<div class="kv-row item" v-for="(item, index) in keyValues" :key="index">
|
||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||
<el-button icon="el-icon-sort" circle size="mini"/>
|
||||
<div class="kv-row item" v-for="(item, index) in items" :key="index">
|
||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||
<el-col class="kv-checkbox" v-if="isShowEnable">
|
||||
<input type="checkbox" v-if="!isDisable(index)" v-model="item.enable"
|
||||
:disabled="isReadOnly"/>
|
||||
|
||||
<el-col class="kv-checkbox" v-if="isShowEnable">
|
||||
<input type="checkbox" v-if="!isDisable(index)" v-model="item.enable"
|
||||
:disabled="isReadOnly"/>
|
||||
</el-col>
|
||||
</el-col>
|
||||
<span style="margin-left: 10px" v-else></span>
|
||||
|
||||
<el-col class="item">
|
||||
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
|
||||
@change="change"
|
||||
:placeholder="keyText" show-word-limit/>
|
||||
<el-autocomplete :disabled="isReadOnly" :maxlength="200" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText"
|
||||
show-word-limit/>
|
||||
<i class="el-icon-top" style="cursor:pointer" @click="moveTop(index)"/>
|
||||
<i class="el-icon-bottom" style="cursor:pointer;" @click="moveBottom(index)"/>
|
||||
|
||||
</el-col>
|
||||
<el-col class="item">
|
||||
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
|
||||
@change="change"
|
||||
:placeholder="keyText" show-word-limit/>
|
||||
<el-autocomplete :disabled="isReadOnly" :maxlength="200" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText"
|
||||
show-word-limit/>
|
||||
|
||||
<el-col class="item">
|
||||
<el-input :disabled="isReadOnly" v-model="item.value" size="small" @change="change"
|
||||
:placeholder="valueText" show-word-limit/>
|
||||
</el-col>
|
||||
<el-col class="item kv-delete">
|
||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||
:disabled="isDisable(index) || isReadOnly"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</ms-draggable>
|
||||
</el-col>
|
||||
|
||||
<el-col class="item">
|
||||
<el-input :disabled="isReadOnly" v-model="item.value" size="small" @change="change"
|
||||
:placeholder="valueText" show-word-limit/>
|
||||
</el-col>
|
||||
<el-col class="item kv-delete">
|
||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||
:disabled="isDisable(index) || isReadOnly"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {KeyValue} from "../model/ApiTestModel";
|
||||
import MsDraggable from 'vuedraggable'
|
||||
import Vue from 'vue';
|
||||
|
||||
|
||||
export default {
|
||||
name: "MsApiKeyValue",
|
||||
components: {
|
||||
MsDraggable
|
||||
},
|
||||
|
||||
props: {
|
||||
keyPlaceholder: String,
|
||||
valuePlaceholder: String,
|
||||
|
@ -66,6 +63,7 @@
|
|||
data() {
|
||||
return {
|
||||
keyValues: [],
|
||||
loading: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -78,6 +76,31 @@
|
|||
},
|
||||
|
||||
methods: {
|
||||
moveBottom(index) {
|
||||
if (this.items.length < 2 || index === this.items.length - 2) {
|
||||
return;
|
||||
}
|
||||
let thisRow = this.items[index];
|
||||
let nextRow = this.items[index + 1];
|
||||
Vue.set(this.items, index + 1, thisRow);
|
||||
Vue.set(this.items, index, nextRow)
|
||||
},
|
||||
moveTop(index) {
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
let thisRow = this.items[index];
|
||||
let lastRow = this.items[index - 1];
|
||||
Vue.set(this.items, index - 1, thisRow);
|
||||
Vue.set(this.items, index, lastRow)
|
||||
|
||||
},
|
||||
reload() {
|
||||
this.loading = true
|
||||
this.$nextTick(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
remove: function (index) {
|
||||
// 移除整行输入控件及内容
|
||||
this.items.splice(index, 1);
|
||||
|
@ -115,27 +138,11 @@
|
|||
return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0);
|
||||
};
|
||||
},
|
||||
endChange(env) {
|
||||
if (env.newIndex == env.oldIndex) {
|
||||
return;
|
||||
}
|
||||
let newItem = this.keyValues[env.newIndex];
|
||||
let oldItem = this.keyValues[env.oldIndex];
|
||||
this.$set(this.keyValues, env.oldIndex, oldItem);
|
||||
this.$set(this.keyValues, env.newIndex, newItem)
|
||||
this.items.forEach(item => {
|
||||
this.items.splice(0);
|
||||
})
|
||||
this.keyValues.forEach(item => {
|
||||
this.items.push(item);
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.items.length === 0 || this.items[this.items.length - 1].name) {
|
||||
this.items.push(new KeyValue({enable: true}));
|
||||
}
|
||||
this.keyValues = this.items;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -161,4 +168,8 @@
|
|||
.el-autocomplete {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
i:hover {
|
||||
color: #783887;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,85 +3,83 @@
|
|||
<span class="kv-description" v-if="description">
|
||||
{{ description }}
|
||||
</span>
|
||||
<ms-draggable element="ul" @update="endChange"
|
||||
v-model="keyValues" v-bind="{draggable:'.item'}">
|
||||
|
||||
<div class="item kv-row" v-for="(item, index) in keyValues" :key="index">
|
||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||
<el-button icon="el-icon-sort" circle size="mini"/>
|
||||
<div class="item kv-row" v-for="(item, index) in parameters" :key="index">
|
||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||
<el-col class="kv-checkbox" v-if="isShowEnable">
|
||||
<input type="checkbox" v-if="!isDisable(index)" v-model="item.enable"
|
||||
:disabled="isReadOnly"/>
|
||||
</el-col>
|
||||
<span style="margin-left: 10px" v-else></span>
|
||||
<i class="el-icon-top" style="cursor:pointer" @click="moveTop(index)"/>
|
||||
<i class="el-icon-bottom" style="cursor:pointer;" @click="moveBottom(index)"/>
|
||||
|
||||
<el-col class="kv-checkbox" v-if="isShowEnable">
|
||||
<input type="checkbox" v-if="!isDisable(index)" v-model="item.enable"
|
||||
:disabled="isReadOnly"/>
|
||||
</el-col>
|
||||
<el-col class="item">
|
||||
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
|
||||
@change="change" :placeholder="keyText" show-word-limit>
|
||||
<template v-slot:prepend>
|
||||
<el-select v-if="type === 'body'" :disabled="isReadOnly" class="kv-type" v-model="item.type"
|
||||
@change="typeChange(item)">
|
||||
<el-option value="text"/>
|
||||
<el-option value="file"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-col class="item">
|
||||
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
|
||||
@change="change" :placeholder="keyText" show-word-limit>
|
||||
<template v-slot:prepend>
|
||||
<el-select v-if="type === 'body'" :disabled="isReadOnly" class="kv-type" v-model="item.type"
|
||||
@change="typeChange(item)">
|
||||
<el-option value="text"/>
|
||||
<el-option value="file"/>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-autocomplete :disabled="isReadOnly" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText" show-word-limit/>
|
||||
|
||||
<el-autocomplete :disabled="isReadOnly" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText" show-word-limit/>
|
||||
</el-col>
|
||||
|
||||
</el-col>
|
||||
<el-col class="item kv-select">
|
||||
<el-select v-model="item.required" size="small">
|
||||
<el-option v-for="req in requireds" :key="req.id" :label="req.name" :value="req.id"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
|
||||
<el-col class="item kv-select">
|
||||
<el-select v-model="item.required" size="small">
|
||||
<el-option v-for="req in requireds" :key="req.id" :label="req.name" :value="req.id"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col class="item" v-if="item.type !== 'file'">
|
||||
<el-autocomplete
|
||||
:disabled="isReadOnly"
|
||||
size="small"
|
||||
class="input-with-autocomplete"
|
||||
v-model="item.value"
|
||||
:fetch-suggestions="funcSearch"
|
||||
:placeholder="valueText"
|
||||
value-key="name"
|
||||
highlight-first-item
|
||||
@select="change">
|
||||
<i slot="suffix" class="el-input__icon el-icon-edit pointer" @click="advanced(item)"></i>
|
||||
</el-autocomplete>
|
||||
</el-col>
|
||||
|
||||
<el-col class="item" v-if="item.type !== 'file'">
|
||||
<el-autocomplete
|
||||
:disabled="isReadOnly"
|
||||
size="small"
|
||||
class="input-with-autocomplete"
|
||||
v-model="item.value"
|
||||
:fetch-suggestions="funcSearch"
|
||||
:placeholder="valueText"
|
||||
value-key="name"
|
||||
highlight-first-item
|
||||
@select="change">
|
||||
<i slot="suffix" class="el-input__icon el-icon-edit pointer" @click="advanced(item)"></i>
|
||||
</el-autocomplete>
|
||||
</el-col>
|
||||
<el-col class="item">
|
||||
<el-input v-model="item.description" size="small" maxlength="200"
|
||||
:placeholder="$t('commons.description')" show-word-limit>
|
||||
</el-input>
|
||||
|
||||
<el-col class="item">
|
||||
<el-input v-model="item.description" size="small" maxlength="200"
|
||||
:placeholder="$t('commons.description')" show-word-limit>
|
||||
</el-input>
|
||||
<el-autocomplete :disabled="isReadOnly" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText" show-word-limit/>
|
||||
|
||||
<el-autocomplete :disabled="isReadOnly" v-if="suggestions" v-model="item.name" size="small"
|
||||
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText" show-word-limit/>
|
||||
</el-col>
|
||||
|
||||
</el-col>
|
||||
<el-col v-if="item.type === 'file'" class="item">
|
||||
<ms-api-body-file-upload :parameter="item"/>
|
||||
</el-col>
|
||||
|
||||
<el-col v-if="item.type === 'file'" class="item">
|
||||
<ms-api-body-file-upload :parameter="item"/>
|
||||
</el-col>
|
||||
<el-col v-if="type === 'body'" class="item kv-select">
|
||||
<el-input :disabled="isReadOnly" v-model="item.contentType" size="small"
|
||||
@change="change" :placeholder="$t('api_test.request.content_type')" show-word-limit>
|
||||
</el-input>
|
||||
</el-col>
|
||||
|
||||
<el-col v-if="type === 'body'" class="item kv-select">
|
||||
<el-input :disabled="isReadOnly" v-model="item.contentType" size="small"
|
||||
@change="change" :placeholder="$t('api_test.request.content_type')" show-word-limit>
|
||||
</el-input>
|
||||
</el-col>
|
||||
|
||||
<el-col class="item kv-delete">
|
||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||
:disabled="isDisable(index) || isReadOnly"/>
|
||||
</el-col>
|
||||
<el-col class="item kv-delete">
|
||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||
:disabled="isDisable(index) || isReadOnly"/>
|
||||
</el-col>
|
||||
|
||||
|
||||
</el-row>
|
||||
</div>
|
||||
</ms-draggable>
|
||||
</el-row>
|
||||
</div>
|
||||
<ms-api-variable-advance ref="variableAdvance" :environment="environment" :scenario="scenario"
|
||||
:parameters="parameters"
|
||||
:current-item="currentItem"/>
|
||||
|
@ -94,11 +92,11 @@
|
|||
import MsApiVariableAdvance from "./ApiVariableAdvance";
|
||||
import MsApiBodyFileUpload from "./body/ApiBodyFileUpload";
|
||||
import {REQUIRED} from "../model/JsonData";
|
||||
import MsDraggable from 'vuedraggable'
|
||||
import Vue from 'vue';
|
||||
|
||||
export default {
|
||||
name: "MsApiVariable",
|
||||
components: {MsApiBodyFileUpload, MsApiVariableAdvance, MsDraggable},
|
||||
components: {MsApiBodyFileUpload, MsApiVariableAdvance},
|
||||
props: {
|
||||
keyPlaceholder: String,
|
||||
valuePlaceholder: String,
|
||||
|
@ -125,7 +123,6 @@
|
|||
return {
|
||||
currentItem: null,
|
||||
requireds: REQUIRED,
|
||||
keyValues: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -137,6 +134,25 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
moveBottom(index) {
|
||||
if (this.parameters.length < 2 || index === this.parameters.length - 2) {
|
||||
return;
|
||||
}
|
||||
let thisRow = this.parameters[index];
|
||||
let nextRow = this.parameters[index + 1];
|
||||
Vue.set(this.parameters, index + 1, thisRow);
|
||||
Vue.set(this.parameters, index, nextRow)
|
||||
},
|
||||
moveTop(index) {
|
||||
if (index === 0) {
|
||||
return;
|
||||
}
|
||||
let thisRow = this.parameters[index];
|
||||
let lastRow = this.parameters[index - 1];
|
||||
Vue.set(this.parameters, index - 1, thisRow);
|
||||
Vue.set(this.parameters, index, lastRow)
|
||||
|
||||
},
|
||||
remove: function (index) {
|
||||
// 移除整行输入控件及内容
|
||||
this.parameters.splice(index, 1);
|
||||
|
@ -204,21 +220,6 @@
|
|||
item.contentType = 'text/plain';
|
||||
}
|
||||
},
|
||||
endChange(env) {
|
||||
if (env.newIndex == env.oldIndex) {
|
||||
return;
|
||||
}
|
||||
let newItem = this.keyValues[env.newIndex];
|
||||
let oldItem = this.keyValues[env.oldIndex];
|
||||
this.$set(this.keyValues, env.oldIndex, oldItem);
|
||||
this.$set(this.keyValues, env.newIndex, newItem)
|
||||
this.parameters.forEach(item => {
|
||||
this.parameters.splice(0);
|
||||
})
|
||||
this.keyValues.forEach(item => {
|
||||
this.parameters.push(item);
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.parameters.length === 0 || this.parameters[this.parameters.length - 1].name) {
|
||||
|
@ -230,7 +231,6 @@
|
|||
contentType: 'text/plain'
|
||||
}));
|
||||
}
|
||||
this.keyValues = this.parameters;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
|
||||
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
|
||||
|
||||
<ms-api-request-form :showScript="true" :is-read-only="isReadOnly" :headers="apiCase.request.headers " :request="apiCase.request" v-if="api.protocol==='HTTP'"/>
|
||||
<ms-api-request-form :isShowEnable="true" :showScript="true" :is-read-only="isReadOnly" :headers="apiCase.request.headers " :request="apiCase.request" v-if="api.protocol==='HTTP'"/>
|
||||
<ms-tcp-basis-parameters :showScript="true" :request="apiCase.request" v-if="api.protocol==='TCP'"/>
|
||||
<ms-sql-basis-parameters :showScript="true" :request="apiCase.request" v-if="api.protocol==='SQL'"/>
|
||||
<ms-dubbo-basis-parameters :showScript="true" :request="apiCase.request" v-if="api.protocol==='DUBBO'"/>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
|
||||
<!-- HTTP 请求参数 -->
|
||||
<ms-api-request-form :headers="request.headers" :request="request" :response="responseData"/>
|
||||
<ms-api-request-form :isShowEnable="true" :headers="request.headers" :request="request" :response="responseData"/>
|
||||
</el-form>
|
||||
<!-- HTTP 请求返回数据 -->
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
|
|
|
@ -49,8 +49,13 @@
|
|||
},
|
||||
methods: {
|
||||
validateSocket(socket) {
|
||||
// if (!socket) return true;
|
||||
if (socket !== ''){
|
||||
if (!socket) {
|
||||
this.httpConfig.domain = socket;
|
||||
this.httpConfig.port = '';
|
||||
this.httpConfig.socket = socket;
|
||||
return true;
|
||||
}
|
||||
|
||||
let urlStr = this.httpConfig.protocol + '://' + socket;
|
||||
let url = {};
|
||||
try {
|
||||
|
@ -67,13 +72,6 @@
|
|||
this.httpConfig.socket = this.httpConfig.domain + path;
|
||||
}
|
||||
return true;
|
||||
}else {
|
||||
this.httpConfig.domain = socket;
|
||||
this.httpConfig.port = '';
|
||||
this.httpConfig.socket = socket;
|
||||
return true;
|
||||
}
|
||||
|
||||
},
|
||||
validate() {
|
||||
let isValidate = false;
|
||||
|
|
|
@ -317,6 +317,15 @@
|
|||
case 'coverage':
|
||||
this.condition.apiCaseCoverage = 'coverage';
|
||||
break;
|
||||
case 'Prepare':
|
||||
this.condition.filters.status = [this.selectDataRange];
|
||||
break;
|
||||
case 'Completed':
|
||||
this.condition.filters.status = [this.selectDataRange];
|
||||
break;
|
||||
case 'Underway':
|
||||
this.condition.filters.status = [this.selectDataRange];
|
||||
break;
|
||||
}
|
||||
if (this.condition.projectId) {
|
||||
this.result = this.$post("/api/definition/list/" + this.currentPage + "/" + this.pageSize, this.condition, response => {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<ms-tag v-if="value === 'Prepare'" type="info" effect="plain" :content="$t('test_track.plan.plan_status_prepare')"/>
|
||||
<ms-tag v-if="value === 'Underway'" type="warning" effect="plain" :content="$t('test_track.plan.plan_status_running')"/>
|
||||
<ms-tag v-if="value === 'Completed'" type="success" effect="plain" :content="$t('test_track.plan.plan_status_completed')"/>
|
||||
<ms-tag v-if="value === 'Trash'" type="danger" effect="plain" content="废弃"/>
|
||||
<ms-tag v-if="value === 'Trash'" type="danger" effect="plain" :content="$t('test_track.plan.plan_status_trash')"/>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
|
||||
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
|
||||
<!-- HTTP 请求参数 -->
|
||||
<ms-api-request-form :headers="api.request.headers" :request="api.request"/>
|
||||
<ms-api-request-form :isShowEnable="true" :headers="api.request.headers" :request="api.request"/>
|
||||
|
||||
</el-form>
|
||||
<!--返回结果-->
|
||||
|
|
|
@ -1024,9 +1024,9 @@ export class LoopController extends Controller {
|
|||
this.type = "LoopController";
|
||||
this.active = false;
|
||||
this.loopType = "LOOP_COUNT";
|
||||
this.countController = {loops: 0, interval: 0, proceed: true};
|
||||
this.forEachController = {inputVal: "", returnVal: "", interval: 0};
|
||||
this.whileController = {variable: "", operator: "", value: "", timeout: 0};
|
||||
this.countController = {loops: 0, interval: 0, proceed: true, requestResult: {}};
|
||||
this.forEachController = {inputVal: "", returnVal: "", interval: 0, requestResult: {}};
|
||||
this.whileController = {variable: "", operator: "", value: "", timeout: 0, requestResult: {}};
|
||||
this.hashTree = [];
|
||||
this.set(options);
|
||||
}
|
||||
|
|
|
@ -64,11 +64,11 @@ export class HttpConfig extends BaseConfig {
|
|||
constructor(options = {}) {
|
||||
super();
|
||||
|
||||
this.socket = undefined;
|
||||
this.domain = undefined;
|
||||
this.socket = '';
|
||||
this.domain = '';
|
||||
this.headers = [];
|
||||
this.protocol = 'https';
|
||||
this.port = undefined;
|
||||
this.port = '';
|
||||
|
||||
this.set(options);
|
||||
this.sets({headers: KeyValue}, options);
|
||||
|
|
|
@ -93,11 +93,13 @@ export default {
|
|||
],
|
||||
isCodeEditAlive: true,
|
||||
languages: [
|
||||
'beanshell', "python"
|
||||
'beanshell', "python", "groovy", "javascript"
|
||||
],
|
||||
codeEditModeMap: {
|
||||
beanshell: 'java',
|
||||
python: 'python'
|
||||
python: 'python',
|
||||
groovy: 'java',
|
||||
javascript: 'javascript',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
import MsDubboRegistryCenter from "@/business/components/api/test/components/request/dubbo/RegistryCenter";
|
||||
import MsDubboConfigCenter from "@/business/components/api/test/components/request/dubbo/ConfigCenter";
|
||||
import MsDubboConsumerService from "@/business/components/api/test/components/request/dubbo/ConsumerAndService";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import MsJsr233Processor from "../processor/Jsr233Processor";
|
||||
|
||||
export default {
|
||||
name: "MsApiDubboRequestForm",
|
||||
|
|
|
@ -94,7 +94,7 @@ import ApiRequestMethodSelect from "../collapse/ApiRequestMethodSelect";
|
|||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import MsApiVariable from "@/business/components/api/test/components/ApiVariable";
|
||||
import MsApiAdvancedConfig from "../ApiAdvancedConfig";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import MsJsr233Processor from "../processor/Jsr233Processor";
|
||||
|
||||
export default {
|
||||
name: "MsApiHttpRequestForm",
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
import MsDubboConsumerService from "@/business/components/api/test/components/request/dubbo/ConsumerAndService";
|
||||
import MsCodeEdit from "../../../../common/components/MsCodeEdit";
|
||||
import MsApiScenarioVariables from "../ApiScenarioVariables";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import MsJsr233Processor from "../processor/Jsr233Processor";
|
||||
|
||||
export default {
|
||||
name: "MsApiSqlRequestForm",
|
||||
|
|
|
@ -120,7 +120,7 @@ import {Scenario, TCPConfig, TCPRequest} from "@/business/components/api/test/mo
|
|||
import MsApiAssertions from "@/business/components/api/test/components/assertion/ApiAssertions";
|
||||
import MsApiExtract from "@/business/components/api/test/components/extract/ApiExtract";
|
||||
import MsCodeEdit from "@/business/components/common/components/MsCodeEdit";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import MsJsr233Processor from "../processor/Jsr233Processor";
|
||||
|
||||
export default {
|
||||
name: "MsApiTcpRequestForm",
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
<el-row>
|
||||
<el-col :span="4">
|
||||
<span>
|
||||
{{operationType == 'edit' ? ( readOnly ? $t('test_track.case.view_case') : $t('test_track.case.edit_case')) : $t('test_track.case.create')}}
|
||||
{{
|
||||
operationType == 'edit' ? (readOnly ? $t('test_track.case.view_case') : $t('test_track.case.edit_case')) : $t('test_track.case.create')
|
||||
}}
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col class="head-right" :span="19">
|
||||
|
@ -82,7 +84,7 @@
|
|||
<el-row>
|
||||
<el-col :span="10" :offset="1">
|
||||
<el-form-item :label="$t('commons.tag')" :label-width="formLabelWidth" prop="tag">
|
||||
<ms-input-tag :currentScenario="form" ref="tag"/>
|
||||
<ms-input-tag :currentScenario="form" v-if="showInputTag" ref="tag"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -318,6 +320,7 @@ export default {
|
|||
result: ''
|
||||
}],
|
||||
remark: '',
|
||||
tags: [],
|
||||
},
|
||||
moduleOptions: [],
|
||||
maintainerOptions: [],
|
||||
|
@ -350,7 +353,8 @@ export default {
|
|||
],
|
||||
testCase: {},
|
||||
testCases: [],
|
||||
index: 0
|
||||
index: 0,
|
||||
showInputTag: true,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -416,6 +420,7 @@ export default {
|
|||
this.form.type = 'functional';
|
||||
this.form.method = 'manual';
|
||||
this.form.maintainer = user.id;
|
||||
this.form.tags = [];
|
||||
this.getSelectOptions();
|
||||
this.reload();
|
||||
}
|
||||
|
@ -441,6 +446,7 @@ export default {
|
|||
});
|
||||
},
|
||||
getTestCase(index) {
|
||||
this.showInputTag = false;
|
||||
let testCase = this.testCases[index];
|
||||
this.result = this.$get('/test/case/get/' + testCase.id, response => {
|
||||
let testCase = response.data;
|
||||
|
@ -448,6 +454,9 @@ export default {
|
|||
this.setTestCaseExtInfo(testCase);
|
||||
this.getSelectOptions();
|
||||
this.reload();
|
||||
this.$nextTick(() => {
|
||||
this.showInputTag = true
|
||||
})
|
||||
})
|
||||
},
|
||||
setFormData(testCase) {
|
||||
|
@ -459,7 +468,7 @@ export default {
|
|||
this.form.module = testCase.nodeId;
|
||||
this.getFileMetaData(testCase);
|
||||
},
|
||||
setTestCaseExtInfo (testCase) {
|
||||
setTestCaseExtInfo(testCase) {
|
||||
this.testCase = {};
|
||||
if (testCase) {
|
||||
// 复制 不查询评论
|
||||
|
|
|
@ -137,12 +137,6 @@ export default {
|
|||
return false;
|
||||
},
|
||||
filter(val) {
|
||||
if (!val) {
|
||||
val = this.filterText;
|
||||
} else {
|
||||
// 记录condition 的 filterText
|
||||
this.filterText = val;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.filter(val);
|
||||
});
|
||||
|
|
|
@ -3,12 +3,14 @@
|
|||
<ms-tag v-if="value == 'Prepare'" type="info" :content="$t('test_track.plan.plan_status_prepare')"/>
|
||||
<ms-tag v-if="value == 'Underway'" type="primary" :content="$t('test_track.plan.plan_status_running')"/>
|
||||
<ms-tag v-if="value == 'Completed'" type="success" :content="$t('test_track.plan.plan_status_completed')"/>
|
||||
<ms-tag v-if="value === 'Trash'" type="danger" effect="plain" :content="$t('test_track.plan.plan_status_trash')"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTag from "../../../../common/components/MsTag";
|
||||
export default {
|
||||
import MsTag from "../../../../common/components/MsTag";
|
||||
|
||||
export default {
|
||||
name: "PlanStatusTableItem",
|
||||
components: {MsTag},
|
||||
props: {
|
||||
|
|
|
@ -75,6 +75,9 @@
|
|||
<el-tag size="mini" type="success" v-else-if="row.caseStatus === 'success'">
|
||||
{{ row.caseStatus }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" v-else-if="row.caseStatus === 'run'">
|
||||
{{ row.caseStatus }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -264,17 +267,22 @@ export default {
|
|||
title: loadCase.caseName,
|
||||
message: this.$t('test_track.plan.load_case.exec').toString()
|
||||
});
|
||||
this.initTable();
|
||||
this.updateStatus(loadCase, 'run');
|
||||
}).catch(() => {
|
||||
this.$post('/test/plan/load/case/update', {id: loadCase.id, status: "error"}, () => {
|
||||
this.initTable();
|
||||
});
|
||||
this.updateStatus(loadCase, 'error');
|
||||
this.$notify.error({
|
||||
title: loadCase.caseName,
|
||||
message: this.$t('test_track.plan.load_case.error').toString()
|
||||
});
|
||||
})
|
||||
},
|
||||
updateStatus(loadCase, status) {
|
||||
this.$post('/test/plan/load/case/update', {id: loadCase.id, status: status}, () => {
|
||||
this.$post('/test/plan/edit/status/' + loadCase.testPlanId, {},() => {
|
||||
this.initTable();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete(loadCase) {
|
||||
this.result = this.$get('/test/plan/load/case/delete/' + loadCase.id, () => {
|
||||
this.$success(this.$t('test_track.cancel_relevance_success'));
|
||||
|
@ -284,15 +292,15 @@ export default {
|
|||
},
|
||||
sort(column) {
|
||||
// 每次只对一个字段排序
|
||||
// if (this.condition.orders) {
|
||||
// this.condition.orders = [];
|
||||
// }
|
||||
// _sort(column, this.condition);
|
||||
// this.initTable();
|
||||
if (this.condition.orders) {
|
||||
this.condition.orders = [];
|
||||
}
|
||||
_sort(column, this.condition);
|
||||
this.initTable();
|
||||
},
|
||||
filter(filters) {
|
||||
// _filter(filters, this.condition);
|
||||
// this.initTable();
|
||||
_filter(filters, this.condition);
|
||||
this.initTable();
|
||||
},
|
||||
getReport(data) {
|
||||
const {loadReportId} = data;
|
||||
|
|
|
@ -5,8 +5,25 @@
|
|||
@search="initTableData"
|
||||
:title="$t('test_track.report.name')"/>
|
||||
</template>
|
||||
<el-table border class="adjust-table" :data="tableData"
|
||||
@filter-change="filter" @sort-change="sort">
|
||||
<el-table border :data="tableData"
|
||||
@select-all="handleSelectAll"
|
||||
@select="handleSelect"
|
||||
row-key="id" class="test-content adjust-table ms-select-all"
|
||||
@filter-change="filter" @sort-change="sort" ref="testPlanReportTable">
|
||||
|
||||
<el-table-column width="50" type="selection"/>
|
||||
<ms-table-select-all
|
||||
:page-size="pageSize>total?total:pageSize"
|
||||
:total="total"
|
||||
@selectPageAll="isSelectDataAll(false)"
|
||||
@selectAll="isSelectDataAll(true)"/>
|
||||
|
||||
<el-table-column width="30" :resizable="false" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column min-width="300" prop="name" :label="$t('test_track.report.list.name')" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="testPlanName" sortable :label="$t('test_track.report.list.test_plan')" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="creator" :label="$t('test_track.report.list.creator')" show-overflow-tooltip></el-table-column>
|
||||
|
@ -20,6 +37,13 @@
|
|||
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="scope">
|
||||
<ms-tag v-if="scope.row.status == 'RUNNING'" type="success" effect="plain" :content="'Running'"/>
|
||||
<ms-tag v-else-if="scope.row.status == 'COMPLETED'||scope.row.status == 'SUCCESS'||scope.row.status == 'FAILED'" type="info" effect="plain" :content="'Completed'"/>
|
||||
<ms-tag v-else type="effect" effect="plain" :content="scope.row.status"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="150" :label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<ms-table-operator-button :tip="$t('test_track.plan_view.view_report')" icon="el-icon-document"
|
||||
|
@ -45,13 +69,17 @@ import {TEST_PLAN_REPORT_CONFIGS} from "../../../common/components/search/search
|
|||
import {getCurrentProjectID} from "../../../../../common/js/utils";
|
||||
import TestPlanReportView from "@/business/components/track/report/components/TestPlanReportView";
|
||||
import ReportTriggerModeItem from "@/business/components/common/tableItem/ReportTriggerModeItem";
|
||||
import MsTag from "@/business/components/common/components/MsTag";
|
||||
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
|
||||
import MsTableSelectAll from "@/business/components/common/components/table/MsTableSelectAll";
|
||||
|
||||
export default {
|
||||
name: "TestPlanReportList",
|
||||
components: {
|
||||
TestPlanReportView,
|
||||
MsTableOperator, MsTableOperatorButton, MsTableHeader, MsTablePagination,
|
||||
ReportTriggerModeItem
|
||||
ReportTriggerModeItem,MsTag,
|
||||
ShowMoreBtn,MsTableSelectAll,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -64,6 +92,7 @@ export default {
|
|||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
isTestManagerOrTestUser: false,
|
||||
selectRows: new Set(),
|
||||
total: 0,
|
||||
tableData: [],
|
||||
statusFilters: [
|
||||
|
@ -76,13 +105,16 @@ export default {
|
|||
{text: this.$t('test_track.plan.system_test'), value: 'system'},
|
||||
{text: this.$t('test_track.plan.regression_test'), value: 'regression'},
|
||||
],
|
||||
buttons: [
|
||||
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
|
||||
],
|
||||
selectAll: false,
|
||||
unSelection: [],
|
||||
selectDataCounts: 0,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route'(to, from) {
|
||||
// if (to.path.indexOf("/track/plan/all") >= 0) {
|
||||
// this.initTableData();
|
||||
// }
|
||||
}
|
||||
},
|
||||
activated() {
|
||||
|
@ -95,6 +127,10 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
initTableData() {
|
||||
this.selectRows = new Set();
|
||||
this.selectAll = false;
|
||||
this.unSelection = [];
|
||||
this.selectDataCounts = 0;
|
||||
if (this.planId) {
|
||||
this.condition.planId = this.planId;
|
||||
}
|
||||
|
@ -108,19 +144,52 @@ export default {
|
|||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
this.tableData = data.listObject;
|
||||
// for (let i = 0; i < this.tableData.length; i++) {
|
||||
// let path = "/test/plan/project";
|
||||
// this.$post(path, {planId: this.tableData[i].id}, res => {
|
||||
// let arr = res.data;
|
||||
// let projectIds = arr.filter(d => d.id !== this.tableData[i].projectId).map(data => data.id);
|
||||
// this.$set(this.tableData[i], "projectIds", projectIds);
|
||||
// })
|
||||
// }
|
||||
this.unSelection = data.listObject.map(s => s.id);
|
||||
});
|
||||
},
|
||||
buildPagePath(path) {
|
||||
return path + "/" + this.currentPage + "/" + this.pageSize;
|
||||
},
|
||||
handleSelect(selection, row) {
|
||||
row.hashTree = [];
|
||||
if (this.selectRows.has(row)) {
|
||||
this.$set(row, "showMore", false);
|
||||
this.selectRows.delete(row);
|
||||
} else {
|
||||
this.$set(row, "showMore", true);
|
||||
this.selectRows.add(row);
|
||||
}
|
||||
let arr = Array.from(this.selectRows);
|
||||
// 选中1个以上的用例时显示更多操作
|
||||
if (this.selectRows.size === 1) {
|
||||
this.$set(arr[0], "showMore", true);
|
||||
} else if (this.selectRows.size === 2) {
|
||||
arr.forEach(row => {
|
||||
this.$set(row, "showMore", true);
|
||||
})
|
||||
}
|
||||
this.selectRowsCount(this.selectRows)
|
||||
},
|
||||
handleSelectAll(selection) {
|
||||
if (selection.length > 0) {
|
||||
if (selection.length === 1) {
|
||||
selection.hashTree = [];
|
||||
this.selectRows.add(selection[0]);
|
||||
} else {
|
||||
this.tableData.forEach(item => {
|
||||
item.hashTree = [];
|
||||
this.$set(item, "showMore", true);
|
||||
this.selectRows.add(item);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
this.selectRows.clear();
|
||||
this.tableData.forEach(row => {
|
||||
this.$set(row, "showMore", false);
|
||||
})
|
||||
}
|
||||
this.selectRowsCount(this.selectRows)
|
||||
},
|
||||
handleDelete(testPlanReport) {
|
||||
this.$alert(this.$t('report.delete_confirm') + ' ' + testPlanReport.name + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
|
@ -135,7 +204,32 @@ export default {
|
|||
}
|
||||
});
|
||||
},
|
||||
|
||||
handleDeleteBatch(){
|
||||
this.$alert(this.$t('report.delete_batch_confirm') + ' ' + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
let deleteParam = {};
|
||||
let ids = Array.from(this.selectRows).map(row => row.id);
|
||||
deleteParam.dataIds = ids;
|
||||
deleteParam.projectId = getCurrentProjectID();
|
||||
deleteParam.selectAllDate = this.isSelectAllDate;
|
||||
deleteParam.unSelectIds = this.unSelection;
|
||||
deleteParam = Object.assign(deleteParam, this.condition);
|
||||
this.$post('/test/plan/report/deleteBatchByParams/', deleteParam, () => {
|
||||
this.$success(this.$t('commons.delete_success'));
|
||||
this.initTableData();
|
||||
this.selectRows.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
getIds(rowSets) {
|
||||
let rowArray = Array.from(rowSets)
|
||||
let ids = rowArray.map(s => s.id);
|
||||
return ids;
|
||||
},
|
||||
filter(filters) {
|
||||
_filter(filters, this.condition);
|
||||
this.initTableData();
|
||||
|
@ -149,6 +243,26 @@ export default {
|
|||
this.$refs.testPlanReportView.open(planId);
|
||||
}
|
||||
},
|
||||
isSelectDataAll(dataType) {
|
||||
this.isSelectAllDate = dataType;
|
||||
this.selectRowsCount(this.selectRows)
|
||||
//如果已经全选,不需要再操作了
|
||||
if (this.selectRows.size != this.tableData.length) {
|
||||
this.$refs.testPlanReportTable.toggleAllSelection(true);
|
||||
}
|
||||
},
|
||||
selectRowsCount(selection) {
|
||||
let selectedIDs = this.getIds(selection);
|
||||
let allIDs = this.tableData.map(s => s.id);
|
||||
this.unSelection = allIDs.filter(function (val) {
|
||||
return selectedIDs.indexOf(val) === -1
|
||||
});
|
||||
if (this.isSelectAllDate) {
|
||||
this.selectDataCounts = this.total - this.unSelection.length;
|
||||
} else {
|
||||
this.selectDataCounts = selection.size;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
@ -164,4 +278,22 @@ export default {
|
|||
.el-table {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.operate-button > div {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.request-method {
|
||||
padding: 0 5px;
|
||||
color: #1E90FF;
|
||||
}
|
||||
|
||||
.ms-select-all >>> th:first-child {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.ms-select-all >>> th:nth-child(2) .el-icon-arrow-down {
|
||||
top: -2px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 8a6a9ae708306eaf436f35394b71927cec075b0e
|
||||
Subproject commit 5abe43dc1f65b529ad59c17bfdc58aea33d23cad
|
|
@ -129,6 +129,7 @@ export default {
|
|||
validate: "Validate",
|
||||
batch_add: "Batch add",
|
||||
check_project_tip: "Create or select the project first",
|
||||
auth_redirect_tip: 'Jump to the authentication source page for authentication',
|
||||
table: {
|
||||
select_tip: "Item {0} data is selected"
|
||||
},
|
||||
|
@ -831,8 +832,8 @@ export default {
|
|||
swagger_export_tip: "Export jSON-formatted files via Swagger website",
|
||||
suffixFormatErr: "The file format does not meet the requirements",
|
||||
swagger_url_import: "Import using URL",
|
||||
timing_synchronization:"Timing synchronization",
|
||||
next_synchronization_time:"Next synchronization time",
|
||||
timing_synchronization: "Timing synchronization",
|
||||
next_synchronization_time: "Next synchronization time",
|
||||
|
||||
},
|
||||
home_page: {
|
||||
|
@ -1493,6 +1494,6 @@ export default {
|
|||
format: "Output format",
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt:'This operation will delete the authentication source, do you want to continue? '
|
||||
delete_prompt: 'This operation will delete the authentication source, do you want to continue? '
|
||||
}
|
||||
};
|
||||
|
|
|
@ -129,6 +129,7 @@ export default {
|
|||
validate: "校验",
|
||||
batch_add: "批量添加",
|
||||
check_project_tip: "请先创建或选择项目",
|
||||
auth_redirect_tip: '即将跳转到认证源页面进行认证',
|
||||
table: {
|
||||
select_tip: "已选中 {0} 条数据"
|
||||
},
|
||||
|
@ -833,8 +834,8 @@ export default {
|
|||
swagger_export_tip: "通过 Swagger 页面导出",
|
||||
suffixFormatErr: "文件格式不符合要求",
|
||||
swagger_url_import: "使用URL导入",
|
||||
timing_synchronization:"定时同步",
|
||||
next_synchronization_time:"下次同步时间"
|
||||
timing_synchronization: "定时同步",
|
||||
next_synchronization_time: "下次同步时间"
|
||||
|
||||
|
||||
},
|
||||
|
|
|
@ -129,6 +129,7 @@ export default {
|
|||
validate: "校驗",
|
||||
batch_add: "批量添加",
|
||||
check_project_tip: "請先創建或選擇項目",
|
||||
auth_redirect_tip: '即將跳轉到認證源頁面進行認證',
|
||||
table: {
|
||||
select_tip: "已选中 {0} 条数据"
|
||||
},
|
||||
|
@ -256,8 +257,8 @@ export default {
|
|||
nail_robot: '釘釘機器人',
|
||||
enterprise_wechat_robot: '企業微信機器人',
|
||||
notes: '1.釘釘和企業群裏新建壹個自定義機器人,然後復制 webhook 地址在我們平臺上;\n' +
|
||||
' 2.機器人選擇為群機器人,安全驗證選擇“自定義關鍵詞” :"任務通知";\n' +
|
||||
' 3.選擇接收人時必須是妳所建的群裏包含的人,接收人手機號為必填項且為釘釘企業所使用的手機號,',
|
||||
' 2.機器人選擇為群機器人,安全驗證選擇“自定義關鍵詞” :"任務通知";\n' +
|
||||
' 3.選擇接收人時必須是妳所建的群裏包含的人,接收人手機號為必填項且為釘釘企業所使用的手機號,',
|
||||
message: '事件,接收人,接收方式為必填項',
|
||||
message_webhook: '接收方式為釘釘和企業機器人時,webhook為必填項',
|
||||
template: "模版"
|
||||
|
@ -832,8 +833,8 @@ export default {
|
|||
swagger_export_tip: "通過 Swagger 頁面導出",
|
||||
suffixFormatErr: "文件格式不符合要求",
|
||||
swagger_url_import: "使用URL導入",
|
||||
timing_synchronization:"定時同步",
|
||||
next_synchronization_time:"下次同步時間",
|
||||
timing_synchronization: "定時同步",
|
||||
next_synchronization_time: "下次同步時間",
|
||||
|
||||
},
|
||||
home_page: {
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
</div>
|
||||
<div class="form">
|
||||
<el-form-item v-slot:default>
|
||||
<el-radio-group v-model="form.authenticate">
|
||||
<el-radio-group v-model="form.authenticate" @change="redirectAuth(form.authenticate)">
|
||||
<el-radio label="LDAP" size="mini" v-if="openLdap">LDAP</el-radio>
|
||||
<el-radio label="LOCAL" size="mini" v-if="openLdap">普通登录</el-radio>
|
||||
<el-radio :label="auth.id" size="mini" v-for="auth in authSources" :key="auth.id">{{ auth.type }} {{ auth.name }}</el-radio>
|
||||
|
@ -172,6 +172,11 @@ export default {
|
|||
} else {
|
||||
window.location.href = "/"
|
||||
}
|
||||
},
|
||||
redirectAuth(authId) {
|
||||
if (auth.default) {
|
||||
auth.default.redirectAuth(this, authId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue