refactor(接口测试): 开源失败重跑和失败重试及生成测试数据功能
--task=1011041 --user=赵勇 【开源计划】失败重试... https://www.tapd.cn/55049933/s/1327959 --task=1011040 --user=赵勇 【开源计划】自动生成... https://www.tapd.cn/55049933/s/1327961
This commit is contained in:
parent
2e0b010fe2
commit
a0326c34e3
|
@ -0,0 +1,72 @@
|
||||||
|
package io.metersphere.api.dto.definition.request.controller;
|
||||||
|
|
||||||
|
import io.metersphere.plugin.core.MsParameter;
|
||||||
|
import io.metersphere.plugin.core.MsTestElement;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.jmeter.control.WhileController;
|
||||||
|
import org.apache.jmeter.save.SaveService;
|
||||||
|
import org.apache.jmeter.testelement.TestElement;
|
||||||
|
import org.apache.jmeter.visualizers.JSR223Listener;
|
||||||
|
import org.apache.jorphan.collections.HashTree;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
public class MsRetryLoopController extends MsTestElement {
|
||||||
|
private String type = "RetryLoopController";
|
||||||
|
private String clazzName = MsRetryLoopController.class.getCanonicalName();
|
||||||
|
|
||||||
|
private long retryNum;
|
||||||
|
|
||||||
|
private String ms_current_timer = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, MsParameter msParameter) {
|
||||||
|
final HashTree groupTree = controller(tree);
|
||||||
|
|
||||||
|
if (CollectionUtils.isNotEmpty(hashTree)) {
|
||||||
|
hashTree.forEach(el -> {
|
||||||
|
// 给所有孩子加一个父亲标志
|
||||||
|
el.setParent(this);
|
||||||
|
el.toHashTree(groupTree, el.getHashTree(), msParameter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WhileController initWhileController(String condition) {
|
||||||
|
if (StringUtils.isEmpty(condition)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
WhileController controller = new WhileController();
|
||||||
|
controller.setEnabled(this.isEnable());
|
||||||
|
controller.setName("WhileController");
|
||||||
|
controller.setProperty(TestElement.TEST_CLASS, WhileController.class.getName());
|
||||||
|
controller.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("WhileControllerGui"));
|
||||||
|
controller.setCondition(condition);
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String script() {
|
||||||
|
String script = "// 失败重试控制\n" + "try{\n" + "\tint errorCount = prev.getErrorCount();\n" + "\tif(errorCount == 0 && prev.getFirstAssertionFailureMessage() == null ){\n" + "\t vars.put(\"" + ms_current_timer + "\", \"stop\");\n" + "\t}\n" + "\tif(vars.get(\"" + ms_current_timer + "_num\") == null){\n" + "\t\tvars.put(\"" + ms_current_timer + "_num\", \"0\");\n" + "\t}else{\n" + "\t\tint retryNum= Integer.parseInt(vars.get(\"" + ms_current_timer + "_num\"));\n" + "\t\tlog.info(\"重试:\"+ retryNum);\n" + " \tprev.setSampleLabel(\"MsRetry_\"+ (retryNum + 1) + \"_\" + prev.getSampleLabel());\n" + "\t\tretryNum =retryNum +1;\n" + "\t\tvars.put(\"" + ms_current_timer + "_num\",retryNum + \"\");\n" + "\t}\n" + "\tif(vars.get(\"" + ms_current_timer + "_num\").equals( \"" + retryNum + "\")){\n" + "\t\tvars.put(\"" + ms_current_timer + "\", \"stop\");\n" + "\t}\n" + "}catch (Exception e){\n" + "\tvars.put(\"" + ms_current_timer + "\", \"stop\");\n" + "}\n";
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashTree controller(HashTree tree) {
|
||||||
|
String whileCondition = "${__jexl3(" + "\"${" + ms_current_timer + "}\" !=\"stop\")}";
|
||||||
|
HashTree hashTree = tree.add(initWhileController(whileCondition));
|
||||||
|
// 添加超时处理,防止死循环
|
||||||
|
JSR223Listener postProcessor = new JSR223Listener();
|
||||||
|
postProcessor.setName("Retry-controller");
|
||||||
|
postProcessor.setProperty(TestElement.TEST_CLASS, JSR223Listener.class.getName());
|
||||||
|
postProcessor.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
|
||||||
|
postProcessor.setProperty("scriptLanguage", "beanshell");
|
||||||
|
postProcessor.setProperty("script", script());
|
||||||
|
hashTree.add(postProcessor);
|
||||||
|
return hashTree;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,7 @@ import io.metersphere.plugin.core.MsTestElement;
|
||||||
import io.metersphere.service.ApiExecutionQueueService;
|
import io.metersphere.service.ApiExecutionQueueService;
|
||||||
import io.metersphere.service.RemakeReportService;
|
import io.metersphere.service.RemakeReportService;
|
||||||
import io.metersphere.utils.LoggerUtil;
|
import io.metersphere.utils.LoggerUtil;
|
||||||
import io.metersphere.xpack.api.service.ApiRetryOnFailureService;
|
import io.metersphere.service.ApiRetryOnFailureService;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.jorphan.collections.HashTree;
|
import org.apache.jorphan.collections.HashTree;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
@ -59,6 +59,8 @@ public class ApiCaseSerialService {
|
||||||
private RedisTemplate<String, Object> redisTemplate;
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
@Resource
|
@Resource
|
||||||
private TestPlanApiCaseMapper testPlanApiCaseMapper;
|
private TestPlanApiCaseMapper testPlanApiCaseMapper;
|
||||||
|
@Resource
|
||||||
|
private ApiRetryOnFailureService apiRetryOnFailureService;
|
||||||
|
|
||||||
public void serial(DBTestQueue executionQueue) {
|
public void serial(DBTestQueue executionQueue) {
|
||||||
ApiExecutionQueueDetail queue = executionQueue.getDetail();
|
ApiExecutionQueueDetail queue = executionQueue.getDetail();
|
||||||
|
@ -154,7 +156,6 @@ public class ApiCaseSerialService {
|
||||||
String data = element.toString();
|
String data = element.toString();
|
||||||
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
|
if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) {
|
||||||
// 失败重试
|
// 失败重试
|
||||||
ApiRetryOnFailureService apiRetryOnFailureService = CommonBeanFactory.getBean(ApiRetryOnFailureService.class);
|
|
||||||
String retryData = apiRetryOnFailureService.retry(data, runRequest.getRetryNum(), true);
|
String retryData = apiRetryOnFailureService.retry(data, runRequest.getRetryNum(), true);
|
||||||
data = StringUtils.isNotEmpty(retryData) ? retryData : data;
|
data = StringUtils.isNotEmpty(retryData) ? retryData : data;
|
||||||
// 格式化数据
|
// 格式化数据
|
||||||
|
|
|
@ -23,7 +23,7 @@ import io.metersphere.service.ApiExecutionQueueService;
|
||||||
import io.metersphere.service.RemakeReportService;
|
import io.metersphere.service.RemakeReportService;
|
||||||
import io.metersphere.utils.LoggerUtil;
|
import io.metersphere.utils.LoggerUtil;
|
||||||
import io.metersphere.vo.BooleanPool;
|
import io.metersphere.vo.BooleanPool;
|
||||||
import io.metersphere.xpack.api.service.ApiRetryOnFailureService;
|
import io.metersphere.service.ApiRetryOnFailureService;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.jorphan.collections.HashTree;
|
import org.apache.jorphan.collections.HashTree;
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package io.metersphere.controller;
|
||||||
|
|
||||||
|
import io.metersphere.service.TestDataGenerator;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping(value = "/api/test/data")
|
||||||
|
public class TestDataController {
|
||||||
|
@PostMapping("/generator")
|
||||||
|
public String preview(@RequestBody String jsonSchema) {
|
||||||
|
return TestDataGenerator.generator(jsonSchema);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
package io.metersphere.service;
|
||||||
|
|
||||||
|
import io.metersphere.api.dto.definition.request.controller.MsRetryLoopController;
|
||||||
|
import io.metersphere.commons.utils.JSON;
|
||||||
|
import io.metersphere.commons.utils.JSONUtil;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
import io.metersphere.plugin.core.MsTestElement;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ApiRetryOnFailureService {
|
||||||
|
public final static List<String> requests = List.of(
|
||||||
|
"HTTPSamplerProxy",
|
||||||
|
"DubboSampler",
|
||||||
|
"JDBCSampler",
|
||||||
|
"TCPSampler",
|
||||||
|
"JSR223Processor");
|
||||||
|
|
||||||
|
private final static String HASH_TREE_ELEMENT = "hashTree";
|
||||||
|
private final static String TYPE = "type";
|
||||||
|
private final static String RESOURCE_ID = "resourceId";
|
||||||
|
private final static String RETRY = "MsRetry_";
|
||||||
|
private final static String LOOP = "LoopController";
|
||||||
|
|
||||||
|
public String retry(String data, long retryNum, boolean isCase) {
|
||||||
|
if (StringUtils.isNotEmpty(data)) {
|
||||||
|
JSONObject element = JSONUtil.parseObject(data);
|
||||||
|
if (element != null && isCase) {
|
||||||
|
return formatSampler(element, retryNum).toString();
|
||||||
|
}
|
||||||
|
if (element != null && element.has(HASH_TREE_ELEMENT) && !StringUtils.equalsIgnoreCase(element.optString(TYPE), LOOP)) {
|
||||||
|
JSONArray hashTree = element.getJSONArray(HASH_TREE_ELEMENT);
|
||||||
|
setRetry(hashTree, retryNum);
|
||||||
|
}
|
||||||
|
return element.toString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MsTestElement retryParse(String data) {
|
||||||
|
try {
|
||||||
|
MsRetryLoopController controller = JSON.parseObject(data, MsRetryLoopController.class);
|
||||||
|
return controller;
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtil.error(e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRetry(JSONArray hashTree, long retryNum) {
|
||||||
|
for (int i = 0; i < hashTree.length(); i++) {
|
||||||
|
JSONObject element = hashTree.getJSONObject(i);
|
||||||
|
if (StringUtils.equalsIgnoreCase(element.optString(TYPE), LOOP)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
JSONObject whileObj = formatSampler(element, retryNum);
|
||||||
|
if (whileObj != null) {
|
||||||
|
hashTree.put(i, whileObj);
|
||||||
|
} else if (element.has(HASH_TREE_ELEMENT)) {
|
||||||
|
JSONArray elementJSONArray = element.getJSONArray(HASH_TREE_ELEMENT);
|
||||||
|
setRetry(elementJSONArray, retryNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JSONObject formatSampler(JSONObject element, long retryNum) {
|
||||||
|
if (element.has(TYPE) && requests.contains(element.optString(TYPE))) {
|
||||||
|
MsRetryLoopController loopController = new MsRetryLoopController();
|
||||||
|
loopController.setClazzName(MsRetryLoopController.class.getCanonicalName());
|
||||||
|
loopController.setName(RETRY + element.optString(RESOURCE_ID));
|
||||||
|
loopController.setRetryNum(retryNum);
|
||||||
|
loopController.setEnable(true);
|
||||||
|
loopController.setResourceId(UUID.randomUUID().toString());
|
||||||
|
|
||||||
|
JSONObject whileObj = JSONUtil.parseObject(JSON.toJSONString(loopController));
|
||||||
|
JSONArray hashTree = new JSONArray();
|
||||||
|
hashTree.put(element);
|
||||||
|
|
||||||
|
whileObj.put(HASH_TREE_ELEMENT, hashTree);
|
||||||
|
return whileObj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -201,7 +201,7 @@ public class MsHashTreeService {
|
||||||
private JSONObject setRefScenario(JSONObject element) {
|
private JSONObject setRefScenario(JSONObject element) {
|
||||||
boolean enable = element.has(ENABLE) ? element.optBoolean(ENABLE) : true;
|
boolean enable = element.has(ENABLE) ? element.optBoolean(ENABLE) : true;
|
||||||
if (!element.has(MIX_ENABLE)) {
|
if (!element.has(MIX_ENABLE)) {
|
||||||
element.put(MIX_ENABLE, true);
|
element.put(MIX_ENABLE, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ApiScenarioDTO scenarioWithBLOBs = extApiScenarioMapper.selectById(element.optString(ID));
|
ApiScenarioDTO scenarioWithBLOBs = extApiScenarioMapper.selectById(element.optString(ID));
|
||||||
|
@ -209,7 +209,7 @@ public class MsHashTreeService {
|
||||||
boolean environmentEnable = element.has(ENV_ENABLE) ? element.optBoolean(ENV_ENABLE) : false;
|
boolean environmentEnable = element.has(ENV_ENABLE) ? element.optBoolean(ENV_ENABLE) : false;
|
||||||
boolean variableEnable = element.has(VARIABLE_ENABLE) ? element.optBoolean(VARIABLE_ENABLE) : false;
|
boolean variableEnable = element.has(VARIABLE_ENABLE) ? element.optBoolean(VARIABLE_ENABLE) : false;
|
||||||
boolean mixEnable = element.has(MIX_ENABLE)
|
boolean mixEnable = element.has(MIX_ENABLE)
|
||||||
? element.getBoolean(MIX_ENABLE) : true;
|
? element.getBoolean(MIX_ENABLE) : false;
|
||||||
|
|
||||||
if (environmentEnable && StringUtils.isNotEmpty(scenarioWithBLOBs.getEnvironmentJson())) {
|
if (environmentEnable && StringUtils.isNotEmpty(scenarioWithBLOBs.getEnvironmentJson())) {
|
||||||
element.put(ENV_MAP, JSON.parseObject(scenarioWithBLOBs.getEnvironmentJson(), Map.class));
|
element.put(ENV_MAP, JSON.parseObject(scenarioWithBLOBs.getEnvironmentJson(), Map.class));
|
||||||
|
|
|
@ -0,0 +1,345 @@
|
||||||
|
package io.metersphere.service;
|
||||||
|
|
||||||
|
import com.apifan.common.random.source.DateTimeSource;
|
||||||
|
import com.apifan.common.random.source.InternetSource;
|
||||||
|
import com.apifan.common.random.source.NumberSource;
|
||||||
|
import com.google.gson.*;
|
||||||
|
import com.mifmif.common.regex.Generex;
|
||||||
|
import io.metersphere.jmeter.utils.ScriptEngineUtils;
|
||||||
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成测试数据
|
||||||
|
*/
|
||||||
|
public class TestDataGenerator {
|
||||||
|
private static final String TYPE = "type";
|
||||||
|
private static final String ALL_OF = "allOf";
|
||||||
|
private static final String DEFINITIONS = "definitions";
|
||||||
|
private static final String PROPERTIES = "properties";
|
||||||
|
private static final String ARRAY = "array";
|
||||||
|
private static final String OBJECT = "object";
|
||||||
|
private static final String MS_OBJECT = "MS-OBJECT";
|
||||||
|
private static final String ITEMS = "items";
|
||||||
|
private static final String ENUM = "enum";
|
||||||
|
private static final String STRING = "string";
|
||||||
|
private static final String DEFAULT = "default";
|
||||||
|
private static final String MOCK = "mock";
|
||||||
|
private static final String MAXLENGTH = "maxLength";
|
||||||
|
private static final String MINLENGTH = "minLength";
|
||||||
|
private static final String FORMAT = "format";
|
||||||
|
private static final String PATTERN = "pattern";
|
||||||
|
private static final String INTEGER = "integer";
|
||||||
|
private static final String NUMBER = "number";
|
||||||
|
private static final String BOOLEAN = "boolean";
|
||||||
|
private static final String MINIMUM = "minimum";
|
||||||
|
private static final String MAXIMUM = "maximum";
|
||||||
|
|
||||||
|
public static void analyzeSchema(String json, JSONObject rootObj) {
|
||||||
|
Gson gson = new Gson();
|
||||||
|
JsonElement element = gson.fromJson(json, JsonElement.class);
|
||||||
|
JsonObject rootElement = element.getAsJsonObject();
|
||||||
|
analyzeRootSchemaElement(rootElement, rootObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void analyzeRootSchemaElement(JsonObject rootElement, JSONObject rootObj) {
|
||||||
|
if ((rootElement.has(TYPE) || rootElement.has(ALL_OF)) && rootElement != null) {
|
||||||
|
analyzeObject(rootElement, rootObj);
|
||||||
|
}
|
||||||
|
if (rootElement.has(DEFINITIONS)) {
|
||||||
|
analyzeDefinitions(rootElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void analyzeObject(JsonObject object, JSONObject rootObj) {
|
||||||
|
if (object.has(ALL_OF)) {
|
||||||
|
for (JsonElement el : object.get(ALL_OF).getAsJsonArray()) {
|
||||||
|
JsonObject elObj = el.getAsJsonObject();
|
||||||
|
if (elObj.has(PROPERTIES)) {
|
||||||
|
analyzeProperties(rootObj, elObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (object.has(PROPERTIES)) {
|
||||||
|
analyzeProperties(rootObj, object);
|
||||||
|
} else if (object.has(TYPE) && object.get(TYPE).getAsString().equals(ARRAY)) {
|
||||||
|
analyzeProperty(rootObj, MS_OBJECT, object);
|
||||||
|
} else if (object.has(TYPE) && !object.get(TYPE).getAsString().equals(OBJECT)) {
|
||||||
|
analyzeProperty(rootObj, object.getAsString(), object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void analyzeProperties(JSONObject rootObj, JsonObject allOfElementObj) {
|
||||||
|
JsonObject propertiesObj = allOfElementObj.get(PROPERTIES).getAsJsonObject();
|
||||||
|
for (Entry<String, JsonElement> entry : propertiesObj.entrySet()) {
|
||||||
|
String propertyKey = entry.getKey();
|
||||||
|
JsonObject propertyObj = propertiesObj.get(propertyKey).getAsJsonObject();
|
||||||
|
analyzeProperty(rootObj, propertyKey, propertyObj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void analyzeItems(JSONObject concept, String propertyName, JsonObject object) {
|
||||||
|
// 先设置空值
|
||||||
|
List<Object> array = new LinkedList<>();
|
||||||
|
JsonArray jsonArray = new JsonArray();
|
||||||
|
if (object.has(ITEMS) && object.get(ITEMS).isJsonArray()) {
|
||||||
|
jsonArray = object.get(ITEMS).getAsJsonArray();
|
||||||
|
} else {
|
||||||
|
JsonObject itemsObject = object.get(ITEMS).getAsJsonObject();
|
||||||
|
array.add(itemsObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (JsonElement element : jsonArray) {
|
||||||
|
JsonObject itemsObject = element.getAsJsonObject();
|
||||||
|
if (object.has(ITEMS)) {
|
||||||
|
if (itemsObject.has(ENUM)) {
|
||||||
|
array.add(analyzeEnumProperty(itemsObject));
|
||||||
|
} else if (itemsObject.has(TYPE) && itemsObject.get(TYPE).getAsString().equals(STRING)) {
|
||||||
|
array.add(analyzeString(itemsObject));
|
||||||
|
} else if (itemsObject.has(PROPERTIES)) {
|
||||||
|
JSONObject propertyConcept = new JSONObject();
|
||||||
|
analyzeProperties(propertyConcept, itemsObject);
|
||||||
|
array.add(propertyConcept);
|
||||||
|
} else if (itemsObject.has(TYPE) && itemsObject.get(TYPE) instanceof JsonPrimitive) {
|
||||||
|
JSONObject newJsonObj = new JSONObject();
|
||||||
|
analyzeProperty(newJsonObj, propertyName + "_item", itemsObject);
|
||||||
|
array.add(newJsonObj.get(propertyName + "_item"));
|
||||||
|
}
|
||||||
|
} else if (object.has(ITEMS) && object.get(ITEMS).isJsonArray()) {
|
||||||
|
JsonArray itemsObjectArray = object.get(ITEMS).getAsJsonArray();
|
||||||
|
array.add(itemsObjectArray);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
concept.put(propertyName, array);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMockValue(JsonObject object) {
|
||||||
|
if (object.has(MOCK)
|
||||||
|
&& object.get(MOCK).getAsJsonObject() != null
|
||||||
|
&& object.get(MOCK).getAsJsonObject().get(MOCK) != null
|
||||||
|
&& StringUtils.isNotBlank(object.get(MOCK).getAsJsonObject().get(MOCK).getAsString())) {
|
||||||
|
if (StringUtils.startsWithAny(object.get(MOCK).getAsJsonObject().get(MOCK).getAsString(), "@", "${")) {
|
||||||
|
return ScriptEngineUtils.calculate(object.get(MOCK).getAsJsonObject().get(MOCK).getAsString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String analyzeString(JsonObject object) {
|
||||||
|
// 先设置空值
|
||||||
|
if (object.has(DEFAULT)) {
|
||||||
|
return object.get(DEFAULT).getAsString();
|
||||||
|
}
|
||||||
|
Object mockValue = getMockValue(object);
|
||||||
|
if (mockValue != null) {
|
||||||
|
return mockValue.toString();
|
||||||
|
}
|
||||||
|
int maxLength = 9;
|
||||||
|
if (object.has(MAXLENGTH)) {
|
||||||
|
maxLength = object.get(MAXLENGTH).getAsInt();
|
||||||
|
}
|
||||||
|
int minLength = 0;
|
||||||
|
if (object.has(MINLENGTH)) {
|
||||||
|
minLength = object.get(MINLENGTH).getAsInt();
|
||||||
|
}
|
||||||
|
String value = RandomStringUtils.randomAlphanumeric(minLength, maxLength);
|
||||||
|
Object enumObj = analyzeEnumProperty(object);
|
||||||
|
String v = enumObj == null ? "" : String.valueOf(enumObj);
|
||||||
|
value = StringUtils.isNotBlank(v) ? v : value;
|
||||||
|
try {
|
||||||
|
if (object.has(FORMAT)) {
|
||||||
|
String propertyFormat = object.get(FORMAT).getAsString();
|
||||||
|
switch (propertyFormat) {
|
||||||
|
case "date-time":
|
||||||
|
value = DateTimeSource.getInstance().randomTimestamp(LocalDate.now()) + "";
|
||||||
|
break;
|
||||||
|
case "date":
|
||||||
|
value = DateTimeSource.getInstance().randomDate(LocalDate.now().getYear(), "yyyy-MM-dd");
|
||||||
|
break;
|
||||||
|
case "email":
|
||||||
|
value = InternetSource.getInstance().randomEmail(maxLength);
|
||||||
|
break;
|
||||||
|
case "hostname":
|
||||||
|
value = InternetSource.getInstance().randomDomain(maxLength);
|
||||||
|
break;
|
||||||
|
case "ipv4":
|
||||||
|
value = InternetSource.getInstance().randomPublicIpv4();
|
||||||
|
break;
|
||||||
|
case "ipv6":
|
||||||
|
value = InternetSource.getInstance().randomIpV6();
|
||||||
|
break;
|
||||||
|
case "uri":
|
||||||
|
value = InternetSource.getInstance().randomStaticUrl("jpg");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (object.has(PATTERN)) {
|
||||||
|
String pattern = object.get(PATTERN).getAsString();
|
||||||
|
if (StringUtils.isNotEmpty(pattern)) {
|
||||||
|
Generex generex = new Generex(pattern);
|
||||||
|
value = generex.random();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isNumber(Object obj) {
|
||||||
|
if (ObjectUtils.isEmpty(obj)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Pattern pattern = Pattern.compile("-?[0-9]+\\.?[0-9]*");
|
||||||
|
Matcher isNum = pattern.matcher(obj.toString());
|
||||||
|
if (!isNum.matches()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object analyzeInteger(JsonObject object) {
|
||||||
|
// 先设置空值
|
||||||
|
if (object.has(DEFAULT) && isNumber(object.get(DEFAULT))) {
|
||||||
|
return object.get(DEFAULT).getAsInt();
|
||||||
|
}
|
||||||
|
Object mockValue = getMockValue(object);
|
||||||
|
if (mockValue != null && isNumber(mockValue)) {
|
||||||
|
return Integer.parseInt(mockValue.toString());
|
||||||
|
}
|
||||||
|
int minimum = 1;
|
||||||
|
int maximum = 101;
|
||||||
|
if (object.has(MINIMUM)) {
|
||||||
|
minimum = object.get(MINIMUM).getAsInt() < 0 ? 0 : object.get(MINIMUM).getAsInt();
|
||||||
|
}
|
||||||
|
if (object.has(MAXIMUM)) {
|
||||||
|
maximum = object.get(MAXIMUM).getAsInt();
|
||||||
|
}
|
||||||
|
return NumberSource.getInstance().randomInt(minimum, maximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object analyzeNumber(JsonObject object) {
|
||||||
|
if (object != null && object.has(DEFAULT) && isNumber(object.has(DEFAULT))) {
|
||||||
|
return object.get(DEFAULT).getAsFloat();
|
||||||
|
}
|
||||||
|
Object mockValue = getMockValue(object);
|
||||||
|
if (mockValue != null && isNumber(mockValue)) {
|
||||||
|
return Float.parseFloat(mockValue.toString());
|
||||||
|
}
|
||||||
|
float maximum = 200001.0f;
|
||||||
|
float minimum = 100000.0f;
|
||||||
|
if (object.has(MINIMUM)) {
|
||||||
|
float min = object.get(MINIMUM).getAsFloat();
|
||||||
|
minimum = min < 0 ? 0 : min;
|
||||||
|
}
|
||||||
|
if (object.has(MAXIMUM)) {
|
||||||
|
float max = object.get(MAXIMUM).getAsFloat();
|
||||||
|
maximum = max > 0 ? max : maximum;
|
||||||
|
}
|
||||||
|
return NumberSource.getInstance().randomDouble(minimum, maximum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean getRandomBoolean() {
|
||||||
|
return Math.random() < 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean analyzeBoolean(JsonObject object) {
|
||||||
|
if (object.has(DEFAULT)) {
|
||||||
|
return object.get(DEFAULT).getAsBoolean();
|
||||||
|
}
|
||||||
|
return getRandomBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void analyzeProperty(JSONObject concept, String propertyName, JsonObject object) {
|
||||||
|
if (object != null && object.has(TYPE)) {
|
||||||
|
String propertyObjType = getPropertyObjType(object);
|
||||||
|
if (object.has(DEFAULT)) {
|
||||||
|
concept.put(propertyName, object.get(DEFAULT).getAsString());
|
||||||
|
} else if (object.has(ENUM)) {
|
||||||
|
concept.put(propertyName, analyzeEnumProperty(object));
|
||||||
|
} else if (propertyObjType.equals(STRING)) {
|
||||||
|
concept.put(propertyName, analyzeString(object));
|
||||||
|
} else if (propertyObjType.equals(INTEGER)) {
|
||||||
|
concept.put(propertyName, analyzeInteger(object));
|
||||||
|
} else if (propertyObjType.equals(NUMBER)) {
|
||||||
|
concept.put(propertyName, analyzeNumber(object));
|
||||||
|
} else if (propertyObjType.equals(BOOLEAN)) {
|
||||||
|
concept.put(propertyName, analyzeBoolean(object));
|
||||||
|
} else if (propertyObjType.equals(ARRAY)) {
|
||||||
|
analyzeItems(concept, propertyName, object);
|
||||||
|
} else if (propertyObjType.equals(OBJECT)) {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
concept.put(propertyName, obj);
|
||||||
|
analyzeObject(object, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPropertyObjType(JsonObject object) {
|
||||||
|
if (object.get(TYPE) != null && object.get(TYPE) instanceof JsonPrimitive) {
|
||||||
|
return object.get(TYPE).getAsString();
|
||||||
|
} else if (object.get(TYPE) instanceof JsonArray) {
|
||||||
|
JsonArray typeArray = object.get(TYPE).getAsJsonArray();
|
||||||
|
return typeArray.get(0).getAsString();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object analyzeEnumProperty(JsonObject object) {
|
||||||
|
Object enumValue = "";
|
||||||
|
try {
|
||||||
|
if (object.get(ENUM) != null) {
|
||||||
|
String enums = object.get(ENUM).getAsString();
|
||||||
|
if (StringUtils.isNotBlank(enums)) {
|
||||||
|
String enumArr[] = enums.split("\n");
|
||||||
|
int index = (int) (Math.random() * enumArr.length);
|
||||||
|
enumValue = enumArr[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String propertyObjType = getPropertyObjType(object);
|
||||||
|
if (propertyObjType.equals(INTEGER)) {
|
||||||
|
enumValue = Integer.parseInt(enumValue.toString());
|
||||||
|
} else if (propertyObjType.equals(NUMBER)) {
|
||||||
|
enumValue = Float.parseFloat(enumValue.toString());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return enumValue;
|
||||||
|
}
|
||||||
|
return enumValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void analyzeDefinitions(JsonObject object) {
|
||||||
|
JsonObject definitionsObj = object.get(DEFINITIONS).getAsJsonObject();
|
||||||
|
for (Entry<String, JsonElement> entry : definitionsObj.entrySet()) {
|
||||||
|
String definitionKey = entry.getKey();
|
||||||
|
JsonObject definitionObj = definitionsObj.get(definitionKey).getAsJsonObject();
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
analyzeRootSchemaElement(definitionObj, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String generator(String jsonSchema) {
|
||||||
|
try {
|
||||||
|
if (StringUtils.isEmpty(jsonSchema)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
JSONObject root = new JSONObject();
|
||||||
|
analyzeSchema(jsonSchema, root);
|
||||||
|
// 格式化返回
|
||||||
|
if (root != null && root.has(MS_OBJECT)) {
|
||||||
|
return root.get(MS_OBJECT).toString();
|
||||||
|
}
|
||||||
|
return root.toString();
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return jsonSchema;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,7 +118,6 @@ import {apiProjectByScenarioId, getProjectApplicationConfig} from '../../../api/
|
||||||
import { apiTestReRun } from '../../../api/xpack';
|
import { apiTestReRun } from '../../../api/xpack';
|
||||||
import { getUUID } from 'metersphere-frontend/src/utils';
|
import { getUUID } from 'metersphere-frontend/src/utils';
|
||||||
import { getApiScenarioIdByPlanScenarioId } from '@/api/test-plan';
|
import { getApiScenarioIdByPlanScenarioId } from '@/api/test-plan';
|
||||||
import {getScenarioReport} from '../../../api/scenario-report';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'MsApiReportViewHeader',
|
name: 'MsApiReportViewHeader',
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
@click.stop
|
@click.stop
|
||||||
@click="generate"
|
@click="generate"
|
||||||
style="margin-left: 10px"
|
style="margin-left: 10px"
|
||||||
v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API') && hasLicense()">
|
v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API')">
|
||||||
{{ $t('commons.generate_test_data') }}
|
{{ $t('commons.generate_test_data') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -111,9 +111,7 @@
|
||||||
:headers="request.headers"
|
:headers="request.headers"
|
||||||
:body="request.body" />
|
:body="request.body" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane
|
<el-tab-pane name="create" v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API') && definitionTest">
|
||||||
name="create"
|
|
||||||
v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API') && hasLicense() && definitionTest">
|
|
||||||
<template v-slot:label>
|
<template v-slot:label>
|
||||||
<el-button size="mini" type="primary" @click.stop @click="generate"
|
<el-button size="mini" type="primary" @click.stop @click="generate"
|
||||||
>{{ $t('commons.generate_test_data') }}
|
>{{ $t('commons.generate_test_data') }}
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
@click.stop
|
@click.stop
|
||||||
@click="generate"
|
@click="generate"
|
||||||
style="margin-left: 10px"
|
style="margin-left: 10px"
|
||||||
v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API') && hasLicense()">
|
v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API')">
|
||||||
{{ $t('commons.generate_test_data') }}
|
{{ $t('commons.generate_test_data') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package io.metersphere.xpack.api.service;
|
|
||||||
|
|
||||||
import io.metersphere.plugin.core.MsTestElement;
|
|
||||||
|
|
||||||
public interface ApiRetryOnFailureService {
|
|
||||||
public String retry(String data, long retryNum, boolean isCase);
|
|
||||||
|
|
||||||
public MsTestElement retryParse(String retryCase);
|
|
||||||
|
|
||||||
}
|
|
|
@ -117,7 +117,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 失败重试 -->
|
<!-- 失败重试 -->
|
||||||
<div class="mode-row" v-if="isHasLicense">
|
<div class="mode-row">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="runConfig.retryEnable"
|
v-model="runConfig.retryEnable"
|
||||||
class="ms-failure-div-right"
|
class="ms-failure-div-right"
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
<span v-if="runConfig.retryEnable">
|
<span v-if="runConfig.retryEnable">
|
||||||
<el-tooltip placement="top" style="margin: 0 4px 0 2px">
|
<el-tooltip placement="top" style="margin: 0 4px 0 2px">
|
||||||
<div slot="content">{{ $t("run_mode.retry_message") }}</div>
|
<div slot="content">{{ $t("run_mode.retry_message") }}</div>
|
||||||
<i class="el-icon-question" style="cursor: pointer"/>
|
<i class="el-icon-question" style="cursor: pointer" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span style="margin-left: 10px">
|
<span style="margin-left: 10px">
|
||||||
{{ $t("run_mode.retry") }}
|
{{ $t("run_mode.retry") }}
|
||||||
|
@ -146,9 +146,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mode-row" v-if="runConfig.mode === 'serial'">
|
<div class="mode-row" v-if="runConfig.mode === 'serial'">
|
||||||
<el-checkbox v-model="runConfig.onSampleError">{{
|
<el-checkbox v-model="runConfig.onSampleError"
|
||||||
$t("api_test.fail_to_stop")
|
>{{ $t("api_test.fail_to_stop") }}
|
||||||
}}
|
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -166,44 +165,43 @@
|
||||||
<el-button @click="close">{{ $t("commons.cancel") }}</el-button>
|
<el-button @click="close">{{ $t("commons.cancel") }}</el-button>
|
||||||
<el-dropdown @command="handleCommand" style="margin-left: 5px">
|
<el-dropdown @command="handleCommand" style="margin-left: 5px">
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
{{
|
{{ $t("load_test.save_and_run")
|
||||||
$t("load_test.save_and_run")
|
|
||||||
}}<i class="el-icon-arrow-down el-icon--right"></i>
|
}}<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item command="run">{{
|
<el-dropdown-item command="run"
|
||||||
$t("load_test.save_and_run")
|
>{{ $t("load_test.save_and_run") }}
|
||||||
}}
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item command="save">{{
|
<el-dropdown-item command="save"
|
||||||
$t("commons.save")
|
>{{ $t("commons.save") }}
|
||||||
}}
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<ms-dialog-footer v-else @cancel="close" @confirm="handleRunBatch"/>
|
<ms-dialog-footer v-else @cancel="close" @confirm="handleRunBatch" />
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
||||||
import {hasLicense} from "metersphere-frontend/src/utils/permission";
|
import { strMapToObj } from "metersphere-frontend/src/utils";
|
||||||
import {strMapToObj} from "metersphere-frontend/src/utils";
|
|
||||||
|
|
||||||
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
|
import { ENV_TYPE } from "metersphere-frontend/src/utils/constants";
|
||||||
import {getCurrentProjectID, getOwnerProjects} from "@/business/utils/sdk-utils";
|
import {
|
||||||
import {getQuotaValidResourcePools} from "@/api/remote/resource-pool";
|
getCurrentProjectID,
|
||||||
|
getOwnerProjects,
|
||||||
|
} from "@/business/utils/sdk-utils";
|
||||||
|
import { getQuotaValidResourcePools } from "@/api/remote/resource-pool";
|
||||||
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
|
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
|
||||||
import {getApiCaseEnv} from "@/api/remote/plan/test-plan-api-case";
|
import { getApiCaseEnv } from "@/api/remote/plan/test-plan-api-case";
|
||||||
import {getApiScenarioEnv, getPlanCaseEnv} from "@/api/remote/plan/test-plan";
|
import { getApiScenarioEnv, getPlanCaseEnv } from "@/api/remote/plan/test-plan";
|
||||||
import {getSystemBaseSetting} from "metersphere-frontend/src/api/system";
|
import { getSystemBaseSetting } from "metersphere-frontend/src/api/system";
|
||||||
import {getProjectConfig} from "@/api/project";
|
import { getProjectConfig } from "@/api/project";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsPlanRunModeWithEnv",
|
name: "MsPlanRunModeWithEnv",
|
||||||
components: {EnvGroupPopover, MsDialogFooter},
|
components: { EnvGroupPopover, MsDialogFooter },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
@ -227,7 +225,6 @@ export default {
|
||||||
retryNum: 1,
|
retryNum: 1,
|
||||||
browser: "CHROME",
|
browser: "CHROME",
|
||||||
},
|
},
|
||||||
isHasLicense: hasLicense(),
|
|
||||||
projectEnvListMap: {},
|
projectEnvListMap: {},
|
||||||
projectList: [],
|
projectList: [],
|
||||||
projectIds: new Set(),
|
projectIds: new Set(),
|
||||||
|
@ -274,8 +271,12 @@ export default {
|
||||||
open(testType, runModeConfig) {
|
open(testType, runModeConfig) {
|
||||||
if (runModeConfig) {
|
if (runModeConfig) {
|
||||||
this.runConfig = JSON.parse(runModeConfig);
|
this.runConfig = JSON.parse(runModeConfig);
|
||||||
this.runConfig.onSampleError = this.runConfig.onSampleError === 'true' || this.runConfig.onSampleError === true;
|
this.runConfig.onSampleError =
|
||||||
this.runConfig.runWithinResourcePool = this.runConfig.runWithinResourcePool === 'true' || this.runConfig.runWithinResourcePool === true;
|
this.runConfig.onSampleError === "true" ||
|
||||||
|
this.runConfig.onSampleError === true;
|
||||||
|
this.runConfig.runWithinResourcePool =
|
||||||
|
this.runConfig.runWithinResourcePool === "true" ||
|
||||||
|
this.runConfig.runWithinResourcePool === true;
|
||||||
}
|
}
|
||||||
this.runModeVisible = true;
|
this.runModeVisible = true;
|
||||||
this.testType = testType;
|
this.testType = testType;
|
||||||
|
@ -285,21 +286,21 @@ export default {
|
||||||
},
|
},
|
||||||
query() {
|
query() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.result = getSystemBaseSetting().then(response => {
|
this.result = getSystemBaseSetting().then((response) => {
|
||||||
if (!response.data.runMode) {
|
if (!response.data.runMode) {
|
||||||
response.data.runMode = 'LOCAL'
|
response.data.runMode = "LOCAL";
|
||||||
}
|
}
|
||||||
this.runMode = response.data.runMode;
|
this.runMode = response.data.runMode;
|
||||||
if (this.runMode === 'POOL') {
|
if (this.runMode === "POOL") {
|
||||||
this.runConfig.runWithinResourcePool = true;
|
this.runConfig.runWithinResourcePool = true;
|
||||||
this.getProjectApplication();
|
this.getProjectApplication();
|
||||||
} else {
|
} else {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
getProjectApplication() {
|
getProjectApplication() {
|
||||||
getProjectConfig(getCurrentProjectID(), "").then(res => {
|
getProjectConfig(getCurrentProjectID(), "").then((res) => {
|
||||||
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
||||||
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
||||||
}
|
}
|
||||||
|
@ -329,10 +330,9 @@ export default {
|
||||||
this.close();
|
this.close();
|
||||||
},
|
},
|
||||||
getResourcePools() {
|
getResourcePools() {
|
||||||
getQuotaValidResourcePools()
|
getQuotaValidResourcePools().then((response) => {
|
||||||
.then((response) => {
|
this.resourcePools = response.data;
|
||||||
this.resourcePools = response.data;
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
setProjectEnvMap(projectEnvMap) {
|
setProjectEnvMap(projectEnvMap) {
|
||||||
this.runConfig.envMap = strMapToObj(projectEnvMap);
|
this.runConfig.envMap = strMapToObj(projectEnvMap);
|
||||||
|
@ -341,10 +341,9 @@ export default {
|
||||||
this.runConfig.environmentGroupId = id;
|
this.runConfig.environmentGroupId = id;
|
||||||
},
|
},
|
||||||
getWsProjects() {
|
getWsProjects() {
|
||||||
getOwnerProjects()
|
getOwnerProjects().then((res) => {
|
||||||
.then((res) => {
|
this.projectList = res.data;
|
||||||
this.projectList = res.data;
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
showPopover() {
|
showPopover() {
|
||||||
this.projectIds.clear();
|
this.projectIds.clear();
|
||||||
|
@ -374,7 +373,7 @@ export default {
|
||||||
this.$refs.envPopover.openEnvSelect();
|
this.$refs.envPopover.openEnvSelect();
|
||||||
});
|
});
|
||||||
} else if (this.type === "plan") {
|
} else if (this.type === "plan") {
|
||||||
param = {id: this.planId};
|
param = { id: this.planId };
|
||||||
getPlanCaseEnv(param).then((res) => {
|
getPlanCaseEnv(param).then((res) => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
|
@ -3,32 +3,33 @@
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
:title="$t('load_test.runtime_config')"
|
:title="$t('load_test.runtime_config')"
|
||||||
width="550px"
|
width="550px"
|
||||||
style="margin-top: -8.65vh;max-height: 87.3vh"
|
style="margin-top: -8.65vh; max-height: 87.3vh"
|
||||||
@close="close"
|
@close="close"
|
||||||
:visible.sync="runModeVisible"
|
:visible.sync="runModeVisible"
|
||||||
>
|
>
|
||||||
<div class="env-container">
|
<div class="env-container">
|
||||||
<div>
|
<div>
|
||||||
<div>{{ $t("commons.environment") }}:</div>
|
<div>{{ $t("commons.environment") }}:</div>
|
||||||
<env-select-popover :project-ids="projectIds"
|
<env-select-popover
|
||||||
:project-list="projectList"
|
:project-ids="projectIds"
|
||||||
:project-env-map="projectEnvListMap"
|
:project-list="projectList"
|
||||||
:environment-type.sync="runConfig.environmentType"
|
:project-env-map="projectEnvListMap"
|
||||||
:has-option-group="true"
|
:environment-type.sync="runConfig.environmentType"
|
||||||
:group-id="runConfig.environmentGroupId"
|
:has-option-group="true"
|
||||||
@setProjectEnvMap="setProjectEnvMap"
|
:group-id="runConfig.environmentGroupId"
|
||||||
@setEnvGroup="setEnvGroup"
|
@setProjectEnvMap="setProjectEnvMap"
|
||||||
ref="envSelectPopover"
|
@setEnvGroup="setEnvGroup"
|
||||||
class="mode-row"
|
ref="envSelectPopover"
|
||||||
|
class="mode-row"
|
||||||
></env-select-popover>
|
></env-select-popover>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="haveUICase">
|
<div v-if="haveUICase">
|
||||||
<div>{{ $t("ui.browser") }}:</div>
|
<div>{{ $t("ui.browser") }}:</div>
|
||||||
<div >
|
<div>
|
||||||
<el-select
|
<el-select
|
||||||
size="mini"
|
size="mini"
|
||||||
v-model="runConfig.browser"
|
v-model="runConfig.browser"
|
||||||
style="width: 100% "
|
style="width: 100%"
|
||||||
class="mode-row"
|
class="mode-row"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="mode-row">{{ $t("run_mode.title") }}:</div>
|
<div class="mode-row">{{ $t("run_mode.title") }}:</div>
|
||||||
<div >
|
<div>
|
||||||
<el-radio-group
|
<el-radio-group
|
||||||
v-model="runConfig.mode"
|
v-model="runConfig.mode"
|
||||||
@change="changeMode"
|
@change="changeMode"
|
||||||
|
@ -54,13 +55,17 @@
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div >
|
<div>
|
||||||
<div class="mode-row">{{ $t("run_mode.other_config") }}:</div>
|
<div class="mode-row">{{ $t("run_mode.other_config") }}:</div>
|
||||||
<div >
|
<div>
|
||||||
<!-- 串行 -->
|
<!-- 串行 -->
|
||||||
<div
|
<div
|
||||||
class="mode-row"
|
class="mode-row"
|
||||||
v-if="runConfig.mode === 'serial' && testType === 'API' && haveOtherExecCase"
|
v-if="
|
||||||
|
runConfig.mode === 'serial' &&
|
||||||
|
testType === 'API' &&
|
||||||
|
haveOtherExecCase
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="runConfig.runWithinResourcePool"
|
v-model="runConfig.runWithinResourcePool"
|
||||||
|
@ -68,13 +73,13 @@
|
||||||
class="radio-change"
|
class="radio-change"
|
||||||
:disabled="runMode === 'POOL'"
|
:disabled="runMode === 'POOL'"
|
||||||
>
|
>
|
||||||
{{ $t("run_mode.run_with_resource_pool") }}
|
{{ $t("run_mode.run_with_resource_pool") }} </el-checkbox
|
||||||
</el-checkbox><br/>
|
><br />
|
||||||
<el-select
|
<el-select
|
||||||
:disabled="!runConfig.runWithinResourcePool"
|
:disabled="!runConfig.runWithinResourcePool"
|
||||||
v-model="runConfig.resourcePoolId"
|
v-model="runConfig.resourcePoolId"
|
||||||
size="mini"
|
size="mini"
|
||||||
style="width:100%; margin-top: 8px"
|
style="width: 100%; margin-top: 8px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in resourcePools"
|
v-for="item in resourcePools"
|
||||||
|
@ -88,7 +93,11 @@
|
||||||
<!-- 并行 -->
|
<!-- 并行 -->
|
||||||
<div
|
<div
|
||||||
class="mode-row"
|
class="mode-row"
|
||||||
v-if="runConfig.mode === 'parallel' && testType === 'API' && haveOtherExecCase"
|
v-if="
|
||||||
|
runConfig.mode === 'parallel' &&
|
||||||
|
testType === 'API' &&
|
||||||
|
haveOtherExecCase
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="runConfig.runWithinResourcePool"
|
v-model="runConfig.runWithinResourcePool"
|
||||||
|
@ -96,13 +105,13 @@
|
||||||
class="radio-change"
|
class="radio-change"
|
||||||
:disabled="runMode === 'POOL'"
|
:disabled="runMode === 'POOL'"
|
||||||
>
|
>
|
||||||
{{ $t("run_mode.run_with_resource_pool") }}
|
{{ $t("run_mode.run_with_resource_pool") }} </el-checkbox
|
||||||
</el-checkbox><br/>
|
><br />
|
||||||
<el-select
|
<el-select
|
||||||
:disabled="!runConfig.runWithinResourcePool"
|
:disabled="!runConfig.runWithinResourcePool"
|
||||||
v-model="runConfig.resourcePoolId"
|
v-model="runConfig.resourcePoolId"
|
||||||
size="mini"
|
size="mini"
|
||||||
style="width:100%; margin-top: 8px"
|
style="width: 100%; margin-top: 8px"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in resourcePools"
|
v-for="item in resourcePools"
|
||||||
|
@ -116,7 +125,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 失败重试 -->
|
<!-- 失败重试 -->
|
||||||
<div class="mode-row" v-if="isHasLicense">
|
<div class="mode-row">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="runConfig.retryEnable"
|
v-model="runConfig.retryEnable"
|
||||||
class="radio-change ms-failure-div-right"
|
class="radio-change ms-failure-div-right"
|
||||||
|
@ -126,8 +135,11 @@
|
||||||
<span v-if="runConfig.retryEnable">
|
<span v-if="runConfig.retryEnable">
|
||||||
<el-tooltip placement="top" style="margin: 0 4px 0 2px">
|
<el-tooltip placement="top" style="margin: 0 4px 0 2px">
|
||||||
<div slot="content">{{ $t("run_mode.retry_message") }}</div>
|
<div slot="content">{{ $t("run_mode.retry_message") }}</div>
|
||||||
<i class="el-icon-question" style="cursor: pointer"/>
|
<i
|
||||||
</el-tooltip><br/>
|
class="el-icon-question"
|
||||||
|
style="cursor: pointer"
|
||||||
|
/> </el-tooltip
|
||||||
|
><br />
|
||||||
<span>
|
<span>
|
||||||
{{ $t("run_mode.retry") }}
|
{{ $t("run_mode.retry") }}
|
||||||
<el-input-number
|
<el-input-number
|
||||||
|
@ -136,7 +148,7 @@
|
||||||
:min="1"
|
:min="1"
|
||||||
:max="10000000"
|
:max="10000000"
|
||||||
size="mini"
|
size="mini"
|
||||||
style="width: 103px;margin-top: 8px"
|
style="width: 103px; margin-top: 8px"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{{ $t("run_mode.retry_frequency") }}
|
{{ $t("run_mode.retry_frequency") }}
|
||||||
|
@ -145,14 +157,16 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mode-row" v-if="runConfig.mode === 'serial'">
|
<div class="mode-row" v-if="runConfig.mode === 'serial'">
|
||||||
<el-checkbox v-model="runConfig.onSampleError" class="radio-change">{{
|
<el-checkbox v-model="runConfig.onSampleError" class="radio-change"
|
||||||
$t("api_test.fail_to_stop")
|
>{{ $t("api_test.fail_to_stop") }}
|
||||||
}}
|
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mode-row" v-if="haveUICase">
|
<div class="mode-row" v-if="haveUICase">
|
||||||
<el-checkbox v-model="runConfig.headlessEnabled" class="radio-change">
|
<el-checkbox
|
||||||
|
v-model="runConfig.headlessEnabled"
|
||||||
|
class="radio-change"
|
||||||
|
>
|
||||||
{{ $t("ui.performance_mode") }}
|
{{ $t("ui.performance_mode") }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
@ -165,55 +179,64 @@
|
||||||
<el-button @click="close">{{ $t("commons.cancel") }}</el-button>
|
<el-button @click="close">{{ $t("commons.cancel") }}</el-button>
|
||||||
<el-dropdown @command="handleCommand" style="margin-left: 5px">
|
<el-dropdown @command="handleCommand" style="margin-left: 5px">
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
{{
|
{{ $t("api_test.run")
|
||||||
$t("api_test.run")
|
|
||||||
}}<i class="el-icon-arrow-down el-icon--right"></i>
|
}}<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item command="run">{{
|
<el-dropdown-item command="run"
|
||||||
$t("api_test.run")
|
>{{ $t("api_test.run") }}
|
||||||
}}
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item command="runAndSave">{{
|
<el-dropdown-item command="runAndSave"
|
||||||
$t("load_test.save_and_run")
|
>{{ $t("load_test.save_and_run") }}
|
||||||
}}
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item command="save">{{
|
<el-dropdown-item command="save"
|
||||||
$t("commons.save")
|
>{{ $t("commons.save") }}
|
||||||
}}
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<ms-dialog-footer v-else @cancel="close" @confirm="handleRunBatch"/>
|
<ms-dialog-footer v-else @cancel="close" @confirm="handleRunBatch" />
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
||||||
import {hasLicense} from "metersphere-frontend/src/utils/permission";
|
import { strMapToObj } from "metersphere-frontend/src/utils";
|
||||||
import {strMapToObj} from "metersphere-frontend/src/utils";
|
|
||||||
import MsTag from "metersphere-frontend/src/components/MsTag";
|
import MsTag from "metersphere-frontend/src/components/MsTag";
|
||||||
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
|
import { ENV_TYPE } from "metersphere-frontend/src/utils/constants";
|
||||||
import {getCurrentProjectID, getOwnerProjects} from "@/business/utils/sdk-utils";
|
import {
|
||||||
import {getQuotaValidResourcePools} from "@/api/remote/resource-pool";
|
getCurrentProjectID,
|
||||||
|
getOwnerProjects,
|
||||||
|
} from "@/business/utils/sdk-utils";
|
||||||
|
import { getQuotaValidResourcePools } from "@/api/remote/resource-pool";
|
||||||
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
|
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
|
||||||
import {getApiCaseEnv} from "@/api/remote/plan/test-plan-api-case";
|
import { getApiCaseEnv } from "@/api/remote/plan/test-plan-api-case";
|
||||||
import {getApiScenarioEnv, getPlanCaseEnv, getPlanCaseProjectIds} from "@/api/remote/plan/test-plan";
|
import {
|
||||||
|
getApiScenarioEnv,
|
||||||
|
getPlanCaseEnv,
|
||||||
|
getPlanCaseProjectIds,
|
||||||
|
} from "@/api/remote/plan/test-plan";
|
||||||
import EnvGroupWithOption from "../env/EnvGroupWithOption";
|
import EnvGroupWithOption from "../env/EnvGroupWithOption";
|
||||||
import EnvironmentGroup from "@/business/plan/env/EnvironmentGroupList";
|
import EnvironmentGroup from "@/business/plan/env/EnvironmentGroupList";
|
||||||
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
|
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
|
||||||
import {getSystemBaseSetting} from "metersphere-frontend/src/api/system";
|
import { getSystemBaseSetting } from "metersphere-frontend/src/api/system";
|
||||||
import {getProjectConfig} from "@/api/project";
|
import { getProjectConfig } from "@/api/project";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsTestPlanRunModeWithEnv",
|
name: "MsTestPlanRunModeWithEnv",
|
||||||
components: {EnvGroupPopover, MsDialogFooter,MsTag,EnvGroupWithOption,EnvironmentGroup,EnvSelectPopover},
|
components: {
|
||||||
|
EnvGroupPopover,
|
||||||
|
MsDialogFooter,
|
||||||
|
MsTag,
|
||||||
|
EnvGroupWithOption,
|
||||||
|
EnvironmentGroup,
|
||||||
|
EnvSelectPopover,
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
ENV_TYPE() {
|
ENV_TYPE() {
|
||||||
return ENV_TYPE;
|
return ENV_TYPE;
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -221,7 +244,7 @@ export default {
|
||||||
btnStyle: {
|
btnStyle: {
|
||||||
width: "260px",
|
width: "260px",
|
||||||
},
|
},
|
||||||
result:{loading: false},
|
result: { loading: false },
|
||||||
runModeVisible: false,
|
runModeVisible: false,
|
||||||
testType: null,
|
testType: null,
|
||||||
resourcePools: [],
|
resourcePools: [],
|
||||||
|
@ -239,7 +262,6 @@ export default {
|
||||||
retryNum: 1,
|
retryNum: 1,
|
||||||
browser: "CHROME",
|
browser: "CHROME",
|
||||||
},
|
},
|
||||||
isHasLicense: hasLicense(),
|
|
||||||
projectList: [],
|
projectList: [],
|
||||||
projectIds: new Set(),
|
projectIds: new Set(),
|
||||||
options: [
|
options: [
|
||||||
|
@ -290,8 +312,12 @@ export default {
|
||||||
if (runModeConfig) {
|
if (runModeConfig) {
|
||||||
this.runConfig = JSON.parse(runModeConfig);
|
this.runConfig = JSON.parse(runModeConfig);
|
||||||
this.runConfig.envMap = new Map();
|
this.runConfig.envMap = new Map();
|
||||||
this.runConfig.onSampleError = this.runConfig.onSampleError === 'true' || this.runConfig.onSampleError === true;
|
this.runConfig.onSampleError =
|
||||||
this.runConfig.runWithinResourcePool = this.runConfig.runWithinResourcePool === 'true' || this.runConfig.runWithinResourcePool === true;
|
this.runConfig.onSampleError === "true" ||
|
||||||
|
this.runConfig.onSampleError === true;
|
||||||
|
this.runConfig.runWithinResourcePool =
|
||||||
|
this.runConfig.runWithinResourcePool === "true" ||
|
||||||
|
this.runConfig.runWithinResourcePool === true;
|
||||||
}
|
}
|
||||||
this.runModeVisible = true;
|
this.runModeVisible = true;
|
||||||
this.testType = testType;
|
this.testType = testType;
|
||||||
|
@ -302,21 +328,21 @@ export default {
|
||||||
},
|
},
|
||||||
query() {
|
query() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.result = getSystemBaseSetting().then(response => {
|
this.result = getSystemBaseSetting().then((response) => {
|
||||||
if (!response.data.runMode) {
|
if (!response.data.runMode) {
|
||||||
response.data.runMode = 'LOCAL'
|
response.data.runMode = "LOCAL";
|
||||||
}
|
}
|
||||||
this.runMode = response.data.runMode;
|
this.runMode = response.data.runMode;
|
||||||
if (this.runMode === 'POOL') {
|
if (this.runMode === "POOL") {
|
||||||
this.runConfig.runWithinResourcePool = true;
|
this.runConfig.runWithinResourcePool = true;
|
||||||
this.getProjectApplication();
|
this.getProjectApplication();
|
||||||
} else {
|
} else {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
getProjectApplication() {
|
getProjectApplication() {
|
||||||
getProjectConfig(getCurrentProjectID(), "").then(res => {
|
getProjectConfig(getCurrentProjectID(), "").then((res) => {
|
||||||
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
||||||
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
||||||
}
|
}
|
||||||
|
@ -346,10 +372,9 @@ export default {
|
||||||
this.close();
|
this.close();
|
||||||
},
|
},
|
||||||
getResourcePools() {
|
getResourcePools() {
|
||||||
getQuotaValidResourcePools()
|
getQuotaValidResourcePools().then((response) => {
|
||||||
.then((response) => {
|
this.resourcePools = response.data;
|
||||||
this.resourcePools = response.data;
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
setProjectEnvMap(projectEnvMap) {
|
setProjectEnvMap(projectEnvMap) {
|
||||||
this.runConfig.envMap = projectEnvMap;
|
this.runConfig.envMap = projectEnvMap;
|
||||||
|
@ -358,10 +383,9 @@ export default {
|
||||||
this.runConfig.environmentGroupId = id;
|
this.runConfig.environmentGroupId = id;
|
||||||
},
|
},
|
||||||
getWsProjects() {
|
getWsProjects() {
|
||||||
getOwnerProjects()
|
getOwnerProjects().then((res) => {
|
||||||
.then((res) => {
|
this.projectList = res.data;
|
||||||
this.projectList = res.data;
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
showPopover() {
|
showPopover() {
|
||||||
this.projectIds.clear();
|
this.projectIds.clear();
|
||||||
|
@ -391,7 +415,7 @@ export default {
|
||||||
this.$refs.envSelectPopover.open();
|
this.$refs.envSelectPopover.open();
|
||||||
});
|
});
|
||||||
} else if (this.type === "plan") {
|
} else if (this.type === "plan") {
|
||||||
param = {id: this.planId};
|
param = { id: this.planId };
|
||||||
getPlanCaseEnv(param).then((res) => {
|
getPlanCaseEnv(param).then((res) => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -401,7 +425,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.projectIds.size === 0) {
|
if (this.projectIds.size === 0) {
|
||||||
param = {id: this.planId};
|
param = { id: this.planId };
|
||||||
getPlanCaseProjectIds(param).then((res) => {
|
getPlanCaseProjectIds(param).then((res) => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
if (data) {
|
if (data) {
|
||||||
|
@ -416,28 +440,27 @@ export default {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
handleCommand(command) {
|
handleCommand(command) {
|
||||||
if (
|
if (
|
||||||
this.runConfig.runWithinResourcePool &&
|
this.runConfig.runWithinResourcePool &&
|
||||||
this.runConfig.resourcePoolId == null && this.haveOtherExecCase
|
this.runConfig.resourcePoolId == null &&
|
||||||
|
this.haveOtherExecCase
|
||||||
) {
|
) {
|
||||||
this.$warning(
|
this.$warning(
|
||||||
this.$t("workspace.env_group.please_select_run_within_resource_pool")
|
this.$t("workspace.env_group.please_select_run_within_resource_pool")
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.runConfig.envMap =strMapToObj(this.runConfig.envMap)
|
this.runConfig.envMap = strMapToObj(this.runConfig.envMap);
|
||||||
if (command === "runAndSave") {
|
if (command === "runAndSave") {
|
||||||
this.runConfig.executionWay = "runAndSave";
|
this.runConfig.executionWay = "runAndSave";
|
||||||
} else if(command === "save"){
|
} else if (command === "save") {
|
||||||
this.runConfig.executionWay = "save";
|
this.runConfig.executionWay = "save";
|
||||||
} else {
|
} else {
|
||||||
this.runConfig.executionWay = "run";
|
this.runConfig.executionWay = "run";
|
||||||
}
|
}
|
||||||
this.handleRunBatch();
|
this.handleRunBatch();
|
||||||
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -461,13 +484,12 @@ export default {
|
||||||
.mode-row {
|
.mode-row {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.radio-change:deep(.el-radio__input.is-checked + .el-radio__label) {
|
.radio-change:deep(.el-radio__input.is-checked + .el-radio__label) {
|
||||||
color: #606266 !important;
|
color: #606266 !important;
|
||||||
}
|
}
|
||||||
.radio-change:deep(.el-checkbox__input.is-checked+.el-checkbox__label) {
|
.radio-change:deep(.el-checkbox__input.is-checked + .el-checkbox__label) {
|
||||||
color: #606266 !important;
|
color: #606266 !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,60 +1,97 @@
|
||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-loading="result.loading"
|
v-loading="result.loading"
|
||||||
:close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible"
|
:close-on-click-modal="false"
|
||||||
:append-to-body='true'
|
width="60%"
|
||||||
@close="close">
|
class="schedule-edit"
|
||||||
|
:visible.sync="dialogVisible"
|
||||||
|
:append-to-body="true"
|
||||||
|
@close="close"
|
||||||
|
>
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-tabs v-model="activeName">
|
<el-tabs v-model="activeName">
|
||||||
<el-tab-pane :label="$t('schedule.task_config')" name="first">
|
<el-tab-pane :label="$t('schedule.task_config')" name="first">
|
||||||
<div class="el-step__icon is-text" style="margin-right: 10px;">
|
<div class="el-step__icon is-text" style="margin-right: 10px">
|
||||||
<div class="el-step__icon-inner">1</div>
|
<div class="el-step__icon-inner">1</div>
|
||||||
</div>
|
</div>
|
||||||
<span>{{ $t('schedule.edit_timer_task') }}</span>
|
<span>{{ $t("schedule.edit_timer_task") }}</span>
|
||||||
<el-form :model="form" :rules="rules" ref="from" style="padding-top: 10px;margin-left: 20px;"
|
<el-form
|
||||||
class="ms-el-form-item__error">
|
:model="form"
|
||||||
<el-form-item :label="$t('commons.schedule_cron_title')"
|
:rules="rules"
|
||||||
prop="cronValue" style="height: 50px">
|
ref="from"
|
||||||
|
style="padding-top: 10px; margin-left: 20px"
|
||||||
|
class="ms-el-form-item__error"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
:label="$t('commons.schedule_cron_title')"
|
||||||
|
prop="cronValue"
|
||||||
|
style="height: 50px"
|
||||||
|
>
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="16">
|
<el-col :span="16">
|
||||||
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
|
<el-input
|
||||||
:placeholder="$t('schedule.please_input_cron_expression')" size="mini">
|
:disabled="isReadOnly"
|
||||||
<a :disabled="isReadOnly" type="primary" @click="showCronDialog" slot="suffix" class="head">
|
v-model="form.cronValue"
|
||||||
{{ $t('schedule.generate_expression') }}
|
class="inp"
|
||||||
|
:placeholder="$t('schedule.please_input_cron_expression')"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
:disabled="isReadOnly"
|
||||||
|
type="primary"
|
||||||
|
@click="showCronDialog"
|
||||||
|
slot="suffix"
|
||||||
|
class="head"
|
||||||
|
>
|
||||||
|
{{ $t("schedule.generate_expression") }}
|
||||||
</a>
|
</a>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
|
||||||
<span>{{ this.$t('commons.schedule_switch') }}</span>
|
<span>{{ this.$t("commons.schedule_switch") }}</span>
|
||||||
<el-tooltip effect="dark" placement="bottom"
|
<el-tooltip
|
||||||
:content="schedule.enable ? $t('commons.close_schedule') : $t('commons.open_schedule')">
|
effect="dark"
|
||||||
<el-switch v-model="schedule.enable" style="margin-left: 20px"></el-switch>
|
placement="bottom"
|
||||||
|
:content="
|
||||||
|
schedule.enable
|
||||||
|
? $t('commons.close_schedule')
|
||||||
|
: $t('commons.open_schedule')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<el-switch
|
||||||
|
v-model="schedule.enable"
|
||||||
|
style="margin-left: 20px"
|
||||||
|
></el-switch>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="2">
|
<el-col :span="2">
|
||||||
<el-button :disabled="isReadOnly" type="primary" @click="saveCron" size="mini">{{
|
<el-button
|
||||||
$t('commons.save')
|
:disabled="isReadOnly"
|
||||||
}}
|
type="primary"
|
||||||
|
@click="saveCron"
|
||||||
|
size="mini"
|
||||||
|
>{{ $t("commons.save") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<crontab-result :ex="form.cronValue" ref="crontabResult"/>
|
<crontab-result :ex="form.cronValue" ref="crontabResult" />
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<div class="el-step__icon is-text" style="margin-right: 10px;">
|
<div class="el-step__icon is-text" style="margin-right: 10px">
|
||||||
<div class="el-step__icon-inner">2</div>
|
<div class="el-step__icon-inner">2</div>
|
||||||
</div>
|
</div>
|
||||||
<span>{{ $t('load_test.runtime_config') }}</span>
|
<span>{{ $t("load_test.runtime_config") }}</span>
|
||||||
<div class="ms-mode-div">
|
<div class="ms-mode-div">
|
||||||
<span class="ms-mode-span">{{ $t("run_mode.title") }}:</span>
|
<span class="ms-mode-span">{{ $t("run_mode.title") }}:</span>
|
||||||
<el-radio-group v-model="runConfig.mode" @change="changeMode">
|
<el-radio-group v-model="runConfig.mode" @change="changeMode">
|
||||||
<el-radio label="serial">{{ $t("run_mode.serial") }}</el-radio>
|
<el-radio label="serial">{{ $t("run_mode.serial") }}</el-radio>
|
||||||
<el-radio label="parallel">{{ $t("run_mode.parallel") }}</el-radio>
|
<el-radio label="parallel">{{
|
||||||
|
$t("run_mode.parallel")
|
||||||
|
}}</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 10px;" v-if="haveUICase">
|
<div style="margin-top: 10px" v-if="haveUICase">
|
||||||
<span class="ms-mode-span">{{ $t("浏览器") }}:</span>
|
<span class="ms-mode-span">{{ $t("浏览器") }}:</span>
|
||||||
<el-select
|
<el-select
|
||||||
size="mini"
|
size="mini"
|
||||||
|
@ -72,20 +109,30 @@
|
||||||
<div class="ms-mode-div" v-if="runConfig.mode === 'serial'">
|
<div class="ms-mode-div" v-if="runConfig.mode === 'serial'">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="3">
|
<el-col :span="3">
|
||||||
<span class="ms-mode-span">{{ $t("run_mode.other_config") }}:</span>
|
<span class="ms-mode-span"
|
||||||
|
>{{ $t("run_mode.other_config") }}:</span
|
||||||
|
>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="18">
|
<el-col :span="18">
|
||||||
<div v-if="testType === 'API'">
|
<div v-if="testType === 'API'">
|
||||||
<el-checkbox v-model="runConfig.runWithinResourcePool" style="padding-right: 10px;" :disabled="runMode === 'POOL'">
|
<el-checkbox
|
||||||
{{ $t('run_mode.run_with_resource_pool') }}
|
v-model="runConfig.runWithinResourcePool"
|
||||||
|
style="padding-right: 10px"
|
||||||
|
:disabled="runMode === 'POOL'"
|
||||||
|
>
|
||||||
|
{{ $t("run_mode.run_with_resource_pool") }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<el-select :disabled="!runConfig.runWithinResourcePool" v-model="runConfig.resourcePoolId"
|
<el-select
|
||||||
size="mini">
|
:disabled="!runConfig.runWithinResourcePool"
|
||||||
|
v-model="runConfig.resourcePoolId"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in resourcePools"
|
v-for="item in resourcePools"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:value="item.id">
|
:value="item.id"
|
||||||
|
>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -95,21 +142,31 @@
|
||||||
<div class="ms-mode-div" v-if="runConfig.mode === 'parallel'">
|
<div class="ms-mode-div" v-if="runConfig.mode === 'parallel'">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="3">
|
<el-col :span="3">
|
||||||
<span class="ms-mode-span">{{ $t("run_mode.other_config") }}:</span>
|
<span class="ms-mode-span"
|
||||||
|
>{{ $t("run_mode.other_config") }}:</span
|
||||||
|
>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="18">
|
<el-col :span="18">
|
||||||
<div v-if="testType === 'API'">
|
<div v-if="testType === 'API'">
|
||||||
<el-checkbox v-model="runConfig.runWithinResourcePool" style="padding-right: 10px;" :disabled="runMode === 'POOL'">
|
<el-checkbox
|
||||||
{{ $t('run_mode.run_with_resource_pool') }}
|
v-model="runConfig.runWithinResourcePool"
|
||||||
|
style="padding-right: 10px"
|
||||||
|
:disabled="runMode === 'POOL'"
|
||||||
|
>
|
||||||
|
{{ $t("run_mode.run_with_resource_pool") }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<el-select :disabled="!runConfig.runWithinResourcePool" v-model="runConfig.resourcePoolId"
|
<el-select
|
||||||
size="mini">
|
:disabled="!runConfig.runWithinResourcePool"
|
||||||
|
v-model="runConfig.resourcePoolId"
|
||||||
|
size="mini"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in resourcePools"
|
v-for="item in resourcePools"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
:label="item.name"
|
:label="item.name"
|
||||||
:disabled="!item.api"
|
:disabled="!item.api"
|
||||||
:value="item.id">
|
:value="item.id"
|
||||||
|
>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,45 +175,55 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 失败重试 -->
|
<!-- 失败重试 -->
|
||||||
<div class="ms-mode-div" v-if="isHasLicense">
|
<div class="ms-mode-div">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="3">
|
<el-col :span="3">
|
||||||
<span class="ms-mode-span"> </span>
|
<span class="ms-mode-span"> </span>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="18">
|
<el-col :span="18">
|
||||||
<el-checkbox v-model="runConfig.retryEnable" class="ms-failure-div-right">
|
<el-checkbox
|
||||||
{{ $t('run_mode.retry_on_failure') }}
|
v-model="runConfig.retryEnable"
|
||||||
|
class="ms-failure-div-right"
|
||||||
|
>
|
||||||
|
{{ $t("run_mode.retry_on_failure") }}
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
<span v-if="runConfig.retryEnable">
|
<span v-if="runConfig.retryEnable">
|
||||||
<el-tooltip placement="top">
|
<el-tooltip placement="top">
|
||||||
<div slot="content">{{ $t('run_mode.retry_message') }}</div>
|
<div slot="content">
|
||||||
<i class="el-icon-question" style="cursor: pointer"/>
|
{{ $t("run_mode.retry_message") }}
|
||||||
</el-tooltip>
|
</div>
|
||||||
<span>
|
<i class="el-icon-question" style="cursor: pointer" />
|
||||||
{{ $t('run_mode.retry') }}
|
</el-tooltip>
|
||||||
<el-input-number :value="runConfig.retryNum" v-model="runConfig.retryNum" :min="1" :max="10000000"
|
<span>
|
||||||
size="mini"/>
|
{{ $t("run_mode.retry") }}
|
||||||
|
<el-input-number
|
||||||
{{ $t('run_mode.retry_frequency') }}
|
:value="runConfig.retryNum"
|
||||||
</span>
|
v-model="runConfig.retryNum"
|
||||||
|
:min="1"
|
||||||
|
:max="10000000"
|
||||||
|
size="mini"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{{ $t("run_mode.retry_frequency") }}
|
||||||
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<div class="ms-failure-div" v-if="runConfig.mode === 'serial'" >
|
<div class="ms-failure-div" v-if="runConfig.mode === 'serial'">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="18" :offset="3">
|
<el-col :span="18" :offset="3">
|
||||||
<div>
|
<div>
|
||||||
<el-checkbox v-model="runConfig.onSampleError">{{ $t("api_test.fail_to_stop") }}</el-checkbox>
|
<el-checkbox v-model="runConfig.onSampleError">{{
|
||||||
|
$t("api_test.fail_to_stop")
|
||||||
|
}}</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="haveUICase">
|
<div v-if="haveUICase">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="3">
|
<el-col :span="3"> </el-col>
|
||||||
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="18">
|
<el-col :span="18">
|
||||||
<div style="margin-top: 10px">
|
<div style="margin-top: 10px">
|
||||||
<el-checkbox v-model="runConfig.headlessEnabled">
|
<el-checkbox v-model="runConfig.headlessEnabled">
|
||||||
|
@ -167,15 +234,25 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog width="60%" :title="$t('schedule.generate_expression')" :visible.sync="showCron"
|
<el-dialog
|
||||||
:modal="false">
|
width="60%"
|
||||||
<crontab @hide="showCron=false" @fill="crontabFill" :expression="schedule.value"
|
:title="$t('schedule.generate_expression')"
|
||||||
ref="crontab"/>
|
:visible.sync="showCron"
|
||||||
|
:modal="false"
|
||||||
|
>
|
||||||
|
<crontab
|
||||||
|
@hide="showCron = false"
|
||||||
|
@fill="crontabFill"
|
||||||
|
:expression="schedule.value"
|
||||||
|
ref="crontab"
|
||||||
|
/>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
<el-tab-pane :label="$t('schedule.task_notification')" name="second">
|
<el-tab-pane :label="$t('schedule.task_notification')" name="second">
|
||||||
<ms-schedule-notification :test-id="testId"
|
<ms-schedule-notification
|
||||||
:schedule-receiver-options="scheduleReceiverOptions"/>
|
:test-id="testId"
|
||||||
|
:schedule-receiver-options="scheduleReceiverOptions"
|
||||||
|
/>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
</div>
|
</div>
|
||||||
|
@ -184,30 +261,36 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getCurrentProjectID, getCurrentUser, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
|
import {
|
||||||
import {hasLicense} from "metersphere-frontend/src/utils/permission";
|
getCurrentProjectID,
|
||||||
import {listenGoBack, removeGoBackListener} from "metersphere-frontend/src/utils";
|
getCurrentUser,
|
||||||
|
getCurrentWorkspaceId,
|
||||||
|
} from "metersphere-frontend/src/utils/token";
|
||||||
|
import {
|
||||||
|
listenGoBack,
|
||||||
|
removeGoBackListener,
|
||||||
|
} from "metersphere-frontend/src/utils";
|
||||||
import Crontab from "metersphere-frontend/src/components/cron/Crontab";
|
import Crontab from "metersphere-frontend/src/components/cron/Crontab";
|
||||||
import CrontabResult from "metersphere-frontend/src/components/cron/CrontabResult";
|
import CrontabResult from "metersphere-frontend/src/components/cron/CrontabResult";
|
||||||
import {cronValidate} from "metersphere-frontend/src/utils/cron";
|
import { cronValidate } from "metersphere-frontend/src/utils/cron";
|
||||||
import MsScheduleNotification from "./ScheduleNotification";
|
import MsScheduleNotification from "./ScheduleNotification";
|
||||||
import ScheduleSwitch from "./ScheduleSwitch";
|
import ScheduleSwitch from "./ScheduleSwitch";
|
||||||
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
|
import { ENV_TYPE } from "metersphere-frontend/src/utils/constants";
|
||||||
import MxNotification from "metersphere-frontend/src/components/MxNoticeTemplate";
|
import MxNotification from "metersphere-frontend/src/components/MxNoticeTemplate";
|
||||||
import {
|
import {
|
||||||
createSchedule,
|
createSchedule,
|
||||||
getPlanSchedule,
|
getPlanSchedule,
|
||||||
updateSchedule,
|
updateSchedule,
|
||||||
updateScheduleEnableByPrimyKey
|
updateScheduleEnableByPrimyKey,
|
||||||
} from "@/api/remote/plan/test-plan";
|
} from "@/api/remote/plan/test-plan";
|
||||||
import {saveNotice} from "@/api/notice";
|
import { saveNotice } from "@/api/notice";
|
||||||
import {getProjectMember} from "@/api/user";
|
import { getProjectMember } from "@/api/user";
|
||||||
import {getQuotaValidResourcePools} from "@/api/remote/resource-pool";
|
import { getQuotaValidResourcePools } from "@/api/remote/resource-pool";
|
||||||
import {getProjectConfig} from "@/api/project";
|
import { getProjectConfig } from "@/api/project";
|
||||||
import {getSystemBaseSetting} from "metersphere-frontend/src/api/system";
|
import { getSystemBaseSetting } from "metersphere-frontend/src/api/system";
|
||||||
|
|
||||||
function defaultCustomValidate() {
|
function defaultCustomValidate() {
|
||||||
return {pass: true};
|
return { pass: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -217,47 +300,46 @@ export default {
|
||||||
ScheduleSwitch,
|
ScheduleSwitch,
|
||||||
Crontab,
|
Crontab,
|
||||||
MsScheduleNotification,
|
MsScheduleNotification,
|
||||||
"NoticeTemplate": MxNotification,
|
NoticeTemplate: MxNotification,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
customValidate: {
|
customValidate: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: defaultCustomValidate
|
default: defaultCustomValidate,
|
||||||
},
|
},
|
||||||
isReadOnly: {
|
isReadOnly: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
},
|
},
|
||||||
planCaseIds: [],
|
planCaseIds: [],
|
||||||
type: String,
|
type: String,
|
||||||
//是否含有ui场景 有 ui 场景就要展示 浏览器选项,性能模式
|
//是否含有ui场景 有 ui 场景就要展示 浏览器选项,性能模式
|
||||||
haveUICase: {
|
haveUICase: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
'schedule.value'() {
|
"schedule.value"() {
|
||||||
this.form.cronValue = this.schedule.value;
|
this.form.cronValue = this.schedule.value;
|
||||||
},
|
},
|
||||||
'runConfig.runWithinResourcePool'() {
|
"runConfig.runWithinResourcePool"() {
|
||||||
if (!this.runConfig.runWithinResourcePool) {
|
if (!this.runConfig.runWithinResourcePool) {
|
||||||
this.runConfig.resourcePoolId = null;
|
this.runConfig.resourcePoolId = null;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const validateCron = (rule, cronValue, callback) => {
|
const validateCron = (rule, cronValue, callback) => {
|
||||||
let customValidate = this.customValidate(this.getIntervalTime());
|
let customValidate = this.customValidate(this.getIntervalTime());
|
||||||
if (!cronValue) {
|
if (!cronValue) {
|
||||||
callback(new Error(this.$t('commons.input_content')));
|
callback(new Error(this.$t("commons.input_content")));
|
||||||
} else if (!cronValidate(cronValue)) {
|
} else if (!cronValidate(cronValue)) {
|
||||||
callback(new Error(this.$t('schedule.cron_expression_format_error')));
|
callback(new Error(this.$t("schedule.cron_expression_format_error")));
|
||||||
} else if (!this.intervalValidate()) {
|
} else if (!this.intervalValidate()) {
|
||||||
callback(new Error(this.$t('schedule.cron_expression_interval_error')));
|
callback(new Error(this.$t("schedule.cron_expression_interval_error")));
|
||||||
} else if (!customValidate.pass) {
|
} else if (!customValidate.pass) {
|
||||||
callback(new Error(customValidate.info));
|
callback(new Error(customValidate.info));
|
||||||
} else {
|
} else {
|
||||||
|
@ -269,7 +351,6 @@ export default {
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
runMode: "",
|
runMode: "",
|
||||||
isHasLicense: hasLicense(),
|
|
||||||
result: {},
|
result: {},
|
||||||
scheduleReceiverOptions: [],
|
scheduleReceiverOptions: [],
|
||||||
operation: true,
|
operation: true,
|
||||||
|
@ -281,12 +362,14 @@ export default {
|
||||||
testId: String,
|
testId: String,
|
||||||
showCron: false,
|
showCron: false,
|
||||||
form: {
|
form: {
|
||||||
cronValue: ""
|
cronValue: "",
|
||||||
},
|
},
|
||||||
paramRow: {},
|
paramRow: {},
|
||||||
activeName: 'first',
|
activeName: "first",
|
||||||
rules: {
|
rules: {
|
||||||
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
|
cronValue: [
|
||||||
|
{ required: true, validator: validateCron, trigger: "blur" },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
resourcePools: [],
|
resourcePools: [],
|
||||||
runConfig: {
|
runConfig: {
|
||||||
|
@ -298,10 +381,10 @@ export default {
|
||||||
retryEnable: false,
|
retryEnable: false,
|
||||||
retryNum: 1,
|
retryNum: 1,
|
||||||
browser: "CHROME",
|
browser: "CHROME",
|
||||||
headlessEnabled: true
|
headlessEnabled: true,
|
||||||
},
|
},
|
||||||
projectList: [],
|
projectList: [],
|
||||||
testType: 'API',
|
testType: "API",
|
||||||
planId: String,
|
planId: String,
|
||||||
projectIds: new Set(),
|
projectIds: new Set(),
|
||||||
browsers: [
|
browsers: [
|
||||||
|
@ -312,28 +395,28 @@ export default {
|
||||||
{
|
{
|
||||||
label: this.$t("firefox"),
|
label: this.$t("firefox"),
|
||||||
value: "FIREFOX",
|
value: "FIREFOX",
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
query() {
|
query() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.result = getSystemBaseSetting().then(response => {
|
this.result = getSystemBaseSetting().then((response) => {
|
||||||
if (!response.data.runMode) {
|
if (!response.data.runMode) {
|
||||||
response.data.runMode = 'LOCAL'
|
response.data.runMode = "LOCAL";
|
||||||
}
|
}
|
||||||
this.runMode = response.data.runMode;
|
this.runMode = response.data.runMode;
|
||||||
if (this.runMode === 'POOL') {
|
if (this.runMode === "POOL") {
|
||||||
this.runConfig.runWithinResourcePool = true;
|
this.runConfig.runWithinResourcePool = true;
|
||||||
this.getProjectApplication();
|
this.getProjectApplication();
|
||||||
} else {
|
} else {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
getProjectApplication() {
|
getProjectApplication() {
|
||||||
getProjectConfig(getCurrentProjectID(), "").then(res => {
|
getProjectConfig(getCurrentProjectID(), "").then((res) => {
|
||||||
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
||||||
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
||||||
}
|
}
|
||||||
|
@ -350,14 +433,14 @@ export default {
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
updateTask(param) {
|
updateTask(param) {
|
||||||
this.result = updateScheduleEnableByPrimyKey(param).then(response => {
|
this.result = updateScheduleEnableByPrimyKey(param).then((response) => {
|
||||||
this.taskID = this.paramRow.id;
|
this.taskID = this.paramRow.id;
|
||||||
this.findSchedule(this.taskID);
|
this.findSchedule(this.taskID);
|
||||||
this.$emit("refreshTable");
|
this.$emit("refreshTable");
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
initUserList() {
|
initUserList() {
|
||||||
this.result = getProjectMember().then(response => {
|
this.result = getProjectMember().then((response) => {
|
||||||
this.scheduleReceiverOptions = response.data;
|
this.scheduleReceiverOptions = response.data;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -379,7 +462,7 @@ export default {
|
||||||
this.dialogVisible = true;
|
this.dialogVisible = true;
|
||||||
this.form.cronValue = this.schedule.value;
|
this.form.cronValue = this.schedule.value;
|
||||||
listenGoBack(this.close);
|
listenGoBack(this.close);
|
||||||
this.activeName = 'first';
|
this.activeName = "first";
|
||||||
this.getResourcePools();
|
this.getResourcePools();
|
||||||
this.runConfig.environmentType = ENV_TYPE.JSON;
|
this.runConfig.environmentType = ENV_TYPE.JSON;
|
||||||
this.runConfig.retryEnable = false;
|
this.runConfig.retryEnable = false;
|
||||||
|
@ -387,22 +470,24 @@ export default {
|
||||||
},
|
},
|
||||||
findSchedule() {
|
findSchedule() {
|
||||||
let scheduleResourceID = this.testId;
|
let scheduleResourceID = this.testId;
|
||||||
this.result = getPlanSchedule(scheduleResourceID,"TEST_PLAN_TEST").then(response => {
|
this.result = getPlanSchedule(scheduleResourceID, "TEST_PLAN_TEST").then(
|
||||||
if (response.data != null) {
|
(response) => {
|
||||||
this.schedule = response.data;
|
if (response.data != null) {
|
||||||
if (response.data.config) {
|
this.schedule = response.data;
|
||||||
this.runConfig = JSON.parse(response.data.config);
|
if (response.data.config) {
|
||||||
if (this.runConfig.environmentType) {
|
this.runConfig = JSON.parse(response.data.config);
|
||||||
delete this.runConfig.environmentType;
|
if (this.runConfig.environmentType) {
|
||||||
|
delete this.runConfig.environmentType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.schedule = {
|
||||||
|
value: "",
|
||||||
|
enable: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
this.schedule = {
|
|
||||||
value: '',
|
|
||||||
enable: false
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
},
|
},
|
||||||
crontabFill(value, resultList) {
|
crontabFill(value, resultList) {
|
||||||
//确定后回传的值
|
//确定后回传的值
|
||||||
|
@ -412,22 +497,27 @@ export default {
|
||||||
this.schedule.enable = true;
|
this.schedule.enable = true;
|
||||||
}
|
}
|
||||||
this.$refs.crontabResult.resultList = resultList;
|
this.$refs.crontabResult.resultList = resultList;
|
||||||
this.$refs['from'].validate();
|
this.$refs["from"].validate();
|
||||||
},
|
},
|
||||||
showCronDialog() {
|
showCronDialog() {
|
||||||
let tmp = this.schedule.value;
|
let tmp = this.schedule.value;
|
||||||
this.schedule.value = '';
|
this.schedule.value = "";
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.schedule.value = tmp;
|
this.schedule.value = tmp;
|
||||||
this.showCron = true;
|
this.showCron = true;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
saveCron() {
|
saveCron() {
|
||||||
if (this.runConfig.runWithinResourcePool && this.runConfig.resourcePoolId == null) {
|
if (
|
||||||
this.$warning(this.$t('workspace.env_group.please_select_run_within_resource_pool'));
|
this.runConfig.runWithinResourcePool &&
|
||||||
|
this.runConfig.resourcePoolId == null
|
||||||
|
) {
|
||||||
|
this.$warning(
|
||||||
|
this.$t("workspace.env_group.please_select_run_within_resource_pool")
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.$refs['from'].validate((valid) => {
|
this.$refs["from"].validate((valid) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
this.intervalShortValidate();
|
this.intervalShortValidate();
|
||||||
let formCronValue = this.form.cronValue;
|
let formCronValue = this.form.cronValue;
|
||||||
|
@ -452,8 +542,13 @@ export default {
|
||||||
if (!param.workspaceId) {
|
if (!param.workspaceId) {
|
||||||
param.workspaceId = getCurrentWorkspaceId();
|
param.workspaceId = getCurrentWorkspaceId();
|
||||||
}
|
}
|
||||||
if (this.runConfig.runWithinResourcePool && this.runConfig.resourcePoolId == null) {
|
if (
|
||||||
this.$warning(this.$t('workspace.env_group.please_select_run_within_resource_pool'));
|
this.runConfig.runWithinResourcePool &&
|
||||||
|
this.runConfig.resourcePoolId == null
|
||||||
|
) {
|
||||||
|
this.$warning(
|
||||||
|
this.$t("workspace.env_group.please_select_run_within_resource_pool")
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
param.config = JSON.stringify(this.runConfig);
|
param.config = JSON.stringify(this.runConfig);
|
||||||
|
@ -462,12 +557,12 @@ export default {
|
||||||
//测试计划页面跳转的创建
|
//测试计划页面跳转的创建
|
||||||
if (param.id) {
|
if (param.id) {
|
||||||
updateSchedule(param).then(() => {
|
updateSchedule(param).then(() => {
|
||||||
this.$success(this.$t('commons.save_success'));
|
this.$success(this.$t("commons.save_success"));
|
||||||
this.$emit("refreshTable");
|
this.$emit("refreshTable");
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
createSchedule(param).then(() => {
|
createSchedule(param).then(() => {
|
||||||
this.$success(this.$t('commons.save_success'));
|
this.$success(this.$t("commons.save_success"));
|
||||||
this.$emit("refreshTable");
|
this.$emit("refreshTable");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -475,7 +570,7 @@ export default {
|
||||||
},
|
},
|
||||||
checkScheduleEdit() {
|
checkScheduleEdit() {
|
||||||
if (this.create) {
|
if (this.create) {
|
||||||
this.$message(this.$t('api_test.environment.please_save_test'));
|
this.$message(this.$t("api_test.environment.please_save_test"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -483,13 +578,13 @@ export default {
|
||||||
saveNotice() {
|
saveNotice() {
|
||||||
let param = this.buildParam();
|
let param = this.buildParam();
|
||||||
saveNotice(param).then(() => {
|
saveNotice(param).then(() => {
|
||||||
this.$success(this.$t('commons.save_success'));
|
this.$success(this.$t("commons.save_success"));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
this.dialogVisible = false;
|
this.dialogVisible = false;
|
||||||
this.form.cronValue = '';
|
this.form.cronValue = "";
|
||||||
this.$refs['from'].resetFields();
|
this.$refs["from"].resetFields();
|
||||||
if (!this.schedule.value) {
|
if (!this.schedule.value) {
|
||||||
this.$refs.crontabResult.resultList = [];
|
this.$refs.crontabResult.resultList = [];
|
||||||
}
|
}
|
||||||
|
@ -498,12 +593,12 @@ export default {
|
||||||
intervalShortValidate() {
|
intervalShortValidate() {
|
||||||
if (this.schedule.enable && this.getIntervalTime() < 3 * 60 * 1000) {
|
if (this.schedule.enable && this.getIntervalTime() < 3 * 60 * 1000) {
|
||||||
// return false;
|
// return false;
|
||||||
this.$info(this.$t('schedule.cron_expression_interval_short_error'));
|
this.$info(this.$t("schedule.cron_expression_interval_short_error"));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
resultListChange() {
|
resultListChange() {
|
||||||
this.$refs['from'].validate();
|
this.$refs["from"].validate();
|
||||||
},
|
},
|
||||||
getIntervalTime() {
|
getIntervalTime() {
|
||||||
let resultList = this.$refs.crontabResult.resultList;
|
let resultList = this.$refs.crontabResult.resultList;
|
||||||
|
@ -515,7 +610,7 @@ export default {
|
||||||
alert(executeTileArr);
|
alert(executeTileArr);
|
||||||
},
|
},
|
||||||
getResourcePools() {
|
getResourcePools() {
|
||||||
this.result = getQuotaValidResourcePools().then(response => {
|
this.result = getQuotaValidResourcePools().then((response) => {
|
||||||
this.resourcePools = response.data;
|
this.resourcePools = response.data;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -528,13 +623,12 @@ export default {
|
||||||
computed: {
|
computed: {
|
||||||
isTesterPermission() {
|
isTesterPermission() {
|
||||||
return true;
|
return true;
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
.inp {
|
.inp {
|
||||||
width: 40%;
|
width: 40%;
|
||||||
margin-right: 20px;
|
margin-right: 20px;
|
||||||
|
@ -560,7 +654,8 @@ export default {
|
||||||
.head {
|
.head {
|
||||||
border-bottom: 1px solid var(--primary_color);
|
border-bottom: 1px solid var(--primary_color);
|
||||||
color: var(--primary_color);
|
color: var(--primary_color);
|
||||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
|
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
|
||||||
|
Arial, sans-serif;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 失败重试 -->
|
<!-- 失败重试 -->
|
||||||
<div class="mode-row" v-if="isHasLicense">
|
<div class="mode-row">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-model="runConfig.retryEnable"
|
v-model="runConfig.retryEnable"
|
||||||
class="ms-failure-div-right"
|
class="ms-failure-div-right"
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
<span v-if="runConfig.retryEnable">
|
<span v-if="runConfig.retryEnable">
|
||||||
<el-tooltip placement="top" style="margin: 0 4px 0 2px">
|
<el-tooltip placement="top" style="margin: 0 4px 0 2px">
|
||||||
<div slot="content">{{ $t("run_mode.retry_message") }}</div>
|
<div slot="content">{{ $t("run_mode.retry_message") }}</div>
|
||||||
<i class="el-icon-question" style="cursor: pointer"/>
|
<i class="el-icon-question" style="cursor: pointer" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<span style="margin-left: 10px">
|
<span style="margin-left: 10px">
|
||||||
{{ $t("run_mode.retry") }}
|
{{ $t("run_mode.retry") }}
|
||||||
|
@ -146,9 +146,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mode-row" v-if="runConfig.mode === 'serial'">
|
<div class="mode-row" v-if="runConfig.mode === 'serial'">
|
||||||
<el-checkbox v-model="runConfig.onSampleError">{{
|
<el-checkbox v-model="runConfig.onSampleError"
|
||||||
$t("api_test.fail_to_stop")
|
>{{ $t("api_test.fail_to_stop") }}
|
||||||
}}
|
|
||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -166,38 +165,38 @@
|
||||||
<el-button @click="close">{{ $t("commons.cancel") }}</el-button>
|
<el-button @click="close">{{ $t("commons.cancel") }}</el-button>
|
||||||
<el-dropdown @command="handleCommand" style="margin-left: 5px">
|
<el-dropdown @command="handleCommand" style="margin-left: 5px">
|
||||||
<el-button type="primary">
|
<el-button type="primary">
|
||||||
{{
|
{{ $t("load_test.save_and_run")
|
||||||
$t("load_test.save_and_run")
|
|
||||||
}}<i class="el-icon-arrow-down el-icon--right"></i>
|
}}<i class="el-icon-arrow-down el-icon--right"></i>
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<el-dropdown-menu slot="dropdown">
|
||||||
<el-dropdown-item command="run">{{
|
<el-dropdown-item command="run"
|
||||||
$t("load_test.save_and_run")
|
>{{ $t("load_test.save_and_run") }}
|
||||||
}}
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
<el-dropdown-item command="save">{{
|
<el-dropdown-item command="save"
|
||||||
$t("commons.save")
|
>{{ $t("commons.save") }}
|
||||||
}}
|
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
</el-dropdown>
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<ms-dialog-footer v-else @cancel="close" @confirm="handleRunBatch"/>
|
<ms-dialog-footer v-else @cancel="close" @confirm="handleRunBatch" />
|
||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {getCurrentProjectID, getOwnerProjects, hasLicense} from "@/business/utils/sdk-utils";
|
import {
|
||||||
import {BODY_TYPE as ENV_TYPE} from "@/business/plan/env/ApiTestModel";
|
getCurrentProjectID,
|
||||||
|
getOwnerProjects,
|
||||||
|
} from "@/business/utils/sdk-utils";
|
||||||
|
import { BODY_TYPE as ENV_TYPE } from "@/business/plan/env/ApiTestModel";
|
||||||
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
|
||||||
import EnvPopover from "@/business/plan/env/EnvPopover";
|
import EnvPopover from "@/business/plan/env/EnvPopover";
|
||||||
import {getProjectConfig} from "@/api/project";
|
import { getProjectConfig } from "@/api/project";
|
||||||
import {getSystemBaseSetting} from "metersphere-frontend/src/api/system";
|
import { getSystemBaseSetting } from "metersphere-frontend/src/api/system";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsPlanRunModeWithEnv",
|
name: "MsPlanRunModeWithEnv",
|
||||||
components: {EnvPopover, MsDialogFooter},
|
components: { EnvPopover, MsDialogFooter },
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
runMode: "",
|
runMode: "",
|
||||||
|
@ -220,7 +219,6 @@ export default {
|
||||||
retryNum: 1,
|
retryNum: 1,
|
||||||
browser: "CHROME",
|
browser: "CHROME",
|
||||||
},
|
},
|
||||||
isHasLicense: hasLicense(),
|
|
||||||
projectEnvListMap: {},
|
projectEnvListMap: {},
|
||||||
projectList: [],
|
projectList: [],
|
||||||
projectIds: new Set(),
|
projectIds: new Set(),
|
||||||
|
@ -267,8 +265,12 @@ export default {
|
||||||
open(testType, runModeConfig) {
|
open(testType, runModeConfig) {
|
||||||
if (runModeConfig) {
|
if (runModeConfig) {
|
||||||
this.runConfig = JSON.parse(runModeConfig);
|
this.runConfig = JSON.parse(runModeConfig);
|
||||||
this.runConfig.onSampleError = this.runConfig.onSampleError === 'true' || this.runConfig.onSampleError === true;
|
this.runConfig.onSampleError =
|
||||||
this.runConfig.runWithinResourcePool = this.runConfig.runWithinResourcePool === 'true' || this.runConfig.runWithinResourcePool === true;
|
this.runConfig.onSampleError === "true" ||
|
||||||
|
this.runConfig.onSampleError === true;
|
||||||
|
this.runConfig.runWithinResourcePool =
|
||||||
|
this.runConfig.runWithinResourcePool === "true" ||
|
||||||
|
this.runConfig.runWithinResourcePool === true;
|
||||||
}
|
}
|
||||||
this.runModeVisible = true;
|
this.runModeVisible = true;
|
||||||
this.testType = testType;
|
this.testType = testType;
|
||||||
|
@ -278,21 +280,21 @@ export default {
|
||||||
},
|
},
|
||||||
query() {
|
query() {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.result = getSystemBaseSetting().then(response => {
|
this.result = getSystemBaseSetting().then((response) => {
|
||||||
if (!response.data.runMode) {
|
if (!response.data.runMode) {
|
||||||
response.data.runMode = 'LOCAL'
|
response.data.runMode = "LOCAL";
|
||||||
}
|
}
|
||||||
this.runMode = response.data.runMode;
|
this.runMode = response.data.runMode;
|
||||||
if (this.runMode === 'POOL') {
|
if (this.runMode === "POOL") {
|
||||||
this.runConfig.runWithinResourcePool = true;
|
this.runConfig.runWithinResourcePool = true;
|
||||||
this.getProjectApplication();
|
this.getProjectApplication();
|
||||||
} else {
|
} else {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
},
|
},
|
||||||
getProjectApplication() {
|
getProjectApplication() {
|
||||||
getProjectConfig(getCurrentProjectID(), "").then(res => {
|
getProjectConfig(getCurrentProjectID(), "").then((res) => {
|
||||||
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
if (res.data && res.data.poolEnable && res.data.resourcePoolId) {
|
||||||
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
this.runConfig.resourcePoolId = res.data.resourcePoolId;
|
||||||
}
|
}
|
||||||
|
@ -336,10 +338,9 @@ export default {
|
||||||
this.runConfig.environmentGroupId = id;
|
this.runConfig.environmentGroupId = id;
|
||||||
},
|
},
|
||||||
getWsProjects() {
|
getWsProjects() {
|
||||||
getOwnerProjects()
|
getOwnerProjects().then((res) => {
|
||||||
.then((res) => {
|
this.projectList = res.data;
|
||||||
this.projectList = res.data;
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
showPopover() {
|
showPopover() {
|
||||||
this.projectIds.clear();
|
this.projectIds.clear();
|
||||||
|
@ -353,7 +354,7 @@ export default {
|
||||||
param = this.planCaseIds;
|
param = this.planCaseIds;
|
||||||
} else if (this.type === "plan") {
|
} else if (this.type === "plan") {
|
||||||
url = "/test/plan/case/env";
|
url = "/test/plan/case/env";
|
||||||
param = {id: this.planId};
|
param = { id: this.planId };
|
||||||
}
|
}
|
||||||
this.$post(url, param, (res) => {
|
this.$post(url, param, (res) => {
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
|
|
|
@ -62,7 +62,6 @@ import ApiResult from "@/business/plan/view/comonents/report/detail/component/Ap
|
||||||
import TestPlanReportContainer from "@/business/plan/view/comonents/report/detail/TestPlanReportContainer";
|
import TestPlanReportContainer from "@/business/plan/view/comonents/report/detail/TestPlanReportContainer";
|
||||||
import ApiCases from "@/business/plan/view/comonents/report/detail/component/ApiCases";
|
import ApiCases from "@/business/plan/view/comonents/report/detail/component/ApiCases";
|
||||||
import TabPaneCount from "@/business/plan/view/comonents/report/detail/component/TabPaneCount";
|
import TabPaneCount from "@/business/plan/view/comonents/report/detail/component/TabPaneCount";
|
||||||
import {hasLicense} from "metersphere-frontend/src/utils/permission";
|
|
||||||
import {apiTestExecRerun} from "@/api/remote/ui/api-test";
|
import {apiTestExecRerun} from "@/api/remote/ui/api-test";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -79,7 +78,7 @@ export default {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
this.showRerunBtn = !this.isShare && hasLicense();
|
this.showRerunBtn = !this.isShare;
|
||||||
},
|
},
|
||||||
props: [
|
props: [
|
||||||
'report', 'planId', 'isTemplate', 'isShare', 'shareId', 'isDb'
|
'report', 'planId', 'isTemplate', 'isShare', 'shareId', 'isDb'
|
||||||
|
|
|
@ -215,7 +215,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rerunVerify() {
|
rerunVerify() {
|
||||||
if (hasLicense() && this.fullTreeNodes && this.fullTreeNodes.length > 0 && !this.isShare) {
|
if (this.fullTreeNodes && this.fullTreeNodes.length > 0 && !this.isShare) {
|
||||||
this.fullTreeNodes.forEach(item => {
|
this.fullTreeNodes.forEach(item => {
|
||||||
item.redirect = true;
|
item.redirect = true;
|
||||||
if (item.totalStatus === 'FAIL' || item.totalStatus === 'ERROR' || item.unExecuteTotal > 0
|
if (item.totalStatus === 'FAIL' || item.totalStatus === 'ERROR' || item.unExecuteTotal > 0
|
||||||
|
|
Loading…
Reference in New Issue