# Conflicts:
#	backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java
This commit is contained in:
fit2-zhao 2021-02-05 10:14:14 +08:00
parent 935bcbfafe
commit 38ce17b583
104 changed files with 2413 additions and 1747 deletions

View File

@ -314,11 +314,7 @@ public class APITestController {
apiCountResult.setThisWeekExecutedCount(executedInThisWeekCountNumber);
//统计 失败 成功 以及总数
// List<ApiDataCountResult> api_allExecuteResult = apiReportService.countByProjectIdGroupByExecuteResult(projectId);
List<ApiDataCountResult> allExecuteResult = apiScenarioReportService.countByProjectIdGroupByExecuteResult(projectId);
// List<ApiDataCountResult> allExecuteResult = new ArrayList<>();
// allExecuteResult.addAll(api_allExecuteResult);
// allExecuteResult.addAll(scene_allExecuteResult);
apiCountResult.countScheduleExecute(allExecuteResult);
long allCount = apiCountResult.getExecutedCount();

View File

@ -6,8 +6,6 @@ import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.ReferenceDTO;
import io.metersphere.api.dto.datacount.request.ScheduleInfoRequest;
import io.metersphere.api.dto.datacount.response.TaskInfoResult;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.ScheduleInfoSwaggerUrlRequest;
@ -216,4 +214,6 @@ public class ApiDefinitionController {
public String preview(@RequestBody String jsonSchema) {
return JSONSchemaGenerator.getJson(jsonSchema);
}
}

View File

@ -33,4 +33,6 @@ public class ApiBatchRequest extends ApiDefinitionWithBLOBs {
private List<String> unSelectIds;
private String moduleId;
}

View File

@ -95,7 +95,7 @@ public class MsScenario extends MsTestElement {
}
// 场景变量和环境变量
tree.add(arguments(config));
this.addCsvDataSet(tree, variables);
//this.addCsvDataSet(tree, variables);
this.addCounter(tree, variables);
this.addRandom(tree, variables);
if (CollectionUtils.isNotEmpty(hashTree)) {

View File

@ -100,6 +100,8 @@ public abstract class MsTestElement {
private String refType;
@JSONField(ordinal = 10)
private LinkedList<MsTestElement> hashTree;
@JSONField(ordinal = 11)
private boolean customizeReq;
private MsTestElement parent;
@ -192,15 +194,16 @@ public abstract class MsTestElement {
csvDataSet.setEnabled(true);
csvDataSet.setProperty(TestElement.TEST_CLASS, CSVDataSet.class.getName());
csvDataSet.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
csvDataSet.setName(item.getName());
csvDataSet.setProperty("fileEncoding", item.getEncoding());
csvDataSet.setProperty("variableNames", item.getName());
csvDataSet.setName(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName());
csvDataSet.setProperty("fileEncoding", StringUtils.isEmpty(item.getEncoding()) ? "UTF-8" : item.getEncoding());
if (CollectionUtils.isNotEmpty(item.getFiles())) {
csvDataSet.setProperty("filename", BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName());
}
csvDataSet.setIgnoreFirstLine(false);
csvDataSet.setRecycle(true);
csvDataSet.setProperty("recycle", true);
csvDataSet.setProperty("delimiter", item.getDelimiter());
csvDataSet.setComment(item.getDescription());
csvDataSet.setComment(StringUtils.isEmpty(item.getDescription()) ? "" : item.getDescription());
tree.add(csvDataSet);
});
}
@ -222,7 +225,7 @@ public abstract class MsTestElement {
counterConfig.setVarName(item.getName());
counterConfig.setIncrement(item.getIncrement());
counterConfig.setFormat(item.getValue());
counterConfig.setComment(item.getDescription());
counterConfig.setComment(StringUtils.isEmpty(item.getDescription()) ? "" : item.getDescription());
tree.add(counterConfig);
});
}

View File

@ -13,13 +13,13 @@ public class MsAssertionJSR223 extends MsAssertionType {
private String desc;
private String name;
private String script;
private String language;
private String scriptLanguage;
public MsAssertionJSR223() {
setType(MsAssertionType.JSR223);
}
public boolean isValid() {
return StringUtils.isNotBlank(script) && StringUtils.isNotBlank(language);
return StringUtils.isNotBlank(script) && StringUtils.isNotBlank(scriptLanguage);
}
}

View File

@ -6,6 +6,7 @@ import io.metersphere.api.dto.definition.request.ParameterConfig;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.assertions.*;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
@ -89,7 +90,7 @@ public class MsAssertions extends MsTestElement {
private JSONPathAssertion jsonPathAssertion(MsAssertionJsonPath assertionJsonPath) {
JSONPathAssertion assertion = new JSONPathAssertion();
assertion.setEnabled(true);
assertion.setName(assertionJsonPath.getDescription());
assertion.setName(StringUtils.isEmpty(assertionJsonPath.getDescription()) ? "JSONPathAssertion" : assertionJsonPath.getDescription());
assertion.setProperty(TestElement.TEST_CLASS, JSONPathAssertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("JSONPathAssertionGui"));
assertion.setJsonPath(assertionJsonPath.getExpression());
@ -104,7 +105,7 @@ public class MsAssertions extends MsTestElement {
private XPath2Assertion xPath2Assertion(MsAssertionXPath2 assertionXPath2) {
XPath2Assertion assertion = new XPath2Assertion();
assertion.setEnabled(true);
assertion.setName(assertionXPath2.getExpression());
assertion.setName(StringUtils.isEmpty(assertionXPath2.getExpression()) ? "XPath2Assertion" : assertionXPath2.getExpression());
assertion.setProperty(TestElement.TEST_CLASS, XPath2Assertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("XPath2AssertionGui"));
assertion.setXPathString(assertionXPath2.getExpression());
@ -125,11 +126,11 @@ public class MsAssertions extends MsTestElement {
private JSR223Assertion jsr223Assertion(MsAssertionJSR223 assertionJSR223) {
JSR223Assertion assertion = new JSR223Assertion();
assertion.setEnabled(true);
assertion.setName(assertionJSR223.getDesc());
assertion.setName(StringUtils.isEmpty(assertionJSR223.getDesc()) ? "JSR223Assertion" : assertionJSR223.getDesc());
assertion.setProperty(TestElement.TEST_CLASS, JSR223Assertion.class.getName());
assertion.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
assertion.setProperty("cacheKey", "true");
assertion.setProperty("scriptLanguage", assertionJSR223.getLanguage());
assertion.setProperty("scriptLanguage", assertionJSR223.getScriptLanguage());
assertion.setProperty("script", assertionJSR223.getScript());
return assertion;
}

View File

@ -42,10 +42,10 @@ public class MsIfController extends MsTestElement {
private IfController ifController() {
IfController ifController = new IfController();
ifController.setEnabled(true);
ifController.setName(this.getLabelName());
ifController.setCondition(this.getCondition());
ifController.setName(StringUtils.isEmpty(this.getName()) ? "IfController" : this.getName());
ifController.setProperty(TestElement.TEST_CLASS, IfController.class.getName());
ifController.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("IfControllerPanel"));
ifController.setCondition(this.getCondition());
ifController.setEvaluateAll(false);
ifController.setUseExpression(true);
return ifController;
@ -79,13 +79,13 @@ public class MsIfController extends MsTestElement {
}
if (StringUtils.equals(operator, "is empty")) {
variable = "empty(" + variable + ")";
variable = "!empty(" + variable + ")";
operator = "";
value = "";
}
if (StringUtils.equals(operator, "is not empty")) {
variable = "!empty(" + variable + ")";
variable = "empty(" + variable + ")";
operator = "";
value = "";
}

View File

@ -123,13 +123,13 @@ public class MsLoopController extends MsTestElement {
}
if (StringUtils.equals(operator, "is empty")) {
variable = "empty(" + variable + ")";
variable = "!empty(" + variable + ")";
operator = "";
value = "";
}
if (StringUtils.equals(operator, "is not empty")) {
variable = "!empty(" + variable + ")";
variable = "empty(" + variable + ")";
operator = "";
value = "";
}

View File

@ -57,7 +57,7 @@ public class MsExtract extends MsTestElement {
if (Optional.ofNullable(extract).orElse(extract).length() > 0) {
JSR223PostProcessor shell = new JSR223PostProcessor();
shell.setEnabled(true);
shell.setName(this.getName());
shell.setName(StringUtils.isEmpty(this.getName()) ? "JSR223PostProcessor" : this.getName());
shell.setProperty(TestElement.TEST_CLASS, JSR223PostProcessor.class.getName());
shell.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("TestBeanGUI"));
shell.setProperty("script", "io.metersphere.api.jmeter.JMeterVars.addVars(prev.hashCode(),vars," + "\"" + extract.toString() + "\"" + ");");

View File

@ -207,14 +207,17 @@ public class MsHTTPSamplerProxy extends MsTestElement {
}
final HashTree httpSamplerTree = tree.add(sampler);
// 注意顺序放在config前面会优先于环境的请求头生效
if (CollectionUtils.isNotEmpty(this.headers)) {
setHeader(httpSamplerTree, this.headers);
}
// 通用请求Headers
if (config != null && config.getConfig() != null && config.getConfig().getHttpConfig() != null
&& CollectionUtils.isNotEmpty(config.getConfig().getHttpConfig().getHeaders())) {
setHeader(httpSamplerTree, config.getConfig().getHttpConfig().getHeaders());
}
if (CollectionUtils.isNotEmpty(this.headers)) {
setHeader(httpSamplerTree, this.headers);
}
//判断是否要开启DNS
if (config != null && config.getConfig() != null && config.getConfig().getCommonConfig() != null
@ -287,6 +290,9 @@ public class MsHTTPSamplerProxy extends MsTestElement {
Arguments arguments = new Arguments();
list.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> {
HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), StringUtils.isNotEmpty(keyValue.getValue()) && keyValue.getValue().startsWith("@") ? ScriptEngineUtils.calculate(keyValue.getValue()) : keyValue.getValue());
if (keyValue.getValue() == null) {
httpArgument.setValue("");
}
httpArgument.setAlwaysEncoded(keyValue.isEncode());
if (StringUtils.isNotBlank(keyValue.getContentType())) {
httpArgument.setContentType(keyValue.getContentType());

View File

@ -96,7 +96,7 @@ public class MsJDBCSampler extends MsTestElement {
private Arguments arguments(String name, List<KeyValue> variables) {
Arguments arguments = new Arguments();
if (!variables.isEmpty()) {
if (CollectionUtils.isNotEmpty(variables)) {
arguments.setEnabled(true);
arguments.setName(name);
arguments.setProperty(TestElement.TEST_CLASS, Arguments.class.getName());

View File

@ -12,6 +12,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.ConfigTestElement;
import org.apache.jmeter.modifiers.UserParameters;
import org.apache.jmeter.protocol.tcp.sampler.TCPSampler;
@ -79,6 +80,13 @@ public class MsTCPSampler extends MsTestElement {
}
config.setConfig(getEnvironmentConfig(useEnvironment));
parseEnvironment(config.getConfig());
// 添加环境中的公共变量
Arguments arguments = this.addArguments(config);
if (arguments != null) {
tree.add(this.addArguments(config));
}
final HashTree samplerHashTree = new ListedHashTree();
samplerHashTree.add(tcpConfig());
tree.set(tcpSampler(config), samplerHashTree);
@ -94,7 +102,7 @@ public class MsTCPSampler extends MsTestElement {
}
private void parseEnvironment(EnvironmentConfig config) {
if (config != null && config.getTcpConfig() != null) {
if (!isCustomizeReq() && config != null && config.getTcpConfig() != null) {
this.server = config.getTcpConfig().getServer();
this.port = config.getTcpConfig().getPort();
}

View File

@ -2,6 +2,7 @@ package io.metersphere.api.jmeter;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.service.*;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.base.domain.ApiScenarioReport;
import io.metersphere.base.domain.ApiTestReport;
import io.metersphere.commons.constants.*;
@ -177,10 +178,17 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
} else if (StringUtils.equals(this.runMode, ApiRunMode.JENKINS.name())) {
apiDefinitionService.addResult(testResult);
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.DEFINITION.name());
apiTestService.changeStatus(testId, APITestStatus.Completed);
report = apiReportService.getRunningReport(testResult.getTestId());
apiReportService.complete(testResult, report);
} else if (StringUtils.equals(this.runMode, ApiRunMode.JENKINS_API_PLAN.name())) {
apiDefinitionService.addResult(testResult);
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name());
ApiDefinitionExecResult result = new ApiDefinitionExecResult();
result = apiDefinitionService.getResultByJenkins(debugReportId, ApiRunMode.API_PLAN.name());
report = new ApiTestReport();
report.setStatus(result.getStatus());
report.setId(result.getId());
report.setTriggerMode(ApiRunMode.API.name());
report.setName(apiDefinitionService.getApiCaseInfo(testId).getName());
} else if (StringUtils.equalsAny(this.runMode, ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name())) {
apiDefinitionService.addResult(testResult);
@ -264,9 +272,15 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
if (StringUtils.equals("Success", report.getStatus())) {
event = NoticeConstants.Event.EXECUTE_SUCCESSFUL;
}
if (StringUtils.equals("success", report.getStatus())) {
event = NoticeConstants.Event.EXECUTE_SUCCESSFUL;
}
if (StringUtils.equals("Error", report.getStatus())) {
event = NoticeConstants.Event.EXECUTE_FAILED;
}
if (StringUtils.equals("error", report.getStatus())) {
event = NoticeConstants.Event.EXECUTE_FAILED;
}
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("testName", report.getName());
paramMap.put("id", report.getId());

View File

@ -60,6 +60,18 @@ public class TestResult {
item.getSubRequestResults().forEach(subItem -> {
subItem.setName(item.getName());
});
} else {
if (requestResultMap.containsKey(result.getName())) {
requestResultMap.get(result.getName()).add(item);
} else {
List<RequestResult> requestResults = new LinkedList<>();
requestResults.add(item);
requestResultMap.put(result.getName(), requestResults);
}
item.getSubRequestResults().forEach(subItem -> {
subItem.setName(item.getName());
});
}
});
}

View File

@ -117,6 +117,15 @@ public class ApiAutomationService {
if (setDefultOrders) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
}
if (StringUtils.isNotEmpty(request.getExecuteStatus())) {
Map<String, List<String>> statusFilter = new HashMap<>();
List<String> list = new ArrayList<>();
list.add("Prepare");
list.add("Underway");
list.add("Completed");
statusFilter.put("status", list);
request.setFilters(statusFilter);
}
if (checkThisWeekData) {
if (request.isSelectThisWeedData()) {
Map<String, Date> weekFirstTimeAndLastTime = DateUtils.getWeedFirstTimeAndLastTime(new Date());
@ -387,7 +396,7 @@ public class ApiAutomationService {
try {
boolean isFirst = true;
for (ApiScenarioWithBLOBs item : apiScenarios) {
if (item.getStepTotal() == 0) {
if (item.getStepTotal() == null || item.getStepTotal() == 0) {
// 只有一个场景且没有测试步骤则提示
if (apiScenarios.size() == 1) {
MSException.throwException((item.getName() + "" + Translator.get("automation_exec_info")));
@ -464,6 +473,8 @@ public class ApiAutomationService {
ids = this.getAllScenarioIdsByFontedSelect(
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
}
//检查是否有正在执行中的情景
this.checkScenarioIsRunnng(ids);
List<ApiScenarioWithBLOBs> apiScenarios = extApiScenarioMapper.selectIds(ids);
String runMode = ApiRunMode.SCENARIO.name();
@ -480,6 +491,15 @@ public class ApiAutomationService {
return request.getId();
}
public void checkScenarioIsRunnng(List<String> ids) {
List<ApiScenarioReport> lastReportStatusByIds = apiReportService.selectLastReportByIds(ids);
for (ApiScenarioReport report : lastReportStatusByIds) {
if (StringUtils.equals(report.getStatus(), APITestStatus.Running.name())) {
MSException.throwException(report.getName() + " Is Running!");
}
}
}
/**
* 获取前台查询条件查询的所有(未经分页筛选)数据ID
*
@ -722,7 +742,9 @@ public class ApiAutomationService {
apiScenarios.forEach(item -> {
JSONObject object = JSONObject.parseObject(item.getScenarioDefinition());
object.put("environmentId", request.getEnvironmentId());
item.setScenarioDefinition(JSONObject.toJSONString(object));
if (object != null) {
item.setScenarioDefinition(JSONObject.toJSONString(object));
}
apiScenarioMapper.updateByPrimaryKeySelective(item);
});
}

View File

@ -82,6 +82,8 @@ public class ApiDefinitionService {
private ExtSwaggerUrlScheduleMapper extSwaggerUrlScheduleMapper;
@Resource
private ScheduleMapper scheduleMapper;
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24);
@ -348,7 +350,8 @@ public class ApiDefinitionService {
private String setImportHashTree(ApiDefinitionWithBLOBs apiDefinition) {
String request = apiDefinition.getRequest();
MsHTTPSamplerProxy msHTTPSamplerProxy = JSONObject.parseObject(request, MsHTTPSamplerProxy.class);
msHTTPSamplerProxy.setHashTree(null);
msHTTPSamplerProxy.setId(apiDefinition.getId());
msHTTPSamplerProxy.setHashTree(new LinkedList<>());
apiDefinition.setRequest(JSONObject.toJSONString(msHTTPSamplerProxy));
return request;
}
@ -468,6 +471,14 @@ public class ApiDefinitionService {
return buildAPIReportResult(result);
}
public ApiDefinitionExecResult getResultByJenkins(String testId, String type) {
return extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(testId, type);
}
public ApiTestCaseWithBLOBs getApiCaseInfo(String apiCaseId) {
return apiTestCaseMapper.selectByPrimaryKey(apiCaseId);
}
public ApiDefinitionImport apiTestImport(MultipartFile file, ApiTestImportRequest request) {
ApiImportParser apiImportParser = ApiImportParserFactory.getApiImportParser(request.getPlatform());

View File

@ -415,4 +415,12 @@ public class ApiScenarioReportService {
public List<ApiDataCountResult> countByProjectIdGroupByExecuteResult(String projectId) {
return extApiScenarioReportMapper.countByProjectIdGroupByExecuteResult(projectId);
}
public List<ApiScenarioReport> selectLastReportByIds(List<String> ids) {
if(!ids.isEmpty()){
return extApiScenarioReportMapper.selectLastReportByIds(ids);
}else {
return new ArrayList<>(0);
}
}
}

View File

@ -473,7 +473,7 @@ public class ApiTestCaseService {
String runMode = ApiRunMode.JENKINS.name();
*/
// 调用执行方法
jMeterService.runDefinition(request.getReportId(), jmeterHashTree, request.getReportId(), request.getRunMode());
jMeterService.runDefinition(request.getCaseId(), jmeterHashTree, request.getReportId(), request.getRunMode());
} catch (Exception ex) {
LogUtil.error(ex.getMessage());

View File

@ -171,12 +171,16 @@ public class HistoricalDataUpgradeService {
element = new MsJDBCSampler();
SqlRequest request1 = (SqlRequest) request;
BeanUtils.copyBean(element, request1);
EnvironmentDTO dto = environmentDTOMap.get(request1.getDataSource());
if (dto != null) {
((MsJDBCSampler) element).setEnvironmentId(dto.getEnvironmentId());
((MsJDBCSampler) element).setDataSourceId(dto.getDatabaseConfig().getId());
((MsJDBCSampler) element).setDataSource(dto.getDatabaseConfig());
}
if (CollectionUtils.isEmpty(request1.getVariables())) {
((MsJDBCSampler) element).setVariables(new ArrayList<>());
}
element.setType("JDBCSampler");
}
if (request instanceof TCPRequest) {

View File

@ -304,7 +304,7 @@
<select id="countRunResultByProjectID" resultType="io.metersphere.api.dto.datacount.ApiDataCountResult">
SELECT count(id) AS countNumber, if(last_result is null,"notRun",last_result) AS groupField FROM api_scenario
WHERE project_id = #{0}
WHERE project_id = #{0} AND status != 'Trash'
GROUP BY groupField
</select>

View File

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

View File

@ -194,4 +194,26 @@
WHERE acr.project_id = #{projectId} AND ar.trigger_mode = 'SCHEDULE'
GROUP BY groupField;
</select>
<select id="selectLastReportByIds" resultType="io.metersphere.base.domain.ApiScenarioReport">
SELECT report.* FROM api_scenario_report report
INNER JOIN (
SELECT a.id,a.createTime,a.scenario_id FROM
(
SELECT id AS id,create_time AS createTime, scenario_id AS scenario_id FROM api_scenario_report
WHERE scenario_id in
<foreach collection="scenarioIdList" item="value" separator="," open="(" close=")">
#{value}
</foreach>
UNION
SELECT report.id AS id,report.create_time AS createTime,planScenario.api_scenario_id AS scenario_id FROM api_scenario_report report
INNER JOIN test_plan_api_scenario planScenario ON report.scenario_id = planScenario.id
WHERE planScenario.api_scenario_id in
<foreach collection="scenarioIdList" item="value" separator="," open="(" close=")">
#{value}
</foreach>
ORDER BY createTime DESC
) a GROUP BY a.scenario_id
) orderData ON orderData.id = report.id;
</select>
</mapper>

View File

@ -4,10 +4,12 @@
<select id="list" resultType="io.metersphere.track.dto.TestPlanReportDTO"
parameterType="io.metersphere.track.request.report.QueryTestPlanReportRequest">
SELECT tpr.id AS id, tpr.`name` AS `name`, tp.`name` AS testPlanName, u.name AS creator, tpr.create_time AS createTime,tpr.trigger_Mode AS triggerMode,tpr.status AS status
SELECT tpr.id AS id, tpr.`name` AS `name`, tp.`name` AS testPlanName,
IF(u.name is null,tpr.creator,u.name)AS creator,
tpr.create_time AS createTime,tpr.trigger_Mode AS triggerMode,tpr.status AS status
FROM test_plan tp
INNER JOIN test_plan_report tpr on tp.id = tpr.test_plan_id
INNER JOIN user u on tpr.creator = u.id
LEFT JOIN user u on tpr.creator = u.id
<where>
<if test="name != null">
and tpr.name like CONCAT('%', #{name},'%')

View File

@ -339,7 +339,8 @@
where plan_id = #{planId}
</select>
<select id="listByPlanId" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
SELECT test_plan_api_case.api_case_id as id,"definition" as type,api_test_case.name,test_plan_api_case.status
SELECT test_plan_api_case.id as reportId,test_plan_api_case.api_case_id as id,"definition" as
type,api_test_case.name,test_plan_api_case.status
from test_plan_api_case left join api_test_case on test_plan_api_case.api_case_id=api_test_case.id
inner join
api_definition a
@ -352,7 +353,7 @@
</if>
</where>
UNION ALL
SELECT test_plan_api_scenario.api_scenario_id as id,"scenario" as
SELECT test_plan_api_scenario.id as reportId,test_plan_api_scenario.api_scenario_id as id,"scenario" as
type,api_scenario.name,test_plan_api_scenario.status
from test_plan_api_scenario
left join
@ -366,7 +367,8 @@
</if>
</where>
UNION ALL
select load_test.id as id,"perform" as type,load_test.name as name,test_plan_load_case.status from test_plan_load_case inner join
select test_plan_load_case.id as reportId,load_test.id as id,"perform" as type,load_test.name as
name,test_plan_load_case.status from test_plan_load_case inner join
load_test on
test_plan_load_case.load_case_id =load_test.id
<where>
@ -375,7 +377,8 @@
</if>
</where>
UNION ALL
SELECT test_case.test_id as id,test_case.type as type,test_case.name,test_plan_test_case.status
SELECT test_case.id as reportId,test_case.test_id as id,test_case.type as
type,test_case.name,test_plan_test_case.status
from test_plan_test_case left join test_case on test_plan_test_case.case_id =test_case.id
<where>
<if test="request.planId != null">

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants;
public enum ApiRunMode {
RUN, DEBUG, DEFINITION, SCENARIO, API_PLAN, JENKINS, SCENARIO_PLAN, API, SCHEDULE_API_PLAN, SCHEDULE_SCENARIO_PLAN, SCHEDULE_PERFORMANCE_TEST
RUN, DEBUG, DEFINITION, SCENARIO, API_PLAN, JENKINS_API_PLAN, JENKINS, SCENARIO_PLAN, API, SCHEDULE_API_PLAN, SCHEDULE_SCENARIO_PLAN, SCHEDULE_PERFORMANCE_TEST
}

View File

@ -11,5 +11,9 @@ public enum ResourceStatusEnum {
/**
* 有效
*/
VALID
VALID,
/**
* 删除
*/
DELETE
}

View File

@ -29,7 +29,7 @@ public class TestPlanTestJob extends MsScheduleJob {
private String projectID;
// private PerformanceTestService performanceTestService;
// private PerformanceTestService performanceTestService;
// private TestPlanScenarioCaseService testPlanScenarioCaseService;
// private TestPlanApiCaseService testPlanApiCaseService;
// private ApiTestCaseService apiTestCaseService;
@ -66,13 +66,12 @@ public class TestPlanTestJob extends MsScheduleJob {
this.projectID = jobDataMap.getString("projectId");
businessExecute(context);
}
@Override
void businessExecute(JobExecutionContext context) {
testPlanService.run(this.resourceId,this.projectID,this.userId,ReportTriggerMode.SCHEDULE.name());
testPlanService.run(this.resourceId, this.projectID, this.userId, ReportTriggerMode.SCHEDULE.name());
}
public static JobKey getJobKey(String testId) {

View File

@ -16,9 +16,6 @@ import io.metersphere.performance.controller.request.ReportRequest;
import io.metersphere.performance.service.ReportService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@ -137,11 +134,7 @@ public class PerformanceReportController {
}
@GetMapping("/jtl/download/{reportId}")
public ResponseEntity<byte[]> downloadJtl(@PathVariable String reportId) {
byte[] bytes = reportService.downloadJtl(reportId);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + reportId + ".jtl\"")
.body(bytes);
public void downloadJtlZip(@PathVariable String reportId, HttpServletResponse response) {
reportService.downloadJtlZip(reportId, response);
}
}

View File

@ -8,6 +8,7 @@ import io.metersphere.base.domain.TestResource;
import io.metersphere.base.domain.TestResourcePool;
import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.commons.constants.ResourcePoolTypeEnum;
import io.metersphere.commons.constants.ResourceStatusEnum;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.config.JmeterProperties;
@ -55,13 +56,16 @@ public abstract class AbstractEngine implements Engine {
MSException.throwException("Resource Pool ID is empty");
}
TestResourcePool resourcePool = testResourcePoolService.getResourcePool(resourcePoolId);
if (resourcePool == null) {
if (resourcePool == null || StringUtils.equals(resourcePool.getStatus(), ResourceStatusEnum.DELETE.name())) {
MSException.throwException("Resource Pool is empty");
}
if (!ResourcePoolTypeEnum.K8S.name().equals(resourcePool.getType())
&& !ResourcePoolTypeEnum.NODE.name().equals(resourcePool.getType())) {
MSException.throwException("Invalid Resource Pool type.");
}
if (!StringUtils.equals(resourcePool.getStatus(), ResourceStatusEnum.VALID.name())) {
MSException.throwException("Resource Pool Status is not VALID");
}
// image
String image = resourcePool.getImage();
if (StringUtils.isNotEmpty(image)) {

View File

@ -387,7 +387,7 @@ public class JmeterDocumentParser implements DocumentParser {
element.appendChild(createStringProp(document, "HTTPSampler.contentEncoding", ""));
element.appendChild(createStringProp(document, "HTTPSampler.path", ""));
element.appendChild(createStringProp(document, "HTTPSampler.concurrentPool", "6"));
element.appendChild(createStringProp(document, "HTTPSampler.connect_timeout", ""));
element.appendChild(createStringProp(document, "HTTPSampler.connect_timeout", "60000"));
element.appendChild(createStringProp(document, "HTTPSampler.response_timeout", ""));
hashTree.appendChild(element);
// 空的 hashTree

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtFileContentMapper;
import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.commons.constants.ReportKeys;
@ -23,11 +24,15 @@ import io.metersphere.performance.engine.EngineFactory;
import io.metersphere.service.FileService;
import io.metersphere.service.TestResourceService;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
@ -52,6 +57,8 @@ public class ReportService {
private LoadTestReportDetailMapper loadTestReportDetailMapper;
@Resource
private FileService fileService;
@Resource
private SqlSessionFactory sqlSessionFactory;
public List<ReportDTO> getRecentReportList(ReportRequest request) {
List<OrderRequest> orders = new ArrayList<>();
@ -284,11 +291,28 @@ public class ReportService {
return JSON.parseArray(content, ChartsData.class);
}
public byte[] downloadJtl(String reportId) {
/**
* 流下载 jtl zip
*/
public void downloadJtlZip(String reportId, HttpServletResponse response) {
LoadTestReportWithBLOBs report = getReport(reportId);
if (StringUtils.isBlank(report.getFileId())) {
throw new RuntimeException(Translator.get("load_test_report_file_not_exist"));
}
return fileService.loadFileAsBytes(report.getFileId());
response.setHeader("Content-Disposition", "attachment;fileName=" + reportId + ".zip");
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
ExtFileContentMapper mapper = sqlSession.getMapper(ExtFileContentMapper.class);
try (InputStream inputStream = mapper.selectZipBytes(report.getFileId())) {
ServletOutputStream outputStream = response.getOutputStream();
byte[] buffer = new byte[1024 * 4];
int read;
while ((read = inputStream.read(buffer)) > -1) {
outputStream.write(buffer, 0, read);
}
} catch (Exception e) {
LogUtil.error(e);
MSException.throwException(e);
}
}
}
}

View File

@ -24,8 +24,7 @@ import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import static io.metersphere.commons.constants.ResourceStatusEnum.INVALID;
import static io.metersphere.commons.constants.ResourceStatusEnum.VALID;
import static io.metersphere.commons.constants.ResourceStatusEnum.*;
/**
* @author dongbin
@ -39,8 +38,6 @@ public class TestResourcePoolService {
@Resource
private TestResourceMapper testResourceMapper;
@Resource
private LoadTestMapper loadTestMapper;
@Resource
private NodeResourcePoolService nodeResourcePoolService;
public TestResourcePoolDTO addTestResourcePool(TestResourcePoolDTO testResourcePool) {
@ -55,25 +52,7 @@ public class TestResourcePoolService {
}
public void deleteTestResourcePool(String testResourcePoolId) {
// check test is Running Starting Error
checkTestStatus(testResourcePoolId);
deleteTestResource(testResourcePoolId);
testResourcePoolMapper.deleteByPrimaryKey(testResourcePoolId);
}
public void checkTestStatus(String testResourcePoolId) {
LoadTestExample example = new LoadTestExample();
example.createCriteria()
.andTestResourcePoolIdEqualTo(testResourcePoolId);
List<LoadTest> loadTests = loadTestMapper.selectByExample(example);
StringBuilder loadTestNames = new StringBuilder();
if (loadTests.size() > 0) {
for (LoadTest loadTest : loadTests) {
loadTestNames = loadTestNames.append(loadTest.getName()).append(",");
}
String str = loadTestNames.substring(0, loadTestNames.length() - 1);
MSException.throwException(Translator.get("load_test") + " " + str + " " + Translator.get("test_resource_pool_is_use"));
}
updateTestResourcePoolStatus(testResourcePoolId, DELETE.name());
}
public void updateTestResourcePool(TestResourcePoolDTO testResourcePool) {
@ -109,8 +88,8 @@ public class TestResourcePoolService {
}
testResourcePool.setUpdateTime(System.currentTimeMillis());
testResourcePool.setStatus(status);
// 禁用资源池
if (INVALID.name().equals(status)) {
// 禁用/删除 资源池
if (INVALID.name().equals(status) || DELETE.name().equals(status)) {
testResourcePoolMapper.updateByPrimaryKeySelective(testResourcePool);
return;
}
@ -140,6 +119,7 @@ public class TestResourcePoolService {
if (StringUtils.isNotBlank(request.getStatus())) {
criteria.andStatusEqualTo(request.getStatus());
}
criteria.andStatusNotEqualTo(DELETE.name());
example.setOrderByClause("update_time desc");
List<TestResourcePool> testResourcePools = testResourcePoolMapper.selectByExample(example);
List<TestResourcePoolDTO> testResourcePoolDTOS = new ArrayList<>();

View File

@ -39,8 +39,14 @@ public class TestReviewTestCaseController {
@PostMapping("/batch/delete")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void deleteTestCaseBath(@RequestBody TestReviewCaseBatchRequest request) {
testReviewTestCaseService.deleteTestCaseBath(request);
public void deleteTestCaseBatch(@RequestBody TestReviewCaseBatchRequest request) {
testReviewTestCaseService.deleteTestCaseBatch(request);
}
@PostMapping("/batch/edit/status")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void editTestCaseBatch(@RequestBody TestReviewCaseBatchRequest request) {
testReviewTestCaseService.editTestCaseBatchStatus(request);
}
@PostMapping("/list/all")

View File

@ -39,7 +39,6 @@ import io.metersphere.track.request.testplan.LoadCaseRequest;
import io.metersphere.track.request.testplan.RunTestPlanRequest;
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
@ -179,8 +178,7 @@ public class TestPlanService {
else if (res.getStatus().equals(TestPlanStatus.Completed.name())) {
testPlan.setActualEndTime(null);
} // 已完成->进行中结束时间置空
}
else if (!res.getStatus().equals(TestPlanStatus.Prepare.name()) &&
} else if (!res.getStatus().equals(TestPlanStatus.Prepare.name()) &&
testPlan.getStatus().equals(TestPlanStatus.Prepare.name())) {
testPlan.setActualStartTime(null);
testPlan.setActualEndTime(null);
@ -338,7 +336,7 @@ public class TestPlanService {
testPlanScenarioCaseService.deleteByPlanId(planId);
//删除定时任务
scheduleService.deleteScheduleAndJobByResourceId(planId,ScheduleGroup.TEST_PLAN_TEST.name());
scheduleService.deleteScheduleAndJobByResourceId(planId, ScheduleGroup.TEST_PLAN_TEST.name());
int num = testPlanMapper.deleteByPrimaryKey(planId);
List<String> relatedUsers = new ArrayList<>();
@ -437,7 +435,7 @@ public class TestPlanService {
}
public List<TestPlanDTOWithMetric> listTestPlanByProject(QueryTestPlanRequest request) {
List<TestPlanDTOWithMetric> testPlans=extTestPlanMapper.list(request);
List<TestPlanDTOWithMetric> testPlans = extTestPlanMapper.list(request);
return testPlans;
}
@ -784,12 +782,13 @@ public class TestPlanService {
}
return issues;
}
public List<TestPlanDTO> selectTestPlanByRelevancy(QueryTestPlanRequest params){
return extTestPlanMapper.selectTestPlanByRelevancy(params);
public List<TestPlanDTO> selectTestPlanByRelevancy(QueryTestPlanRequest params) {
return extTestPlanMapper.selectTestPlanByRelevancy(params);
}
public String findTestProjectNameByTestPlanID(String testPlanId) {
return extTestPlanMapper.findTestProjectNameByTestPlanID(testPlanId);
return extTestPlanMapper.findTestProjectNameByTestPlanID(testPlanId);
}
public String findScheduleCreateUserById(String testPlanId) {
@ -877,23 +876,23 @@ public class TestPlanService {
return request.getId();
}
public void run(String testPlanID,String projectID,String userId,String triggerMode){
Map<String,String> planScenarioIdMap;
Map<String,String> apiTestCaseIdMap;
Map<String,String> performanceIdMap;
public void run(String testPlanID, String projectID, String userId, String triggerMode) {
Map<String, String> planScenarioIdMap;
Map<String, String> apiTestCaseIdMap;
Map<String, String> performanceIdMap;
planScenarioIdMap = new LinkedHashMap<>();
apiTestCaseIdMap = new LinkedHashMap<>();
performanceIdMap = new LinkedHashMap<>();
List<TestPlanApiScenario> testPlanApiScenarioList = testPlanScenarioCaseService.getCasesByPlanId(testPlanID);
for (TestPlanApiScenario model :testPlanApiScenarioList) {
planScenarioIdMap.put(model.getApiScenarioId(),model.getId());
for (TestPlanApiScenario model : testPlanApiScenarioList) {
planScenarioIdMap.put(model.getApiScenarioId(), model.getId());
}
List<TestPlanApiCase> testPlanApiCaseList = testPlanApiCaseService.getCasesByPlanId(testPlanID);
for (TestPlanApiCase model :
testPlanApiCaseList) {
apiTestCaseIdMap.put(model.getApiCaseId(),model.getId());
apiTestCaseIdMap.put(model.getApiCaseId(), model.getId());
}
LoadCaseRequest loadCaseRequest = new LoadCaseRequest();
@ -901,24 +900,24 @@ public class TestPlanService {
loadCaseRequest.setProjectId(projectID);
List<TestPlanLoadCaseDTO> testPlanLoadCaseDTOList = testPlanLoadCaseService.list(loadCaseRequest);
for (TestPlanLoadCaseDTO dto : testPlanLoadCaseDTOList) {
performanceIdMap.put(dto.getId(),dto.getLoadCaseId());
performanceIdMap.put(dto.getId(), dto.getLoadCaseId());
}
LogUtil.info("-------------- start testplan schedule ----------");
TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class);
//首先创建testPlanReport然后返回的ID重新赋值为resourceID作为后续的参数
TestPlanReport testPlanReport = testPlanReportService.genTestPlanReport(testPlanID,userId,triggerMode);
TestPlanReport testPlanReport = testPlanReportService.genTestPlanReport(testPlanID, userId, triggerMode);
//执行接口案例任务
for (Map.Entry<String,String> entry: apiTestCaseIdMap.entrySet()) {
for (Map.Entry<String, String> entry : apiTestCaseIdMap.entrySet()) {
String apiCaseID = entry.getKey();
String planCaseID = entry.getValue();
ApiTestCaseWithBLOBs blobs = apiTestCaseService.get(apiCaseID);
//需要更新这里来保证PlanCase的状态能正常更改
apiTestCaseService.run(blobs,UUID.randomUUID().toString(),testPlanReport.getId(),testPlanID,ApiRunMode.SCHEDULE_API_PLAN.name());
apiTestCaseService.run(blobs, UUID.randomUUID().toString(), testPlanReport.getId(), testPlanID, ApiRunMode.SCHEDULE_API_PLAN.name());
}
//执行场景执行任务
if(!planScenarioIdMap.isEmpty()){
if (!planScenarioIdMap.isEmpty()) {
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------");
SchedulePlanScenarioExecuteRequest scenarioRequest = new SchedulePlanScenarioExecuteRequest();
String senarionReportID = UUID.randomUUID().toString();
@ -927,7 +926,7 @@ public class TestPlanService {
scenarioRequest.setProjectId(projectID);
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
scenarioRequest.setExecuteType(ExecuteType.Saved.name());
Map<String, Map<String,String>> testPlanScenarioIdMap = new HashMap<>();
Map<String, Map<String, String>> testPlanScenarioIdMap = new HashMap<>();
testPlanScenarioIdMap.put(testPlanID, planScenarioIdMap);
scenarioRequest.setTestPlanScenarioIDMap(testPlanScenarioIdMap);
scenarioRequest.setReportUserID(userId);
@ -939,7 +938,7 @@ public class TestPlanService {
}
//执行性能测试任务
List<String> performaneReportIDList = new ArrayList<>();
for (Map.Entry<String,String> entry: performanceIdMap.entrySet()) {
for (Map.Entry<String, String> entry : performanceIdMap.entrySet()) {
String id = entry.getKey();
String caseID = entry.getValue();
RunTestPlanRequest performanceRequest = new RunTestPlanRequest();
@ -949,7 +948,7 @@ public class TestPlanService {
String reportId = null;
try {
reportId = performanceTestService.run(performanceRequest);
if(reportId!=null){
if (reportId != null) {
performaneReportIDList.add(reportId);
TestPlanLoadCase testPlanLoadCase = new TestPlanLoadCase();
@ -957,7 +956,7 @@ public class TestPlanService {
testPlanLoadCase.setLoadReportId(reportId);
testPlanLoadCaseService.update(testPlanLoadCase);
}
}catch (Exception e){
} catch (Exception e) {
e.printStackTrace();
}
//更新关联处的报告
@ -967,9 +966,9 @@ public class TestPlanService {
testPlanLoadCaseService.update(loadCase);
}
if(!performaneReportIDList.isEmpty()){
if (!performaneReportIDList.isEmpty()) {
//性能测试时保存性能测试报告ID在结果返回时用于捕捉并进行
testPlanReportService.updatePerformanceInfo(testPlanReport,performaneReportIDList,ReportTriggerMode.SCHEDULE.name());
testPlanReportService.updatePerformanceInfo(testPlanReport, performaneReportIDList, ReportTriggerMode.SCHEDULE.name());
}
}
}

View File

@ -18,6 +18,7 @@ import io.metersphere.track.request.testreview.QueryCaseReviewRequest;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
@ -97,7 +98,7 @@ public class TestReviewTestCaseService {
}
}
public void deleteTestCaseBath(TestReviewCaseBatchRequest request) {
public void deleteTestCaseBatch(TestReviewCaseBatchRequest request) {
checkReviewer(request.getReviewId());
TestCaseReviewTestCaseExample example = new TestCaseReviewTestCaseExample();
example.createCriteria().andIdIn(request.getIds());
@ -105,15 +106,7 @@ public class TestReviewTestCaseService {
}
public void editTestCase(TestCaseReviewTestCase testCaseReviewTestCase) {
String currentUserId = SessionUtils.getUser().getId();
String reviewId = testCaseReviewTestCase.getReviewId();
TestCaseReviewUsersExample testCaseReviewUsersExample = new TestCaseReviewUsersExample();
testCaseReviewUsersExample.createCriteria().andReviewIdEqualTo(reviewId);
List<TestCaseReviewUsers> testCaseReviewUsers = testCaseReviewUsersMapper.selectByExample(testCaseReviewUsersExample);
List<String> reviewIds = testCaseReviewUsers.stream().map(TestCaseReviewUsers::getUserId).collect(Collectors.toList());
if (!reviewIds.contains(currentUserId)) {
MSException.throwException("非此用例的评审人员!");
}
checkReviewCase(testCaseReviewTestCase.getReviewId());
// 记录测试用例评审状态变更
testCaseReviewTestCase.setStatus(testCaseReviewTestCase.getStatus());
@ -137,4 +130,37 @@ public class TestReviewTestCaseService {
public TestReviewCaseDTO get(String reviewId) {
return extTestReviewCaseMapper.get(reviewId);
}
public void editTestCaseBatchStatus(TestReviewCaseBatchRequest request) {
List<String> ids = request.getIds();
if (CollectionUtils.isEmpty(ids)) {
return;
}
if (StringUtils.isBlank(request.getReviewId())) {
return;
} else {
checkReviewCase(request.getReviewId());
}
// 更新状态
if (StringUtils.isNotBlank(request.getStatus())) {
TestCaseExample example = new TestCaseExample();
example.createCriteria().andIdIn(ids);
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
testCase.setReviewStatus(request.getStatus());
testCaseMapper.updateByExampleSelective(testCase, example);
}
}
private void checkReviewCase(String reviewId) {
String currentUserId = SessionUtils.getUser().getId();
TestCaseReviewUsersExample testCaseReviewUsersExample = new TestCaseReviewUsersExample();
testCaseReviewUsersExample.createCriteria().andReviewIdEqualTo(reviewId);
List<TestCaseReviewUsers> testCaseReviewUsers = testCaseReviewUsersMapper.selectByExample(testCaseReviewUsersExample);
List<String> reviewIds = testCaseReviewUsers.stream().map(TestCaseReviewUsers::getUserId).collect(Collectors.toList());
if (!reviewIds.contains(currentUserId)) {
MSException.throwException("非此用例的评审人员!");
}
}
}

View File

@ -297,5 +297,7 @@
</script>
<style scoped>
/deep/ .el-tabs__header {
margin: 0 0 0px;
}
</style>

View File

@ -83,13 +83,14 @@ import MsTableHeader from "../../../common/components/MsTableHeader";
import MsContainer from "../../../common/components/MsContainer";
import MsMainContainer from "../../../common/components/MsMainContainer";
import MsApiReportStatus from "./ApiReportStatus";
import {_filter, _sort, getCurrentProjectID} from "@/common/js/utils";
import {getCurrentProjectID} from "@/common/js/utils";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import ShowMoreBtn from "../../../track/case/components/ShowMoreBtn";
import MsApiReportDetail from "./ApiReportDetail";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
components: {

View File

@ -74,7 +74,7 @@
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
],
principal: [{
required: true,

View File

@ -67,6 +67,19 @@
});
}
}
if (item && item.files) {
item.files.forEach(fileItem => {
if (fileItem.file) {
if (!fileItem.id) {
let fileId = getUUID().substring(0, 12);
fileItem.name = fileItem.file.name;
fileItem.id = fileId;
}
obj.bodyUploadIds.push(fileItem.id);
bodyUploadFiles.push(fileItem.file);
}
});
}
},
recursiveFile(arr, bodyUploadFiles, obj) {
arr.forEach(item => {
@ -86,6 +99,11 @@
this.recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
})
if (request.variables) {
request.variables.forEach(item => {
this.setFiles(item, bodyUploadFiles, obj);
})
}
return bodyUploadFiles;
},
run() {

View File

@ -250,7 +250,7 @@
rules: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
],
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
apiScenarioModuleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
@ -567,6 +567,9 @@
this.$error("不能引用或复制自身!");
return;
}
if (!item.hashTree) {
item.hashTree = [];
}
item.enable === undefined ? item.enable = true : item.enable;
this.scenarioDefinition.push(item);
})

View File

@ -68,7 +68,7 @@
if (response.data) {
response.data.forEach(item => {
let scenarioDefinition = JSON.parse(item.scenarioDefinition);
let obj = {id: item.id, name: item.name, type: "scenario", referenced: 'Copy', resourceId: getUUID(), hashTree: scenarioDefinition.hashTree};
let obj = {id: item.id, name: item.name, type: "scenario", referenced: 'Copy', resourceId: getUUID(), hashTree: scenarioDefinition && scenarioDefinition.hashTree ? scenarioDefinition.hashTree : []};
scenarios.push(obj);
})
this.$emit('addScenario', scenarios);

View File

@ -1,5 +1,5 @@
<template>
<relevance-dialog :title="'接口导入'" ref="relevanceDialog">
<relevance-dialog :title="$t('api_test.definition.api_import')" ref="relevanceDialog">
<template v-slot:aside>
<ms-api-module
@ -28,32 +28,36 @@
ref="apiCaseList"/>
<template v-slot:footer>
<el-button type="primary" @click="copy" @keydown.enter.native.prevent>复制</el-button>
<el-button v-if="!isApiListEnable" type="primary" @click="reference" @keydown.enter.native.prevent>引用</el-button>
<el-button type="primary" @click="copy" @keydown.enter.native.prevent>{{ $t('commons.copy') }}</el-button>
<el-button v-if="!isApiListEnable" type="primary" @click="reference" @keydown.enter.native.prevent>
{{ $t('api_test.scenario.reference') }}
</el-button>
</template>
</relevance-dialog>
</template>
<script>
import ScenarioRelevanceCaseList from "./RelevanceCaseList";
import MsApiModule from "../../../definition/components/module/ApiModule";
import MsContainer from "../../../../common/components/MsContainer";
import MsAsideContainer from "../../../../common/components/MsAsideContainer";
import MsMainContainer from "../../../../common/components/MsMainContainer";
import ScenarioRelevanceApiList from "./RelevanceApiList";
import RelevanceDialog from "../../../../track/plan/view/comonents/base/RelevanceDialog";
export default {
name: "ApiRelevance",
components: {
RelevanceDialog,
ScenarioRelevanceApiList,
MsMainContainer, MsAsideContainer, MsContainer, MsApiModule, ScenarioRelevanceCaseList},
data() {
return {
result: {},
currentProtocol: null,
selectNodeIds: [],
import ScenarioRelevanceCaseList from "./RelevanceCaseList";
import MsApiModule from "../../../definition/components/module/ApiModule";
import MsContainer from "../../../../common/components/MsContainer";
import MsAsideContainer from "../../../../common/components/MsAsideContainer";
import MsMainContainer from "../../../../common/components/MsMainContainer";
import ScenarioRelevanceApiList from "./RelevanceApiList";
import RelevanceDialog from "../../../../track/plan/view/comonents/base/RelevanceDialog";
export default {
name: "ApiRelevance",
components: {
RelevanceDialog,
ScenarioRelevanceApiList,
MsMainContainer, MsAsideContainer, MsContainer, MsApiModule, ScenarioRelevanceCaseList
},
data() {
return {
result: {},
currentProtocol: null,
selectNodeIds: [],
moduleOptions: {},
isApiListEnable: true,
}
@ -118,4 +122,7 @@
</script>
<style scoped>
/deep/ .filter-input {
width: 140px !important;
}
</style>

View File

@ -88,10 +88,9 @@ import {API_METHOD_COLOUR, CASE_PRIORITY} from "../../../definition/model/JsonDa
import {getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "../../../definition/components/list/ApiListContainer";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import {_filter, _sort} from "../../../../../../common/js/utils";
import {_handleSelect, _handleSelectAll} from "../../../../../../common/js/tableUtils";
import MsEnvironmentSelect from "../../../definition/components/case/MsEnvironmentSelect";
import TableSelectCountBar from "./TableSelectCountBar";
import {_filter, _handleSelect, _handleSelectAll, _sort,} from "@/common/js/tableUtils";
export default {
name: "RelevanceApiList",

View File

@ -6,7 +6,8 @@
<ms-environment-select :project-id="projectId" v-if="isTestPlan" :is-read-only="isReadOnly" @setEnvironment="setEnvironment"/>
<el-input placeholder="搜索" @blur="initTable" @keyup.enter.native="initTable" class="search-input" size="small" v-model="condition.name"/>
<el-input :placeholder="$t('api_test.definition.request.select_case')" @blur="initTable"
@keyup.enter.native="initTable" class="search-input" size="small" v-model="condition.name"/>
<el-table v-loading="result.loading"
border
@ -65,34 +66,32 @@
<script>
import MsTableOperator from "../../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import MsTablePagination from "../../../../common/pagination/TablePagination";
import MsTag from "../../../../common/components/MsTag";
import MsBottomContainer from "../../../definition/components/BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../../../definition/components/basis/BatchEdit";
import {API_METHOD_COLOUR, CASE_PRIORITY} from "../../../definition/model/JsonData";
import {getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "../../../definition/components/list/ApiListContainer";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import {_filter, _sort} from "../../../../../../common/js/utils";
import {_handleSelect, _handleSelectAll} from "../../../../../../common/js/tableUtils";
import MsEnvironmentSelect from "../../../definition/components/case/MsEnvironmentSelect";
import TableSelectCountBar from "./TableSelectCountBar";
import MsTableOperator from "../../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import MsTablePagination from "../../../../common/pagination/TablePagination";
import MsTag from "../../../../common/components/MsTag";
import MsBottomContainer from "../../../definition/components/BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../../../definition/components/basis/BatchEdit";
import {API_METHOD_COLOUR, CASE_PRIORITY} from "../../../definition/model/JsonData";
import {getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "../../../definition/components/list/ApiListContainer";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import MsEnvironmentSelect from "../../../definition/components/case/MsEnvironmentSelect";
import TableSelectCountBar from "./TableSelectCountBar";
import {_filter, _handleSelect, _handleSelectAll, _sort} from "@/common/js/tableUtils";
export default {
name: "RelevanceCaseList",
components: {
TableSelectCountBar,
MsEnvironmentSelect,
PriorityTableItem,
ApiListContainer,
MsTableOperatorButton,
MsTableOperator,
MsTablePagination,
MsTag,
export default {
name: "RelevanceCaseList",
components: {
TableSelectCountBar,
MsEnvironmentSelect,
PriorityTableItem,
ApiListContainer,
MsTableOperatorButton,
MsTableOperator,
MsTablePagination,
MsTag,
MsBottomContainer,
ShowMoreBtn,
MsBatchEdit

View File

@ -1,23 +1,21 @@
<template>
<el-card class="api-component">
<div class="header" @click="active(data)">
<div class="header" @click="active(data)">
<fieldset :disabled="data.disabled" class="ms-fieldset">
<slot name="beforeHeaderLeft">
<div v-if="data.index" class="el-step__icon is-text" style="margin-right: 10px;" :style="{'color': color, 'background-color': backgroundColor}">
<div class="el-step__icon-inner">{{data.index}}</div>
</div>
<el-button class="ms-left-buttion" size="small" :style="{'color': color, 'background-color': backgroundColor}">{{title}}</el-button>
</slot>
</fieldset>
<slot name="beforeHeaderLeft">
<div v-if="data.index" class="el-step__icon is-text" style="margin-right: 10px;" :style="{'color': color, 'background-color': backgroundColor}">
<div class="el-step__icon-inner">{{data.index}}</div>
</div>
<el-button class="ms-left-buttion" size="small" :style="{'color': color, 'background-color': backgroundColor}">{{title}}</el-button>
</slot>
<span @click.stop>
<span @click.stop>
<slot name="headerLeft">
<i class="icon el-icon-arrow-right" :class="{'is-active': data.active}"
@click="active(data)" v-if="data.type!='scenario'"/>
<el-input :draggable="draggable" v-if="isShowInput && isShowNameInput" size="small" v-model="data.name" class="name-input"
@blur="isShowInput = false" :placeholder="$t('commons.input_name')" ref="nameEdit"/>
@blur="isShowInput = false" :placeholder="$t('commons.input_name')" ref="nameEdit" :disabled="data.disabled"/>
<span v-else>
{{data.name}}
<i class="el-icon-edit" style="cursor:pointer" @click="editName" v-tester v-if="data.referenced!='REF' && !data.disabled"/>
@ -26,31 +24,31 @@
<slot name="behindHeaderLeft"></slot>
</span>
<div class="header-right" @click.stop>
<slot name="message"></slot>
<el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top">
<el-switch v-model="data.enable" class="enable-switch"/>
</el-tooltip>
<slot name="button"></slot>
<el-tooltip content="Copy" placement="top">
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow"/>
</el-tooltip>
<el-tooltip :content="$t('commons.remove')" placement="top">
<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove"/>
</el-tooltip>
</div>
<div class="header-right" @click.stop>
<slot name="message"></slot>
<el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top">
<el-switch v-model="data.enable" class="enable-switch"/>
</el-tooltip>
<slot name="button"></slot>
<el-tooltip content="Copy" placement="top">
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow"/>
</el-tooltip>
<el-tooltip :content="$t('commons.remove')" placement="top">
<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove"/>
</el-tooltip>
</div>
<fieldset :disabled="data.disabled" class="ms-fieldset">
<div class="header">
<el-collapse-transition>
<div v-if="data.active && showCollapse" :draggable="draggable">
<el-divider></el-divider>
<slot></slot>
</div>
</el-collapse-transition>
</div>
</fieldset>
</div>
<div class="header">
<fieldset :disabled="data.disabled" class="ms-fieldset">
<el-collapse-transition>
<div v-if="data.active && showCollapse" :draggable="draggable">
<el-divider></el-divider>
<slot></slot>
</div>
</el-collapse-transition>
</fieldset>
</div>
</el-card>
</template>
@ -163,8 +161,4 @@
border: 0px;
}
.ms-fieldset {
display: contents;
}
</style>

View File

@ -25,18 +25,8 @@
</el-tooltip>
</template>
<div v-if="request.protocol === 'HTTP'">
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-if="request.url" v-model="request.url" style="width: 85%;margin-top: 10px" size="small">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</el-input>
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-else v-model="request.path" style="width: 85%;margin-top: 10px" size="small">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select>
</el-input>
</div>
<customize-req-info :is-customize-req="isCustomizeReq" :request="request"/>
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
<ms-api-request-form :isShowEnable="true" :referenced="true" :headers="request.headers " :request="request" v-if="request.protocol==='HTTP' || request.type==='HTTPSamplerProxy'"/>
<ms-tcp-basis-parameters :request="request" v-if="request.protocol==='TCP'|| request.type==='TCPSampler'"/>
@ -65,6 +55,7 @@
import {getUUID} from "@/common/js/utils";
import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiResponseComponent from "./ApiResponseComponent";
import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo";
export default {
name: "MsApiComponent",
@ -79,13 +70,13 @@
currentEnvironmentId: String,
},
components: {
CustomizeReqInfo,
ApiBaseComponent, ApiResponseComponent,
MsSqlBasisParameters, MsTcpBasisParameters, MsDubboBasisParameters, MsApiRequestForm, MsRequestResultTail, MsRun
},
data() {
return {
loading: false,
reqOptions: REQ_METHOD,
reportId: "",
runData: [],
isShowInput: false,
@ -98,15 +89,8 @@
//
this.getApiInfo();
if (this.request.protocol === 'HTTP') {
try {
let urlObject = new URL(this.request.url);
let url = urlObject.protocol + "//" + urlObject.host + "/";
} catch (e) {
if (this.request.url) {
this.request.path = this.request.url;
this.request.url = undefined;
}
}
this.setUrl(this.request.url);
this.setUrl(this.request.path);
// auth
if (this.request.hashTree) {
for (let index in this.request.hashTree) {
@ -180,6 +164,17 @@
copyRow() {
this.$emit('copyRow', this.request, this.node);
},
setUrl(url) {
try {
new URL(url);
this.request.url = url;
} catch (e) {
if (url) {
this.request.path = url;
this.request.url = undefined;
}
}
},
getApiInfo() {
if (this.request.id && this.request.referenced === 'REF') {
let requestResult = this.request.requestResult;
@ -192,7 +187,8 @@
this.request.enable = enable;
if (response.data.path && response.data.path != null) {
this.request.path = response.data.path;
this.request.url = response.data.path;
this.request.url = response.data.url;
this.setUrl(this.request.path);
}
if (response.data.method && response.data.method != null) {
this.request.method = response.data.method;
@ -239,6 +235,7 @@
this.loading = true;
this.runData = [];
this.request.useEnvironment = this.currentEnvironmentId;
this.request.customizeReq = this.isCustomizeReq;
let debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare,

View File

@ -16,7 +16,6 @@
import MsApiComponent from "./ApiComponent";
import MsLoopController from "./LoopController";
import MsApiScenarioComponent from "./ApiScenarioComponent";
import {getUUID} from "@/common/js/utils";
export default {
name: "ComponentConfig",

View File

@ -244,7 +244,7 @@
getFails() {
this.error = 0;
this.success = 0;
if (this.requestResult.scenarios) {
if (this.requestResult.scenarios && this.requestResult.scenarios != null) {
this.requestResult.scenarios.forEach((scenario) => {
if (scenario.requestResults) {
scenario.requestResults.forEach(item => {
@ -255,7 +255,7 @@
})
}
})
this.success = this.requestResult.scenarios.length - this.error;
this.success = this.requestResult.scenarios && this.requestResult.scenarios != null ? this.requestResult.scenarios.length - this.error : 0;
}
},
getReport() {
@ -267,6 +267,9 @@
if (this.isNotRunning) {
try {
this.requestResult = JSON.parse(this.report.content);
if (!this.requestResult) {
this.requestResult = {scenarios: []};
}
this.controller.requestResult = this.requestResult;
switch (this.controller.loopType) {
case "LOOP_COUNT":
@ -282,14 +285,11 @@
break;
}
this.getFails();
if (!this.requestResult) {
this.requestResult = {scenarios: []};
}
} catch (e) {
throw e;
}
this.loading = false;
this.activeName = this.requestResult && this.requestResult.scenarios ? this.requestResult.scenarios[0].name : "";
this.activeName = this.requestResult && this.requestResult.scenarios && this.requestResult.scenarios != null && this.requestResult.scenarios.length > 0 ? this.requestResult.scenarios[0].name : "";
} else {
setTimeout(this.getReport, 2000)
}

View File

@ -127,40 +127,42 @@
</ms-delete-confirm>
<ms-dialog-footer style="float: right;margin: 20px"
@confirm="confirm">
@confirm="confirm" @cancel="cancel">
</ms-dialog-footer>
</el-card>
</template>
<script>
import MsCreateBox from '../../../../settings/CreateBox';
import MsTablePagination from '../../../../../components/common/pagination/TablePagination';
import MsTableHeader from "../../../../common/components/MsTableHeader";
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import MsTableOperator from "../../../../common/components/MsTableOperator";
import PlanStatusTableItem from "../../../../track/common/tableItems/plan/PlanStatusTableItem";
import PlanStageTableItem from "../../../../track/common/tableItems/plan/PlanStageTableItem";
import {_filter, _sort, checkoutTestManagerOrTestUser} from "@/common/js/utils";
import TestReportTemplateList from "../../../../track/plan/view/comonents/TestReportTemplateList";
import TestCaseReportView from "../../../../track/plan/view/comonents/report/TestCaseReportView";
import MsDeleteConfirm from "../../../../common/components/MsDeleteConfirm";
import {TEST_PLAN_CONFIGS} from "../../../../common/components/search/search-components";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import {getCurrentProjectID} from "../../../../../../common/js/utils";
import MsCreateBox from '../../../../settings/CreateBox';
import MsTablePagination from '../../../../../components/common/pagination/TablePagination';
import MsTableHeader from "../../../../common/components/MsTableHeader";
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
import MsTableOperator from "../../../../common/components/MsTableOperator";
import PlanStatusTableItem from "../../../../track/common/tableItems/plan/PlanStatusTableItem";
import PlanStageTableItem from "../../../../track/common/tableItems/plan/PlanStageTableItem";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import TestReportTemplateList from "../../../../track/plan/view/comonents/TestReportTemplateList";
import TestCaseReportView from "../../../../track/plan/view/comonents/report/TestCaseReportView";
import MsDeleteConfirm from "../../../../common/components/MsDeleteConfirm";
import {TEST_PLAN_CONFIGS} from "../../../../common/components/search/search-components";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import {getCurrentProjectID} from "../../../../../../common/js/utils";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "TestPlanList",
components: {
MsDeleteConfirm,
TestCaseReportView,
TestReportTemplateList,
PlanStageTableItem,
PlanStatusTableItem,
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination
},
data() {
export default {
name: "TestPlanList",
components: {
MsDeleteConfirm,
TestCaseReportView,
TestReportTemplateList,
PlanStageTableItem,
PlanStatusTableItem,
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination
},
data() {
return {
result: {},
selection: [],
@ -203,8 +205,12 @@
confirm() {
if (this.selection.length==0) {
this.$warning(this.$t("api_test.definition.request.test_plan_select"));
}else{
this.$emit('addTestPlan', this.selection);
}
this.$emit('addTestPlan', this.selection);
},
cancel(){
this.$emit('cancel');
},
select(selection) {
this.selection = selection.map(s => s.id);

View File

@ -1,14 +1,22 @@
<template>
<el-dialog :title="$t('api_test.scenario.variables')" :close-on-click-modal="false"
:visible.sync="visible" class="environment-dialog" width="60%"
@close="close">
:visible.sync="visible" class="visible-dialog" width="60%"
@close="close" v-loading="loading">
<div>
<el-input placeholder="变量名称搜索" style="width: 50%;margin: 0px 0px 10px" v-model="selectVariable" size="small" @change="filter" @keyup.enter="filter">
<el-select v-model="searchType" slot="prepend" placeholder="类型" style="width: 90px" @change="filter">
<el-option value="CONSTANT" label="常量"></el-option>
<el-option value="LIST" label="列表"></el-option>
<el-option value="CSV" label="CSV"></el-option>
<el-option value="COUNTER" label="计数器"></el-option>
<el-option value="RANDOM" label="随机数"></el-option>
</el-select>
</el-input>
<el-row>
<el-col :span="12">
<div style="border:1px #DCDFE6 solid; min-height: 400px;border-radius: 4px ;width: 100% ;">
<el-table ref="table" border :data="variables" class="adjust-table" @select-all="select" @select="select"
v-loading="loading" @row-click="edit">
v-loading="loading" @row-click="edit" height="400px" :row-class-name="tableRowClassName">
<el-table-column type="selection" width="38"/>
<el-table-column prop="num" label="ID" sortable/>
<el-table-column prop="name" :label="$t('api_test.variable_name')" sortable show-overflow-tooltip/>
@ -28,15 +36,12 @@
<ms-edit-list-value v-if="editData.type=='LIST'" ref="listValue" :editData="editData"/>
<ms-edit-csv v-if="editData.type=='CSV'" ref="csv" :editData.sync="editData"/>
</el-col>
</el-row>
</div>
<template v-slot:footer>
<div style="margin:20px">
<div>
<el-button style="margin-right:10px" @click="deleteVariable">{{$t('commons.delete')}}</el-button>
<el-dropdown split-button type="primary" @command="handleClick" @click="handleClick('CONSTANT')" placement="top-end">
{{$t('commons.add')}}
<el-dropdown-menu slot="dropdown">
@ -49,8 +54,6 @@
</el-dropdown>
</div>
</template>
</el-dialog>
</template>
@ -80,6 +83,9 @@
data() {
return {
variables: [],
searchType: "",
selectVariable: "",
condition: {},
types: new Map([
['CONSTANT', '常量'],
['LIST', '列表'],
@ -105,6 +111,12 @@
edit(row) {
this.editData = row;
},
tableRowClassName(row) {
if (row.row.hidden) {
return 'ms-variable-hidden-row';
}
return '';
},
addParameters(v) {
v.id = getUUID();
if (v.type === 'CSV') {
@ -133,10 +145,13 @@
this.visible = false;
let saveVariables = [];
this.variables.forEach(item => {
item.hidden = undefined;
if (item.name && item.name != "") {
saveVariables.push(item);
}
})
this.selectVariable = "";
this.searchType = "";
this.$emit('setVariables', saveVariables);
},
deleteVariable() {
@ -150,11 +165,48 @@
this.variables.splice(index, 1);
})
this.selection = [];
}
},
filter() {
let datas = [];
this.variables.forEach(item => {
if (this.searchType && this.searchType != "" && this.selectVariable && this.selectVariable != "") {
if ((item.type && item.type.toLowerCase().indexOf(this.searchType.toLowerCase()) == -1) || (item.name && item.name.toLowerCase().indexOf(this.selectVariable.toLowerCase()) == -1)) {
item.hidden = true;
} else {
item.hidden = undefined;
}
}
else if (this.selectVariable && this.selectVariable != "") {
if (item.name && item.name.toLowerCase().indexOf(this.selectVariable.toLowerCase()) == -1) {
item.hidden = true;
} else {
item.hidden = undefined;
}
}
else if (this.searchType && this.searchType != "") {
if (item.type && item.type.toLowerCase().indexOf(this.searchType.toLowerCase()) == -1) {
item.hidden = true;
} else {
item.hidden = undefined;
}
} else {
item.hidden = undefined;
}
datas.push(item);
})
this.variables = datas;
},
createFilter(queryString) {
return item => {
return (item.type && item.type.toLowerCase().indexOf(queryString.toLowerCase()) !== -1);
};
},
}
}
</script>
<style scoped>
<style>
.ms-variable-hidden-row {
display: none;
}
</style>

View File

@ -1,4 +1,5 @@
<template>
<div>
<ms-container v-if="renderComponent">
<ms-aside-container>
<ms-api-module
@ -9,6 +10,7 @@
@debug="debug"
@saveAsEdit="editApi"
@setModuleOptions="setModuleOptions"
@setNodeTree="setNodeTree"
@enableTrash="enableTrash"
:type="'edit'"
ref="nodeTree"/>
@ -25,6 +27,8 @@
<!-- 列表集合 -->
<ms-api-list
v-if="item.type === 'list' && isApiListEnable"
:module-tree="nodeTree"
:module-options="moduleOptions"
:current-protocol="currentProtocol"
:visible="visible"
:currentRow="currentRow"
@ -102,8 +106,12 @@
</template>
</el-tab-pane>
</el-tabs>
</ms-main-container>
</ms-container>
</div>
</template>
<script>
import MsApiList from './components/list/ApiList';
@ -177,7 +185,7 @@
currentModule: null,
selectNodeIds: [],
currentApi: {},
moduleOptions: {},
moduleOptions: [],
trashEnable: false,
apiTabs: [{
title: this.$t('api_test.definition.api_title'),
@ -187,7 +195,8 @@
}],
isApiListEnable: true,
syncTabs: [],
projectId: ""
projectId: "",
nodeTree: []
}
},
mounted() {
@ -215,6 +224,7 @@
}
},
methods: {
changeRedirectParam(redirectIDParam) {
this.redirectID = redirectIDParam;
},
@ -394,6 +404,9 @@
setModuleOptions(data) {
this.moduleOptions = data;
},
setNodeTree(data) {
this.nodeTree = data;
},
changeSelectDataRangeAll(tableType) {
this.$route.params.dataSelectRange = 'all';
},

View File

@ -127,6 +127,9 @@
this.request = createComponent("JDBCSampler");
this.currentApi.request = this.request;
}
if (!this.currentApi.request.variables) {
this.currentApi.request.variables = [];
}
},
initDubbo() {
if (!this.setRequest()) {

View File

@ -9,27 +9,28 @@
</template>
<script>
import MsDrawer from "../../../../common/components/MsDrawer";
import MsInstructionsIcon from "../../../../common/components/MsInstructionsIcon";
import MsDrawer from "../../../../common/components/MsDrawer";
import MsInstructionsIcon from "../../../../common/components/MsInstructionsIcon";
export default {
name: "MsApiJsonpathSuggest",
components: {MsInstructionsIcon, MsDrawer},
data() {
return {
visible: false,
isCheckAll: false,
data: {},
};
},
props: {
tip: {
type: String,
default() {
return ""
}
},
let dotReplace = "#DOT_MASK#";
export default {
name: "MsApiJsonpathSuggest",
components: {MsInstructionsIcon, MsDrawer},
data() {
return {
visible: false,
isCheckAll: false,
data: {},
};
},
props: {
tip: {
type: String,
default() {
return ""
}
},
},
methods: {
close() {
this.visible = false;
@ -50,14 +51,46 @@
this.visible = true;
},
pathChangeHandler(data) {
let paramNames = data.split('.');
let result = this.getParamValue(this.data, 0, paramNames);
let paramNames = [];
let result = {};
try {
paramNames = this.parseSpecialChar(data);
result = this.getParamValue(this.data, 0, paramNames);
} catch (e) {
result = {};
result.key = 'var';
}
result.path = '$.' + data;
this.$emit('addSuggest', result);
},
// .
parseSpecialChar(data) {
let paramNames = [];
let reg = /\['.*'\]/;
let searchStr = reg.exec(data);
if (searchStr) {
searchStr.forEach(item => {
if (data.startsWith("['")) {
data = data.replace(item, item.replace('.', dotReplace));
} else {
data = data.replace(item, '.' + item.replace('.', dotReplace));
}
});
paramNames = data.split('.');
} else {
paramNames = data.split('.');
}
for (let i in paramNames) {
if (paramNames[i].search(reg) > -1) {
paramNames[i] = paramNames[i].substring(2, paramNames[i].length - 2);
}
paramNames[i] = paramNames[i].replace(dotReplace, '.');
}
return paramNames;
},
getParamValue(obj, index, params) {
if (params.length < 1) {
return "";
return {};
}
let param = params[index];

View File

@ -86,7 +86,7 @@
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
],
path: [{required: true, message: this.$t('api_test.definition.request.path_info'), trigger: 'blur'}, {validator: validateURL, trigger: 'blur'}],
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],

View File

@ -288,6 +288,9 @@
url = "/api/testcase/update";
} else {
tmp.request.path = this.api.path;
if (tmp.request.protocol != "dubbo://" && tmp.request.protocol != "DUBBO") {
tmp.request.method = this.api.method;
}
}
if (tmp.tags instanceof Array) {
tmp.tags = JSON.stringify(tmp.tags);

View File

@ -54,7 +54,8 @@
import {CASE_ORDER} from "../../model/JsonData";
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
import MsBatchEdit from "../basis/BatchEdit";
import {CASE_PRIORITY, REQ_METHOD} from "../../model/JsonData";
// import {CASE_PRIORITY, REQ_METHOD} from "../../model/JsonData";
import {CASE_PRIORITY,API_METHOD_COLOUR, API_STATUS, REQ_METHOD, TCP_METHOD, SQL_METHOD, DUBBO_METHOD} from "../../model/JsonData";
export default {
name: 'ApiCaseList',
@ -313,6 +314,17 @@
this.$warning("请选择用例!");
return;
}
// //
if (this.currentApi.protocol == 'HTTP') {
this.valueArr.method = REQ_METHOD;
} else if (this.currentApi.protocol == 'TCP') {
this.valueArr.method = TCP_METHOD;
} else if (this.currentApi.protocol == 'SQL') {
this.valueArr.method = SQL_METHOD;
} else if (this.currentApi.protocol == 'DUBBO') {
this.valueArr.method = DUBBO_METHOD;
}
this.$refs.batchEdit.open();
},
batchEdit(form) {

View File

@ -109,29 +109,29 @@
<script>
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import MsResponseText from "../response/ResponseText";
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
import {KeyValue} from "../../model/ApiTestModel";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import MsResponseText from "../response/ResponseText";
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
import {KeyValue} from "../../model/ApiTestModel";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
export default {
name: "MsAddCompleteHttpApi",
components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag},
data() {
let validateURL = (rule, value, callback) => {
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
callback(this.$t('api_test.definition.request.path_valid_info'));
}
callback();
};
return {
export default {
name: "MsAddCompleteHttpApi",
components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag},
data() {
let validateURL = (rule, value, callback) => {
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
callback(this.$t('api_test.definition.request.path_valid_info'));
}
callback();
};
return {
rule: {
name: [
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
{max: 100, message: this.$t('test_track.length_less_than') + '100', trigger: 'blur'}
],
path: [{required: true, message: this.$t('api_test.definition.request.path_info'), trigger: 'blur'}, {
validator: validateURL,

View File

@ -147,8 +147,6 @@
},
saveAs() {
let obj = {request: this.request};
obj.request.server = this.debugForm.server;
obj.request.port = this.debugForm.port;
obj.server = this.debugForm.server;
obj.port = this.debugForm.port;
obj.request.id = getUUID();

View File

@ -15,6 +15,7 @@
@select-all="handleSelectAll"
@filter-change="filter"
@sort-change="sort"
@header-dragend="headerDragend"
@select="handleSelect" :height="screenHeight">
<el-table-column type="selection" width="50"/>
@ -25,25 +26,28 @@
@selectPageAll="isSelectDataAll(false)"
@selectAll="isSelectDataAll(true)"/>
<el-table-column width="30" :resizable="false" align="center">
<el-table-column width="30" :resizable="false" min-width="30px" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
</template>
</el-table-column>
<el-table-column prop="num" label="ID" show-overflow-tooltip>
<el-table-column prop="num" label="ID" min-width="120px" show-overflow-tooltip>
<template slot-scope="scope">
<el-tooltip content="编辑">
<a style="cursor:pointer" @click="handleTestCase(scope.row)"> {{ scope.row.num }} </a>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="name" :label="$t('test_track.case.name')" show-overflow-tooltip/>
<el-table-column prop="name" min-width="160px" :label="$t('test_track.case.name')" show-overflow-tooltip/>
<el-table-column
prop="priority"
:filters="priorityFilters"
column-key="priority"
min-width="120px"
:label="$t('test_track.case.priority')"
show-overflow-tooltip>
<template v-slot:default="scope">
@ -54,10 +58,12 @@
<el-table-column
sortable="custom"
prop="path"
min-width="180px"
:label="$t('api_test.definition.api_path')"
show-overflow-tooltip/>
<el-table-column prop="tags" :label="$t('commons.tag')">
<el-table-column prop="tags" min-width="120px" :label="$t('commons.tag')">
<template v-slot:default="scope">
<div v-for="(itemName,index) in scope.row.tags" :key="index">
<ms-tag type="success" effect="plain" :content="itemName"/>
@ -68,12 +74,14 @@
<el-table-column
prop="createUser"
:label="'创建人'"
show-overflow-tooltip/>
<el-table-column
sortable="custom"
width="160"
min-width="160"
:label="$t('api_test.definition.api_last_time')"
prop="updateTime">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
@ -82,11 +90,6 @@
<el-table-column fixed="right" v-if="!isReadOnly" :label="$t('commons.operating')" min-width="130" align="center">
<template v-slot:default="scope">
<!--<el-button type="text" @click="reductionApi(scope.row)" v-if="trashEnable">{{$t('commons.reduction')}}</el-button>-->
<!-- <el-button type="text" @click="handleTestCase(scope.row)" v-if="!trashEnable">{{ $t('commons.edit') }}-->
<!-- </el-button>-->
<!-- <el-button type="text" @click="handleDelete(scope.row)" style="color: #F56C6C">{{ $t('commons.delete') }}-->
<!-- </el-button>-->
<ms-table-operator-button :tip="$t('commons.edit')" icon="el-icon-edit" @exec="handleTestCase(scope.row)" v-tester/>
<ms-table-operator-button :tip="$t('commons.delete')" icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger" v-tester/>
<ms-api-case-table-extend-btns @showCaseRef="showCaseRef" @showEnvironment="showEnvironment" @createPerformance="createPerformance" :row="scope.row" v-tester/>
@ -129,18 +132,16 @@ import {API_METHOD_COLOUR, CASE_PRIORITY, DUBBO_METHOD, REQ_METHOD, SQL_METHOD,
import {getBodyUploadFiles, getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "./ApiListContainer";
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
import {_filter, _sort} from "../../../../../../common/js/utils";
import {_handleSelect, _handleSelectAll} from "../../../../../../common/js/tableUtils";
import MsApiCaseTableExtendBtns from "../reference/ApiCaseTableExtendBtns";
import MsReferenceView from "../reference/ReferenceView";
import MsSetEnvironment from "@/business/components/api/definition/components/basis/SetEnvironment";
import TestPlan from "@/business/components/api/definition/components/jmeter/components/test-plan";
import ThreadGroup from "@/business/components/api/definition/components/jmeter/components/thread-group";
import {parseEnvironment} from "@/business/components/api/test/model/EnvironmentModel";
// import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll";
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
import MsTableAdvSearchBar from "@/business/components/common/components/search/MsTableAdvSearchBar";
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
import {_filter, _handleSelect, _handleSelectAll, _sort,} from "@/common/js/tableUtils";
export default {
name: "ApiCaseSimpleList",
@ -505,6 +506,14 @@ export default {
this.clickRow = row;
this.$refs.setEnvironment.open(row);
},
headerDragend(newWidth,oldWidth,column,event){
let finalWidth = newWidth;
if(column.minWidth>finalWidth){
finalWidth = column.minWidth;
}
column.width = finalWidth;
column.realWidth = finalWidth;
},
createPerformance(row, environment) {
/**
* 思路调用后台创建性能测试的方法把当前案例的hashTree在后台转化为jmx并文件创建性能测试

View File

@ -126,6 +126,7 @@
buildNodePath(node, {path: ''}, moduleOptions);
});
this.$emit('setModuleOptions', moduleOptions);
this.$emit('setNodeTree', this.data);
if (this.$refs.nodeTree) {
this.$refs.nodeTree.filter(this.condition.filterText);
}

View File

@ -145,7 +145,7 @@ export default {
}
.filter-input {
width: 175px;
width: 174px;
padding-left: 3px;
}

View File

@ -1,5 +1,5 @@
<template>
<div>
<div v-loading="isReloadData">
<el-row>
<el-col :span="21" style="padding-bottom: 20px">
<div style="border:1px #DCDFE6 solid; height: 100%;border-radius: 4px ;width: 100% ;margin: 20px">
@ -28,7 +28,7 @@
</el-col>
<el-col :span="8">
<el-form-item :label="$t('api_test.request.sql.dataSource')" prop="dataSourceId" style="margin-left: 10px">
<el-select v-model="request.dataSourceId" size="small">
<el-select v-model="request.dataSourceId" size="small" @change="reload">
<el-option v-for="(item, index) in databaseConfigsOptions" :key="index" :value="item.id" :label="item.name"/>
</el-select>
</el-form-item>
@ -143,7 +143,7 @@
activeName: "variables",
rules: {
environmentId: [{required: true, message: this.$t('api_test.definition.request.run_env'), trigger: 'change'}],
dataSourceId: [{required: true, message: this.$t('api_test.request.sql.dataSource'), trigger: 'change'}],
dataSourceId: [{required: true, message: this.$t('api_test.request.sql.dataSource'), trigger: 'blur'}],
},
}
},

View File

@ -63,23 +63,23 @@
</template>
<script>
import MsAssertionResults from "./AssertionResults";
import MsCodeEdit from "../MsCodeEdit";
import MsDropdown from "../../../../common/components/MsDropdown";
import {BODY_FORMAT} from "../../model/ApiTestModel";
import MsSqlResultTable from "./SqlResultTable";
import MsAssertionResults from "./AssertionResults";
import MsCodeEdit from "../MsCodeEdit";
import MsDropdown from "../../../../common/components/MsDropdown";
import {BODY_FORMAT} from "../../model/ApiTestModel";
import MsSqlResultTable from "./SqlResultTable";
export default {
name: "MsResponseResult",
export default {
name: "MsResponseResult",
components: {
MsDropdown,
MsCodeEdit,
MsAssertionResults,
MsSqlResultTable
},
components: {
MsDropdown,
MsCodeEdit,
MsAssertionResults,
MsSqlResultTable
},
props: {
props: {
response: Object,
currentProtocol: String,
},
@ -107,7 +107,7 @@
this.mode = mode;
},
setBodyType() {
if (!this.response.responseResult.headers) {
if (!this.response.responseResult || !this.response.responseResult.headers) {
return;
}
if (this.response.responseResult.headers.indexOf("Content-Type: application/json") > 0) {

View File

@ -1,5 +1,6 @@
import {
Arguments,
ConstantTimer as JMXConstantTimer,
CookieManager,
DNSCacheManager,
DubboSample,
@ -10,6 +11,7 @@ import {
HTTPSamplerArguments,
HTTPsamplerFiles,
HTTPSamplerProxy,
IfController as JMXIfController,
JDBCDataSource,
JDBCSampler,
JSONPathAssertion,
@ -20,12 +22,11 @@ import {
ResponseCodeAssertion,
ResponseDataAssertion,
ResponseHeadersAssertion,
TCPSampler,
TestElement,
TestPlan,
ThreadGroup,
XPath2Extractor,
IfController as JMXIfController,
ConstantTimer as JMXConstantTimer, TCPSampler,
} from "./JMX";
import Mock from "mockjs";
import {funcFilters} from "@/common/js/func-filter";
@ -807,12 +808,12 @@ export class AssertionJSR223 extends AssertionType {
this.name = undefined;
this.script = undefined;
this.language = "beanshell";
this.scriptLanguage = "beanshell";
this.set(options);
}
isValid() {
return !!this.script && !!this.language;
return !!this.script && !!this.scriptLanguage;
}
}

View File

@ -63,12 +63,12 @@ import MsTableHeader from "../../common/components/MsTableHeader";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiReportStatus from "./ApiReportStatus";
import {_filter, _sort} from "@/common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import ShowMoreBtn from "../../track/case/components/ShowMoreBtn";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
components: {

View File

@ -61,31 +61,31 @@
</template>
<script>
import OneClickOperation from './OneClickOperation';
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiTestStatus from "./ApiTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "@/common/js/utils";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import ApiCopyDialog from "./components/ApiCopyDialog";
import MsUpgrade from "./Upgrade";
import OneClickOperation from './OneClickOperation';
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiTestStatus from "./ApiTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import ApiCopyDialog from "./components/ApiCopyDialog";
import MsUpgrade from "./Upgrade";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
components: {
ApiCopyDialog,
OneClickOperation,
MsTableOperators,
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator, MsUpgrade
},
data() {
return {
result: {},
condition: {
export default {
components: {
ApiCopyDialog,
OneClickOperation,
MsTableOperators,
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator, MsUpgrade
},
data() {
return {
result: {},
condition: {
components: TEST_CONFIGS
},
projectId: null,

View File

@ -1,6 +1,6 @@
<template>
<el-dialog
title="选则模块"
:title="$t('commons.module.select_module')"
:visible.sync="oneClickOperationVisible"
width="600px"
left
@ -11,7 +11,7 @@
<ms-node-tree
v-loading="result.loading"
:tree-nodes="data"
allLabel="默认模块"
:allLabel="$t('commons.module.default_module')"
@add="add"
:type="'edit'"
@edit="edit"

View File

@ -2,28 +2,32 @@
<el-dialog width="50%" :close-on-click-modal="false" :title="$t('api_test.jar_config.title')" :visible.sync="visible" class="jar-import" @close="close">
<div v-loading="result.loading">
<ms-jar-config-from :config="currentConfig" :callback="saveConfig" ref="jarConfigFrom" :read-only="isReadOnly"/>
<ms-jar-search-bar @refresh="getJarConfigs" :table-data="configs" ref="jarSearchBar"/>
<ms-jar-search-bar v-if="(!isSearchBarQuery && configs.length > 0) || isSearchBarQuery" :condition="condition"
@search="getJarConfigs" :table-data="configs" ref="jarSearchBar"/>
<ms-jar-config-list @refresh="getJarConfigs" v-if="configs.length > 0" @rowSelect="rowSelect" :table-data="configs" ref="jarConfigList"/>
</div>
</el-dialog>
</template>
<script>
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "../../../../../../common/js/utils";
import MsJarConfigList from "./JarConfigList";
import MsJarConfigFrom from "./JarConfigFrom";
import MsJarSearchBar from "./JarSearchBar";
export default {
name: "MsJarConfig",
components: {MsJarConfigFrom, MsJarSearchBar, MsJarConfigList, MsDialogFooter},
data() {
return {
visible: false,
result: {},
currentConfig: {},
configs: []
}
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import {listenGoBack, removeGoBackListener} from "../../../../../../common/js/utils";
import MsJarConfigList from "./JarConfigList";
import MsJarConfigFrom from "./JarConfigFrom";
import MsJarSearchBar from "./JarSearchBar";
export default {
name: "MsJarConfig",
components: {MsJarConfigFrom, MsJarSearchBar, MsJarConfigList, MsDialogFooter},
data() {
return {
visible: false,
result: {},
currentConfig: {},
configs: [],
condition: {},
isSearchBarQuery: false
}
},
props: {
isReadOnly: {
@ -34,6 +38,8 @@
methods: {
open() {
this.visible = true;
this.condition = {};
this.getJarConfigs();
listenGoBack(this.close);
},
@ -54,11 +60,14 @@
this.getJarConfigs();
});
},
getJarConfigs(condition) {
this.result = this.$post("/jar/list", {name: condition}, response => {
this.configs = response.data;
this.currentConfig = {};
});
getJarConfigs(isSearchBarQuery) {
if (isSearchBarQuery) {
this.isSearchBarQuery = isSearchBarQuery;
}
this.result = this.$post("/jar/list", this.condition, response => {
this.configs = response.data;
this.currentConfig = {};
});
},
rowSelect(config) {
this.currentConfig = config;
@ -74,4 +83,9 @@
<style scoped>
.jar-config-list {
max-height: 600px;
overflow: scroll;
}
</style>

View File

@ -1,10 +1,10 @@
<template>
<el-input class="jar-header-search"
v-model="searchCondition"
v-model="condition.name"
type="text"
size="small"
prefix-icon="el-icon-search"
@keyup.enter.native="search"
@blur="search"
:placeholder="$t('project.search_by_name_jar')" clearable/>
</template>
@ -19,19 +19,9 @@
},
},
},
data() {
return {
searchCondition: this.condition
};
},
watch: {
inputCondition(value) {
this.searchCondition = value;
}
},
methods: {
search() {
this.$emit('refresh', this.searchCondition);
this.$emit('search', true);
}
},
}

View File

@ -172,6 +172,22 @@ export const API_CASE_RESULT = {
}
}
export const API_SCENARIO_RESULT = {
key: "status",
name: 'MsTableSearchSelect',
label: 'test_track.plan_view.execute_result',
operator: {
options: [OPERATORS.IN, OPERATORS.NOT_IN]
},
options: [
{value: 'Success', label: 'api_test.automation.success'},
{value: 'Fail', label: 'api_test.automation.fail'}
],
props: { // 尾部控件的props一般为element ui控件的props
multiple: true
}
}
export const API_METHOD = {
key: "method",
name: 'MsTableSearchSelect',
@ -442,4 +458,6 @@ export const API_DEFINITION_CONFIGS = [NAME, API_METHOD, API_PATH, API_STATUS, A
export const API_CASE_CONFIGS = [NAME, API_CASE_PRIORITY, API_TAGS, API_CASE_RESULT, UPDATE_TIME, CREATE_TIME, CREATOR];
export const API_SCENARIO_CONFIGS = [NAME, API_CASE_PRIORITY, API_TAGS, API_SCENARIO_RESULT, UPDATE_TIME, CREATE_TIME, CREATOR];
export const TEST_PLAN_REPORT_CONFIGS = [NAME, TEST_PLAN_NAME,CREATOR, CREATE_TIME, TEST_PLAN_TRIGGER_MODE, TEST_PLAN_STATUS];

View File

@ -2,25 +2,41 @@
<el-menu :unique-opened="true" mode="horizontal" router
class="header-user-menu align-right"
background-color="#2c2a48"
active-text-color="#fff"
default-active="1"
text-color="#fff">
<el-submenu index="1" popper-class="submenu"
<el-menu-item index="1" v-show="false">Placeholder</el-menu-item>
<el-submenu index="1" popper-class="org-ws-submenu"
v-roles="['org_admin', 'test_manager', 'test_user', 'test_viewer']">
<template v-slot:title>{{$t('commons.organization')}}: {{currentOrganizationName}}</template>
<label v-for="(item,index) in organizationList" :key="index">
<el-menu-item @click="changeOrg(item)">{{item.name}}
<template v-slot:title>{{ $t('commons.organization') }}: {{ currentOrganizationName }}</template>
<el-input :placeholder="$t('project.search_by_name')"
prefix-icon="el-icon-search"
v-model="searchOrg"
clearable
class="search-input"
size="small"/>
<div class="org-ws-menu">
<el-menu-item @click="changeOrg(item)" v-for="(item,index) in organizationList" :key="index">
{{ item.name }}
<i class="el-icon-check"
v-if="item.id === currentUserInfo.lastOrganizationId"></i>
</el-menu-item>
</label>
</div>
</el-submenu>
<el-submenu index="2" popper-class="submenu" v-roles="['test_manager', 'test_user', 'test_viewer']">
<template v-slot:title>{{$t('commons.workspace')}}: {{currentWorkspaceName}}</template>
<label v-for="(item,index) in workspaceList" :key="index">
<el-menu-item @click="changeWs(item)">
{{item.name}}
<template v-slot:title>{{ $t('commons.workspace') }}: {{ currentWorkspaceName }}</template>
<el-input :placeholder="$t('project.search_by_name')"
prefix-icon="el-icon-search"
v-model="searchWs"
clearable
class="search-input"
size="small"/>
<div class="org-ws-menu">
<el-menu-item @click="changeWs(item)" v-for="(item,index) in workspaceList" :key="index">
{{ item.name }}
<i class="el-icon-check" v-if="item.id === currentUserInfo.lastWorkspaceId"></i>
</el-menu-item>
</label>
</div>
</el-submenu>
</el-menu>
</template>
@ -34,107 +50,170 @@ import {
ROLE_TEST_VIEWER,
WORKSPACE_ID
} from '../../../../common/js/constants';
import {getCurrentUser, hasRoles, saveLocalStorage} from "../../../../common/js/utils";
import {getCurrentUser, hasRoles, saveLocalStorage} from "../../../../common/js/utils";
export default {
name: "MsHeaderOrgWs",
created() {
this.initMenuData();
this.getCurrentUserInfo();
},
data() {
return {
organizationList: [
{name: this.$t('organization.none')},
],
workspaceList: [
{name: this.$t('workspace.none')},
],
currentUserInfo: {},
currentUserId: getCurrentUser().id,
workspaceIds: [],
currentOrganizationName: '',
currentWorkspaceName: ''
}
},
computed: {
currentUser: () => {
return getCurrentUser();
}
},
methods: {
initMenuData() {
if (hasRoles(ROLE_ORG_ADMIN, ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
this.$get("/organization/list/userorg/" + encodeURIComponent(this.currentUserId), response => {
let data = response.data;
this.organizationList = data;
let org = data.filter(r => r.id === this.currentUser.lastOrganizationId);
if (org.length > 0) {
this.currentOrganizationName = org[0].name;
}
});
}
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
if (!this.currentUser.lastOrganizationId) {
return false;
}
this.$get("/workspace/list/orgworkspace/", response => {
let data = response.data;
if (data.length === 0) {
this.workspaceList = [{name: this.$t('workspace.none')}]
} else {
this.workspaceList = data;
let workspace = data.filter(r => r.id === this.currentUser.lastWorkspaceId);
if (workspace.length > 0) {
this.currentWorkspaceName = workspace[0].name;
localStorage.setItem(WORKSPACE_ID, workspace[0].id);
}
}
})
}
},
getCurrentUserInfo() {
this.$get("/user/info/" + encodeURIComponent(this.currentUserId), response => {
this.currentUserInfo = response.data;
})
},
changeOrg(data) {
let orgId = data.id;
if (!orgId) {
return false;
}
this.$post("/user/switch/source/org/" + orgId, {}, response => {
saveLocalStorage(response);
if (response.data.workspaceId) {
localStorage.setItem("workspace_id", response.data.workspaceId);
}
localStorage.removeItem(PROJECT_ID);
this.$router.push('/').then(() => {
window.location.reload();
}).catch(err => err);
});
},
changeWs(data) {
let workspaceId = data.id;
if (!workspaceId) {
return false;
}
this.$post("/user/switch/source/ws/" + workspaceId, {}, response => {
saveLocalStorage(response);
localStorage.setItem("workspace_id", workspaceId);
localStorage.removeItem(PROJECT_ID);
this.$router.push('/').then(() => {
window.location.reload();
}).catch(err => err);
})
}
export default {
name: "MsHeaderOrgWs",
created() {
this.initMenuData();
this.getCurrentUserInfo();
},
data() {
return {
organizationList: [
{name: this.$t('organization.none')},
],
workspaceList: [
{name: this.$t('workspace.none')},
],
currentUserInfo: {},
currentUserId: getCurrentUser().id,
workspaceIds: [],
currentOrganizationName: '',
currentWorkspaceName: '',
searchOrg: '',
searchWs: '',
orgListCopy: [{name: this.$t('organization.none')}],
wsListCopy: [{name: this.$t('workspace.none')}]
}
},
computed: {
currentUser: () => {
return getCurrentUser();
}
},
watch: {
searchOrg(val) {
this.query('org', val);
},
searchWs(val) {
this.query('ws', val);
}
},
methods: {
initMenuData() {
if (hasRoles(ROLE_ORG_ADMIN, ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
this.$get("/organization/list/userorg/" + encodeURIComponent(this.currentUserId), response => {
let data = response.data;
this.organizationList = data;
this.orgListCopy = data;
let org = data.filter(r => r.id === this.currentUser.lastOrganizationId);
if (org.length > 0) {
this.currentOrganizationName = org[0].name;
}
});
}
if (hasRoles(ROLE_TEST_VIEWER, ROLE_TEST_USER, ROLE_TEST_MANAGER)) {
if (!this.currentUser.lastOrganizationId) {
return false;
}
this.$get("/workspace/list/orgworkspace/", response => {
let data = response.data;
if (data.length === 0) {
this.workspaceList = [{name: this.$t('workspace.none')}]
} else {
this.workspaceList = data;
this.wsListCopy = data;
let workspace = data.filter(r => r.id === this.currentUser.lastWorkspaceId);
if (workspace.length > 0) {
this.currentWorkspaceName = workspace[0].name;
localStorage.setItem(WORKSPACE_ID, workspace[0].id);
}
}
})
}
},
getCurrentUserInfo() {
this.$get("/user/info/" + encodeURIComponent(this.currentUserId), response => {
this.currentUserInfo = response.data;
})
},
changeOrg(data) {
let orgId = data.id;
if (!orgId) {
return false;
}
this.$post("/user/switch/source/org/" + orgId, {}, response => {
saveLocalStorage(response);
if (response.data.workspaceId) {
localStorage.setItem("workspace_id", response.data.workspaceId);
}
localStorage.removeItem(PROJECT_ID);
this.$router.push('/').then(() => {
window.location.reload();
}).catch(err => err);
});
},
changeWs(data) {
let workspaceId = data.id;
if (!workspaceId) {
return false;
}
this.$post("/user/switch/source/ws/" + workspaceId, {}, response => {
saveLocalStorage(response);
localStorage.setItem("workspace_id", workspaceId);
localStorage.removeItem(PROJECT_ID);
this.$router.push('/').then(() => {
window.location.reload();
}).catch(err => err);
})
},
query(sign, queryString) {
if (sign === 'org') {
this.organizationList = queryString ? this.orgListCopy.filter(this.createFilter(queryString)) : this.orgListCopy;
}
if (sign === 'ws') {
this.workspaceList = queryString ? this.wsListCopy.filter(this.createFilter(queryString)) : this.wsListCopy;
}
},
createFilter(queryString) {
return item => {
return (item.name.toLowerCase().indexOf(queryString.toLowerCase()) !== -1);
};
},
}
}
</script>
<style scoped>
.el-icon-check {
color: #44b349;
margin-left: 10px;
}
.el-icon-check {
color: #44b349;
margin-left: 10px;
}
::-webkit-scrollbar {
width: 10px;
height: 10px;
position: fixed;
}
::-webkit-scrollbar-thumb {
border-radius: 1em;
background-color: #595591;
position: fixed;
}
::-webkit-scrollbar-track {
border-radius: 1em;
background-color: transparent;
position: fixed;
}
.org-ws-menu {
height: 180px;
overflow: auto;
}
.search-input {
padding: 0;
margin-top: -4px;
background-color: #595591;
}
.search-input >>> .el-input__inner {
border-radius: 0;
background-color: #2d2a49;
color: #d2ced8;
border-color: #b4aebe;
}
</style>

View File

@ -3,8 +3,8 @@
<el-row class="row" :gutter="20">
<el-col :span="8" class="ms-col-name">
<div :style="{marginLeft:`${10*deep}px`}" class="ms-col-name-c"/>
<span v-if="pickValue.type==='object'" :class="hidden? 'el-tree-node__expand-icon el-icon-caret-right':
'expanded el-tree-node__expand-icon el-icon-caret-right'" @click="hidden = !hidden"/>
<span v-if="pickValue.type==='object'" :class="hidden? 'el-icon-caret-left ms-transform':
'el-icon-caret-bottom'" @click="hidden = !hidden"/>
<span v-else style="width:10px;display:inline-block"></span>
<input class="el-input el-input__inner" style="height: 32px" :disabled="disabled || root" :value="pickKey" @blur="onInputName" size="small"/>
@ -393,4 +393,8 @@
border-left: 4px solid #783887;
margin: 0px 0px 10px;
}
.ms-transform {
transform: rotate(-180deg);
transition: 0ms;
}
</style>

View File

@ -293,12 +293,12 @@ export default {
};
this.result = this.$request(config).then(response => {
const content = response.data;
const blob = new Blob([content]);
const blob = new Blob([content], {type: "application/octet-stream"});
if ("download" in document.createElement("a")) {
// IE
// chrome/firefox
let aTag = document.createElement('a');
aTag.download = this.reportId + ".jtl";
aTag.download = this.reportId + ".zip";
aTag.href = URL.createObjectURL(blob);
aTag.click();
URL.revokeObjectURL(aTag.href)

View File

@ -93,13 +93,15 @@ import MsTablePagination from "../../common/pagination/TablePagination";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceReportStatus from "./PerformanceReportStatus";
import {_filter, _sort, getCurrentProjectID} from "../../../../common/js/utils";
import {getCurrentProjectID} from "../../../../common/js/utils";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../common/components/search/search-components";
import MsTableHeader from "../../common/components/MsTableHeader";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
import ShowMoreBtn from "../../track/case/components/ShowMoreBtn";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "PerformanceTestReport",

View File

@ -14,11 +14,11 @@
<el-form :inline="true">
<el-form-item :label="$t('load_test.thread_num')">
<el-input-number
:disabled="true"
:placeholder="$t('load_test.input_thread_num')"
v-model="threadGroup.threadNumber"
:min="1"
size="mini"/>
:disabled="true"
:placeholder="$t('load_test.input_thread_num')"
v-model="threadGroup.threadNumber"
:min="1"
size="mini"/>
</el-form-item>
<br>
<el-form-item>
@ -31,72 +31,72 @@
<div v-if="threadGroup.threadType === 'DURATION'">
<el-form-item :label="$t('load_test.duration')">
<el-input-number
:disabled="true"
v-model="threadGroup.duration"
:min="1"
@change="calculateChart(threadGroup)"
size="mini"/>
:disabled="true"
v-model="threadGroup.duration"
:min="1"
@change="calculateChart(threadGroup)"
size="mini"/>
</el-form-item>
<br>
<el-form-item :label="$t('load_test.rps_limit')">
<el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/>
&nbsp;
<el-input-number
:disabled="true "
v-model="threadGroup.rpsLimit"
@change="calculateChart(threadGroup)"
:min="1"
size="mini"/>
:disabled="true "
v-model="threadGroup.rpsLimit"
@change="calculateChart(threadGroup)"
:min="1"
size="mini"/>
</el-form-item>
<br>
<el-form-item :label="$t('load_test.ramp_up_time_within')">
<el-input-number
:disabled="true"
:min="1"
:max="threadGroup.duration"
v-model="threadGroup.rampUpTime"
@change="calculateChart(threadGroup)"
size="mini"/>
:disabled="true"
:min="1"
:max="threadGroup.duration"
v-model="threadGroup.rampUpTime"
@change="calculateChart(threadGroup)"
size="mini"/>
</el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_minutes')">
<el-input-number
:disabled="true"
:min="1"
:max="Math.min(threadGroup.threadNumber, threadGroup.rampUpTime)"
v-model="threadGroup.step"
@change="calculateChart(threadGroup)"
size="mini"/>
:disabled="true"
:min="1"
:max="Math.min(threadGroup.threadNumber, threadGroup.rampUpTime)"
v-model="threadGroup.step"
@change="calculateChart(threadGroup)"
size="mini"/>
</el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_times')"/>
</div>
<div v-if="threadGroup.threadType === 'ITERATION'">
<el-form-item :label="$t('load_test.iterate_num')">
<el-input-number
:disabled="true"
v-model="threadGroup.iterateNum"
:min="1"
@change="calculateChart(threadGroup)"
size="mini"/>
:disabled="true"
v-model="threadGroup.iterateNum"
:min="1"
@change="calculateChart(threadGroup)"
size="mini"/>
</el-form-item>
<br>
<el-form-item :label="$t('load_test.rps_limit')">
<el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/>
&nbsp;
<el-input-number
:disabled="true || !threadGroup.rpsLimitEnable"
v-model="threadGroup.rpsLimit"
@change="calculateChart(threadGroup)"
:min="1"
size="mini"/>
:disabled="true || !threadGroup.rpsLimitEnable"
v-model="threadGroup.rpsLimit"
@change="calculateChart(threadGroup)"
:min="1"
size="mini"/>
</el-form-item>
<br>
<el-form-item :label="$t('load_test.ramp_up_time_within')">
<el-input-number
:disabled="true"
:min="1"
v-model="threadGroup.iterateRampUp"
@change="calculateChart(threadGroup)"
size="mini"/>
:disabled="true"
:min="1"
v-model="threadGroup.iterateRampUp"
@change="calculateChart(threadGroup)"
size="mini"/>
</el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_seconds')"/>
</div>
@ -129,11 +129,11 @@ const ITERATE_RAMP_UP = "iterateRampUpTime";
const hexToRgba = function (hex, opacity) {
return 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) + ','
+ parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')';
+ parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')';
}
const hexToRgb = function (hex) {
return 'rgb(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5))
+ ',' + parseInt('0x' + hex.slice(5, 7)) + ')';
+ ',' + parseInt('0x' + hex.slice(5, 7)) + ')';
}
export default {
@ -469,11 +469,12 @@ export default {
},
},
watch: {
'report.testId': {
report: {
handler() {
this.getJmxContent();
},
}
deep: true
},
}
}
</script>

View File

@ -89,11 +89,12 @@ import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsPerformanceTestStatus from "./PerformanceTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort, getCurrentProjectID} from "@/common/js/utils";
import {getCurrentProjectID} from "@/common/js/utils";
import MsTableHeader from "../../common/components/MsTableHeader";
import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {LIST_CHANGE, PerformanceEvent} from "@/business/components/common/head/ListEvent";
import {WORKSPACE_ID} from "@/common/js/constants";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
components: {

View File

@ -128,7 +128,7 @@ export default {
components: {MsTableOperatorButton},
data() {
return {
timeout: 2000,
timeout: 60000,
responseTimeout: null,
statusCode: [],
domains: [],

View File

@ -10,8 +10,9 @@
<el-table-column prop="description" :label="$t('commons.description')"/>
<el-table-column :label="$t('commons.member')">
<template v-slot:default="scope">
<el-button type="text" class="member-size" @click="cellClick(scope.row)">{{scope.row.memberSize}}
</el-button>
<el-link type="primary" class="member-size" @click="cellClick(scope.row)">
{{ scope.row.memberSize }}
</el-link>
</template>
</el-table-column>
<el-table-column :label="$t('commons.operating')">
@ -506,7 +507,6 @@
.member-size {
text-decoration: underline;
cursor: pointer;
}
.select-width {

View File

@ -114,9 +114,7 @@ export default {
}
],
phone: [
{required: true, message: this.$t('user.input_phone'), trigger: 'blur'},
{
required: false,
pattern: PHONE_REGEX,
message: this.$t('member.mobile_number_format_is_incorrect'),
trigger: 'blur'

View File

@ -92,33 +92,35 @@
</template>
<script>
import MsCreateBox from "../CreateBox";
import {Message} from "element-ui";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {_sort, getCurrentProjectID, getCurrentUser, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsDeleteConfirm from "../../common/components/MsDeleteConfirm";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import {PROJECT_ID} from "@/common/js/constants";
import MsJarConfig from "../../api/test/components/jar/JarConfig";
import MsTableButton from "../../common/components/MsTableButton";
export default {
name: "MsProject",
components: {
MsTableButton,
MsJarConfig,
TemplateComponent,
ApiEnvironmentConfig,
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter
import MsCreateBox from "../CreateBox";
import {Message} from "element-ui";
import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator";
import MsDialogFooter from "../../common/components/MsDialogFooter";
import {getCurrentProjectID, getCurrentUser, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer";
import MsDeleteConfirm from "../../common/components/MsDeleteConfirm";
import MsTableOperatorButton from "../../common/components/MsTableOperatorButton";
import ApiEnvironmentConfig from "../../api/test/components/ApiEnvironmentConfig";
import TemplateComponent from "../../track/plan/view/comonents/report/TemplateComponent/TemplateComponent";
import {PROJECT_ID} from "@/common/js/constants";
import MsJarConfig from "../../api/test/components/jar/JarConfig";
import MsTableButton from "../../common/components/MsTableButton";
import {_sort} from "@/common/js/tableUtils";
export default {
name: "MsProject",
components: {
MsTableButton,
MsJarConfig,
TemplateComponent,
ApiEnvironmentConfig,
MsTableOperatorButton,
MsDeleteConfirm,
MsMainContainer,
MsContainer, MsTableOperator, MsCreateBox, MsTablePagination, MsTableHeader, MsDialogFooter
},
data() {
return {

View File

@ -399,9 +399,7 @@ export default {
}
],
phone: [
{required: true, message: this.$t('user.input_phone'), trigger: 'blur'},
{
required: true,
pattern: PHONE_REGEX,
message: this.$t('user.mobile_number_format_is_incorrect'),
trigger: 'blur'

View File

@ -29,6 +29,7 @@
@filter-change="filter"
@select-all="handleSelectAll"
@select="handleSelect"
@header-dragend="headerDragend"
@cell-mouse-enter="showPopover"
row-key="id"
class="test-content adjust-table ms-select-all-fixed"
@ -145,7 +146,7 @@
</template>
</el-table-column>
<el-table-column fixed="right"
:label="$t('commons.operating')" min-width="150">
:label="$t('commons.operating')" min-width="150">
<template v-slot:default="scope">
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)"
@deleteClick="handleDelete(scope.row)">
@ -186,7 +187,6 @@ import MethodTableItem from "../../common/tableItems/planview/MethodTableItem";
import MsTableOperator from "../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import MsTableButton from "../../../common/components/MsTableButton";
import {_filter, _sort} from "@/common/js/utils";
import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components";
import ShowMoreBtn from "./ShowMoreBtn";
import BatchEdit from "./BatchEdit";
@ -197,7 +197,15 @@ import TestCaseDetail from "./TestCaseDetail";
import ReviewStatus from "@/business/components/track/case/components/ReviewStatus";
import {getCurrentProjectID} from "../../../../../common/js/utils";
import MsTag from "@/business/components/common/components/MsTag";
import {_handleSelect, _handleSelectAll} from "../../../../../common/js/tableUtils";
import {
_filter,
_handleSelect,
_handleSelectAll,
_sort,
getSelectDataCounts,
setUnSelectIds,
toggleAllSelection
} from "@/common/js/tableUtils";
import BatchMove from "./BatchMove";
export default {
@ -431,13 +439,14 @@ export default {
},
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.tableData, this.selectRows);
this.setUnSelectIds();
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
},
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
this.setUnSelectIds();
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
},
importTestCase() {
if (!getCurrentProjectID()) {
this.$warning(this.$t('commons.check_project_tip'));
@ -544,23 +553,17 @@ export default {
},
isSelectDataAll(data) {
this.condition.selectAll = data;
this.setUnSelectIds();
//
if (this.selectRows.size != this.tableData.length) {
this.$refs.table.toggleAllSelection(true);
}
setUnSelectIds(this.tableData, this.condition, this.selectRows);
this.selectDataCounts = getSelectDataCounts(this.condition, this.total, this.selectRows);
toggleAllSelection(this.$refs.table, this.tableData, this.selectRows);
},
setUnSelectIds() {
let ids = Array.from(this.selectRows).map(o => o.id);
let allIDs = this.tableData.map(o => o.id);
this.condition.unSelectIds = allIDs.filter(function (val) {
return ids.indexOf(val) === -1
});
if (this.condition.selectAll) {
this.selectDataCounts = this.total - this.condition.unSelectIds.length;
} else {
this.selectDataCounts = this.selectRows.size;
headerDragend(newWidth,oldWidth,column,event){
let finalWidth = newWidth;
if(column.minWidth>finalWidth){
finalWidth = column.minWidth;
}
column.width = finalWidth;
column.realWidth = finalWidth;
},
moveSave(param) {
param.condition = this.condition;

View File

@ -37,15 +37,15 @@
<plan-status-table-item :value="scope.row.status"/>
</span>
<el-dropdown-menu slot="dropdown" chang>
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{id: scope.row.id, status: 'Prepare'}">
<el-dropdown-item :disabled="!isTestManagerOrTestUser" :command="{item: scope.row, status: 'Prepare'}">
{{ $t('test_track.plan.plan_status_prepare') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{id: scope.row.id, status: 'Underway'}">
:command="{item: scope.row, status: 'Underway'}">
{{ $t('test_track.plan.plan_status_running') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{id: scope.row.id, status: 'Completed'}">
:command="{item: scope.row, status: 'Completed'}">
{{ $t('test_track.plan.plan_status_completed') }}
</el-dropdown-item>
</el-dropdown-menu>
@ -158,7 +158,7 @@ import MsTableOperatorButton from "../../../common/components/MsTableOperatorBut
import MsTableOperator from "../../../common/components/MsTableOperator";
import PlanStatusTableItem from "../../common/tableItems/plan/PlanStatusTableItem";
import PlanStageTableItem from "../../common/tableItems/plan/PlanStageTableItem";
import {_filter, _sort, checkoutTestManagerOrTestUser} from "@/common/js/utils";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import TestReportTemplateList from "../view/comonents/TestReportTemplateList";
import TestCaseReportView from "../view/comonents/report/TestCaseReportView";
import MsDeleteConfirm from "../../../common/components/MsDeleteConfirm";
@ -166,6 +166,8 @@ import {TEST_PLAN_CONFIGS} from "../../../common/components/search/search-compon
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import {getCurrentProjectID} from "../../../../../common/js/utils";
import MsScheduleMaintain from "@/business/components/api/automation/schedule/ScheduleMaintain"
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "TestPlanList",
@ -255,20 +257,25 @@ export default {
this.$emit('testPlanEdit', testPlan);
},
statusChange(param) {
console.log(this.tableData);
let oldStatus = param.item.status;
let newStatus = param.status;
param = param.item;
param.status = newStatus;
this.$post('/test/plan/edit', param, () => {
for (let i = 0; i < this.tableData.length; i++) {
if (this.tableData[i].id == param.id) { //
if (this.tableData[i].status !== "Completed" && param.status === "Completed") {
this.tableData[i].actualEndTime = new Date();
if (oldStatus !== "Completed" && newStatus === "Completed") {
this.tableData[i].actualEndTime = Date.now();
} // ->=null
else if (this.tableData[i].status !== "Underway" && param.status === "Underway") {
this.tableData[i].actualStartTime = new Date();
else if (oldStatus !== "Underway" && newStatus === "Underway") {
this.tableData[i].actualStartTime = Date.now();
this.tableData[i].actualEndTime = "";
} // ->=null
else if (this.tableData[i].status !== "Prepare" && param.status === "Prepare") {
else if (oldStatus !== "Prepare" && newStatus === "Prepare") {
this.tableData[i].actualStartTime = this.tableData[i].actualEndTime = "";
} // ->=null
this.tableData[i].status = param.status;
this.tableData[i].status = newStatus;
break;
}
}

View File

@ -132,13 +132,15 @@ import {API_METHOD_COLOUR, CASE_PRIORITY, RESULT_MAP} from "../../../../../api/d
import {getCurrentProjectID} from "@/common/js/utils";
import ApiListContainer from "../../../../../api/definition/components/list/ApiListContainer";
import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem";
import {_filter, _sort, getBodyUploadFiles, getUUID} from "../../../../../../../common/js/utils";
import {getBodyUploadFiles, getUUID} from "../../../../../../../common/js/utils";
import TestPlanCaseListHeader from "./TestPlanCaseListHeader";
import MsRun from "../../../../../api/definition/components/Run";
import TestPlanApiCaseResult from "./TestPlanApiCaseResult";
import TestPlan from "../../../../../api/definition/components/jmeter/components/test-plan";
import ThreadGroup from "../../../../../api/definition/components/jmeter/components/thread-group";
import {WORKSPACE_ID} from "@/common/js/constants";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "TestPlanApiCaseList",

View File

@ -35,6 +35,7 @@
@select="handleSelectionChange"
row-key="id"
@row-click="showDetail"
@header-dragend="headerDragend"
:data="tableData">
<el-table-column
@ -48,17 +49,23 @@
prop="num"
sortable="custom"
:label="$t('commons.id')"
min-width="120px"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="name"
:label="$t('commons.name')"
min-width="120px"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="priority"
:filters="priorityFilters"
column-key="priority"
min-width="100px"
:label="$t('test_track.case.priority')">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority" ref="priority"/>
@ -70,13 +77,16 @@
:filters="typeFilters"
column-key="type"
:label="$t('test_track.case.type')"
min-width="80px"
show-overflow-tooltip>
<template v-slot:default="scope">
<type-table-item :value="scope.row.type"/>
</template>
</el-table-column>
<el-table-column prop="tags" :label="$t('commons.tag')">
<el-table-column prop="tags" :label="$t('commons.tag')" min-width="120px"
>
<template v-slot:default="scope">
<div v-for="(tag, index) in scope.row.showTags" :key="tag + '_' + index">
<ms-tag type="success" effect="plain" :content="tag"/>
@ -89,6 +99,8 @@
:filters="methodFilters"
column-key="method"
:label="$t('test_track.case.method')"
min-width="100px"
show-overflow-tooltip>
<template v-slot:default="scope">
<method-table-item :value="scope.row.method"/>
@ -98,17 +110,23 @@
<el-table-column
prop="nodePath"
:label="$t('test_track.case.module')"
min-width="120px"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="projectName"
:label="$t('test_track.plan.plan_project')"
min-width="120px"
show-overflow-tooltip>
</el-table-column>
<el-table-column
:label="$t('test_track.issue.issue')"
min-width="80px"
show-overflow-tooltip>
<template v-slot:default="scope">
<el-popover
@ -141,6 +159,8 @@
<el-table-column
prop="executorName"
:filters="executorFilters"
min-width="100px"
column-key="executor"
:label="$t('test_track.plan_view.executor')">
</el-table-column>
@ -149,6 +169,8 @@
prop="status"
:filters="statusFilters"
column-key="status"
min-width="100px"
:label="$t('test_track.plan_view.execute_result')">
<template v-slot:default="scope">
<span @click.stop="clickt = 'stop'">
@ -181,6 +203,8 @@
sortable
prop="updateTime"
:label="$t('commons.update_time')"
min-width="120px"
show-overflow-tooltip>
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
@ -226,21 +250,21 @@ import MsTableButton from '../../../../../common/components/MsTableButton';
import NodeBreadcrumb from '../../../../common/NodeBreadcrumb';
import {ROLE_TEST_MANAGER, ROLE_TEST_USER, TokenKey, WORKSPACE_ID} from "@/common/js/constants";
import {_filter, _sort, checkoutTestManagerOrTestUser, hasRoles} from "@/common/js/utils";
import {checkoutTestManagerOrTestUser, hasRoles} from "@/common/js/utils";
import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem";
import StatusTableItem from "../../../../common/tableItems/planview/StatusTableItem";
import TypeTableItem from "../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../common/tableItems/planview/MethodTableItem";
import MsTableOperator from "../../../../../common/components/MsTableOperator";
import MsTableOperatorButton from "../../../../../common/components/MsTableOperatorButton";
import TestReportTemplateList from "../TestReportTemplateList";
import TestCaseReportView from "../report/TestCaseReportView";
import {TEST_CASE_CONFIGS} from "../../../../../common/components/search/search-components";
import ShowMoreBtn from "../../../../case/components/ShowMoreBtn";
import BatchEdit from "../../../../case/components/BatchEdit";
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
import {hub} from "@/business/components/track/plan/event-bus";
import MsTag from "@/business/components/common/components/MsTag";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "FunctionalTestCaseList",
@ -572,6 +596,14 @@ export default {
_sort(column, this.condition);
this.initTableData();
},
headerDragend(newWidth,oldWidth,column,event){
let finalWidth = newWidth;
if(column.minWidth>finalWidth){
finalWidth = column.minWidth;
}
column.width = finalWidth;
column.realWidth = finalWidth;
},
batchEdit(form) {
let param = {};
param[form.type] = form.value;

View File

@ -70,28 +70,29 @@
<script>
import NodeTree from '../../../../common/NodeTree';
import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../common/tableItems/planview/TypeTableItem";
import MsTableSearchBar from "../../../../../common/components/MsTableSearchBar";
import MsTableAdvSearchBar from "../../../../../common/components/search/MsTableAdvSearchBar";
import MsTableHeader from "../../../../../common/components/MsTableHeader";
import {TEST_CASE_CONFIGS} from "../../../../../common/components/search/search-components";
import elTableInfiniteScroll from 'el-table-infinite-scroll';
import TestCaseRelevanceBase from "../base/TestCaseRelevanceBase";
import {_filter} from "../../../../../../../common/js/utils";
import NodeTree from '../../../../common/NodeTree';
import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../common/tableItems/planview/TypeTableItem";
import MsTableSearchBar from "../../../../../common/components/MsTableSearchBar";
import MsTableAdvSearchBar from "../../../../../common/components/search/MsTableAdvSearchBar";
import MsTableHeader from "../../../../../common/components/MsTableHeader";
import {TEST_CASE_CONFIGS} from "../../../../../common/components/search/search-components";
import elTableInfiniteScroll from 'el-table-infinite-scroll';
import TestCaseRelevanceBase from "../base/TestCaseRelevanceBase";
import {_filter} from "@/common/js/tableUtils";
export default {
name: "TestCaseFunctionalRelevance",
components: {
TestCaseRelevanceBase,
NodeTree,
PriorityTableItem,
TypeTableItem,
MsTableSearchBar,
MsTableAdvSearchBar,
MsTableHeader,
},
export default {
name: "TestCaseFunctionalRelevance",
components: {
TestCaseRelevanceBase,
NodeTree,
PriorityTableItem,
TypeTableItem,
MsTableSearchBar,
MsTableAdvSearchBar,
MsTableHeader,
},
directives: {
'el-table-infinite-scroll': elTableInfiniteScroll
},

View File

@ -57,11 +57,9 @@
'redirectCharType',
'clickType'
],
// activated() {
// this.search();
// this.checkTipsType();
// },
// mounted() {
mounted() {
this.initData();
},
activated(){
this.initData();
this.openTestCaseEdit(this.$route.path);

View File

@ -107,11 +107,11 @@
import TestPlanLoadCaseListHeader
from "@/business/components/track/plan/view/comonents/load/TestPlanLoadCaseListHeader";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import {_filter, _sort} from "@/common/js/utils";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import MsPerformanceTestStatus from "@/business/components/performance/test/PerformanceTestStatus";
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
import LoadCaseReport from "@/business/components/track/plan/view/comonents/load/LoadCaseReport";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "TestPlanLoadCaseList",

View File

@ -1,7 +1,7 @@
<template>
<div class="failure-cases-list">
<div class="failure-cases-list-header">
接口测试用例
{{$t('test_track.plan.api_case')}}
</div>
<el-table
@ -28,7 +28,7 @@
<el-table-column
prop="createUser"
:label="'创建人'"
:label="$t('api_test.automation.creator')"
show-overflow-tooltip/>
<el-table-column prop="lastResult" :label="$t('api_test.automation.last_result')">
@ -60,9 +60,23 @@
name: "ApiFailureCasesList",
components: {MsTag, PriorityTableItem, TypeTableItem, MethodTableItem, StatusTableItem},
props: ['apiTestCases'],
watch: {
apiTestCases() {
this.parseTags();
}
},
methods: {
goFailureTestCase(row) {
this.$emit("openFailureTestCase", row);
},
parseTags() {
if (this.apiTestCases) {
this.apiTestCases.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
}
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="failure-cases-list">
<div class="failure-cases-list-header">
场景测试用例
{{$t('test_track.plan.scenario_case')}}
</div>
<el-table
@ -64,9 +64,21 @@
name: "ScenarioFailureCasesList",
components: {MsTag, PriorityTableItem, TypeTableItem, MethodTableItem, StatusTableItem},
props: ['scenarioTestCases'],
watch: {
scenarioTestCases() {
this.parseTags();
}
},
methods: {
goFailureTestCase(row) {
this.$emit("openFailureTestCase", row);
},
parseTags() {
this.scenarioTestCases.forEach(item => {
if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
});
}
}
}

View File

@ -64,7 +64,7 @@ import MsTablePagination from '../../../../components/common/pagination/TablePag
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import MsTableOperator from "../../../common/components/MsTableOperator";
import {_filter, _sort, checkoutTestManagerOrTestUser} from "@/common/js/utils";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import {TEST_PLAN_REPORT_CONFIGS} from "../../../common/components/search/search-components";
import {getCurrentProjectID} from "../../../../../common/js/utils";
import TestPlanReportView from "@/business/components/track/report/components/TestPlanReportView";
@ -72,14 +72,16 @@ import ReportTriggerModeItem from "@/business/components/common/tableItem/Report
import MsTag from "@/business/components/common/components/MsTag";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import MsTableSelectAll from "@/business/components/common/components/table/MsTableSelectAll";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "TestPlanReportList",
components: {
TestPlanReportView,
MsTableOperator, MsTableOperatorButton, MsTableHeader, MsTablePagination,
ReportTriggerModeItem,MsTag,
ShowMoreBtn,MsTableSelectAll,
ReportTriggerModeItem, MsTag,
ShowMoreBtn, MsTableSelectAll,
},
data() {
return {

View File

@ -88,12 +88,11 @@ import MsTableHeader from "../../../common/components/MsTableHeader";
import MsCreateBox from "../../../settings/CreateBox";
import MsTablePagination from "../../../common/pagination/TablePagination";
import {
_filter,
_sort,
checkoutTestManagerOrTestUser,
getCurrentProjectID,
getCurrentWorkspaceId
} from "../../../../../common/js/utils";
import {_filter, _sort} from "@/common/js/tableUtils";
import PlanStatusTableItem from "../../common/tableItems/plan/PlanStatusTableItem";
export default {

View File

@ -94,31 +94,32 @@
<script>
import NodeTree from "../../../common/NodeTree";
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import PriorityTableItem from "../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../common/tableItems/planview/TypeTableItem";
import MsTableSearchBar from "../../../../common/components/MsTableSearchBar";
import MsTableAdvSearchBar from "../../../../common/components/search/MsTableAdvSearchBar";
import MsTableHeader from "../../../../common/components/MsTableHeader";
import SwitchProject from "../../../case/components/SwitchProject";
import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components";
import {_filter} from "../../../../../../common/js/utils";
import ReviewStatus from "@/business/components/track/case/components/ReviewStatus";
import elTableInfiniteScroll from 'el-table-infinite-scroll';
import SelectMenu from "../../../common/SelectMenu";
import NodeTree from "../../../common/NodeTree";
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import PriorityTableItem from "../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../common/tableItems/planview/TypeTableItem";
import MsTableSearchBar from "../../../../common/components/MsTableSearchBar";
import MsTableAdvSearchBar from "../../../../common/components/search/MsTableAdvSearchBar";
import MsTableHeader from "../../../../common/components/MsTableHeader";
import SwitchProject from "../../../case/components/SwitchProject";
import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components";
import ReviewStatus from "@/business/components/track/case/components/ReviewStatus";
import elTableInfiniteScroll from 'el-table-infinite-scroll';
import SelectMenu from "../../../common/SelectMenu";
import {_filter} from "@/common/js/tableUtils";
export default {
name: "TestReviewRelevance",
components: {
SelectMenu,
NodeTree,
MsDialogFooter,
PriorityTableItem,
TypeTableItem,
MsTableSearchBar,
MsTableAdvSearchBar,
MsTableHeader,
export default {
name: "TestReviewRelevance",
components: {
SelectMenu,
NodeTree,
MsDialogFooter,
PriorityTableItem,
TypeTableItem,
MsTableSearchBar,
MsTableAdvSearchBar,
MsTableHeader,
SwitchProject,
ReviewStatus

View File

@ -146,6 +146,9 @@
@refreshTable="search"/>
</el-card>
<batch-edit ref="batchEdit" @batchEdit="batchEdit"
:type-arr="typeArr" :value-arr="valueArr" :dialog-title="$t('test_track.case.batch_edit_case')"/>
</div>
</template>
@ -166,11 +169,12 @@ import MsTableButton from "../../../../common/components/MsTableButton";
import ShowMoreBtn from "../../../case/components/ShowMoreBtn";
import BatchEdit from "../../../case/components/BatchEdit";
import MsTablePagination from '../../../../common/pagination/TablePagination';
import {_filter, _sort, checkoutTestManagerOrTestUser, hasRoles} from "../../../../../../common/js/utils";
import {checkoutTestManagerOrTestUser, hasRoles} from "../../../../../../common/js/utils";
import {TEST_CASE_CONFIGS} from "../../../../common/components/search/search-components";
import {ROLE_TEST_MANAGER, ROLE_TEST_USER} from "../../../../../../common/js/constants";
import TestReviewTestCaseEdit from "./TestReviewTestCaseEdit";
import ReviewStatus from "@/business/components/track/case/components/ReviewStatus";
import {_filter, _sort} from "@/common/js/tableUtils";
export default {
name: "TestReviewTestCaseList",
@ -214,21 +218,21 @@ export default {
],
showMore: false,
buttons: [
{
name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleEditBatch
},
{
name: this.$t('test_track.case.batch_unlink'), handleClick: this.handleDeleteBatch
}
],
typeArr: [
{id: 'status', name: this.$t('test_track.plan_view.execute_result')},
{id: 'executor', name: this.$t('test_track.plan_view.executor')},
{id: 'status', name: this.$t('test_track.review_view.execute_result')},
],
valueArr: {
executor: [],
status: [
{name: this.$t('test_track.plan_view.pass'), id: 'Pass'},
{name: this.$t('test_track.plan_view.failure'), id: 'Failure'},
{name: this.$t('test_track.plan_view.blocking'), id: 'Blocking'},
{name: this.$t('test_track.plan_view.skip'), id: 'Skip'}
{name: this.$t('test_track.case.status_prepare'), id: 'Prepare'},
{name: this.$t('test_track.case.status_pass'), id: 'Pass'},
{name: this.$t('test_track.case.status_un_pass'), id: 'UnPass'},
]
},
}
@ -261,9 +265,7 @@ export default {
if (this.reviewId) {
this.condition.reviewId = this.reviewId;
}
if (this.selectNodeIds && this.selectNodeIds.length > 0) {
this.condition.nodeIds = this.selectNodeIds;
}
this.condition.nodeIds = this.selectNodeIds;
if (this.reviewId) {
this.result = this.$post(this.buildPagePath('/test/review/case/list'), this.condition, response => {
let data = response.data;
@ -339,6 +341,23 @@ export default {
this.$success(this.$t('test_track.cancel_relevance_success'));
});
},
handleEditBatch() {
this.$refs.batchEdit.open(this.selectRows.size);
},
batchEdit(form) {
let reviewId = this.reviewId;
let param = {};
param[form.type] = form.value;
param.ids = Array.from(this.selectRows).map(row => row.caseId);
param.reviewId = reviewId;
this.$post('/test/review/case/batch/edit/status', param, () => {
this.selectRows.clear();
this.status = '';
this.$post('/test/case/review/edit/status/' + reviewId);
this.$success(this.$t('commons.save_success'));
this.$emit('refresh');
});
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.tableData.forEach(item => {

View File

@ -122,8 +122,8 @@ html,body {
/* 滚动条样式 */
::-webkit-scrollbar{
width: 5px;
height: 5px;
width: 10px;
height: 10px;
position: fixed;
}
::-webkit-scrollbar-thumb{

View File

@ -1,3 +1,4 @@
import {humpToLine} from "@/common/js/utils";
export function _handleSelectAll(component, selection, tableData, selectRows) {
if (selection.length > 0) {
@ -33,3 +34,73 @@ export function _handleSelect(component, selection, row, selectRows) {
component.$set(row, "showMore", true);
})
}
// 设置 unSelectIds 查询条件,返回当前选中的条数
export function setUnSelectIds(tableData, condition, selectRows) {
let ids = Array.from(selectRows).map(o => o.id);
let allIDs = tableData.map(o => o.id);
condition.unSelectIds = allIDs.filter(function (val) {
return ids.indexOf(val) === -1
});
}
export function getSelectDataCounts(condition, total, selectRows) {
if (condition.selectAll) {
return total - condition.unSelectIds.length;
} else {
return selectRows.size;
}
}
// 全选操作
export function toggleAllSelection(table, tableData, selectRows) {
//如果已经全选,不需要再操作了
if (selectRows.size != tableData.length) {
table.toggleAllSelection(true);
}
}
//表格数据过滤
export function _filter(filters, condition) {
if (!condition.filters) {
condition.filters = {};
}
for (let filter in filters) {
if (filters.hasOwnProperty(filter)) {
if (filters[filter] && filters[filter].length > 0) {
condition.filters[humpToLine(filter)] = filters[filter];
} else {
condition.filters[humpToLine(filter)] = null;
}
}
}
}
//表格数据排序
export function _sort(column, condition) {
column.prop = humpToLine(column.prop);
if (column.order === 'descending') {
column.order = 'desc';
} else {
column.order = 'asc';
}
if (!condition.orders) {
condition.orders = [];
}
let hasProp = false;
condition.orders.forEach(order => {
if (order.name === column.prop) {
order.type = column.order;
hasProp = true;
}
});
if (!hasProp) {
condition.orders.push({name: column.prop, type: column.order});
}
}

View File

@ -1,13 +1,13 @@
import {
LicenseKey,
PROJECT_ID,
REFRESH_SESSION_USER_URL,
ROLE_ADMIN,
ROLE_ORG_ADMIN,
ROLE_TEST_MANAGER,
ROLE_TEST_USER,
ROLE_TEST_VIEWER,
TokenKey,
PROJECT_ID
TokenKey
} from "./constants";
import axios from "axios";
import {jsPDF} from "jspdf";
@ -138,45 +138,6 @@ export function humpToLine(name) {
return name.replace(/([A-Z])/g, "_$1").toLowerCase();
}
//表格数据过滤
export function _filter(filters, condition) {
if (!condition.filters) {
condition.filters = {};
}
for (let filter in filters) {
if (filters.hasOwnProperty(filter)) {
if (filters[filter] && filters[filter].length > 0) {
condition.filters[humpToLine(filter)] = filters[filter];
} else {
condition.filters[humpToLine(filter)] = null;
}
}
}
}
//表格数据排序
export function _sort(column, condition) {
column.prop = humpToLine(column.prop);
if (column.order === 'descending') {
column.order = 'desc';
} else {
column.order = 'asc';
}
if (!condition.orders) {
condition.orders = [];
}
let hasProp = false;
condition.orders.forEach(order => {
if (order.name === column.prop) {
order.type = column.order;
hasProp = true;
}
});
if (!hasProp) {
condition.orders.push({name: column.prop, type: column.order});
}
}
export function downloadFile(name, content) {
const blob = new Blob([content]);
if ("download" in document.createElement("a")) {
@ -322,21 +283,23 @@ export function _getBodyUploadFiles(request, bodyUploadFiles, obj) {
body = request.body;
}
if (body) {
body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
if (body.kvs) {
body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
});
}
});
});
}
});
}
if (body.binary) {
body.binary.forEach(param => {
if (param.files) {

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