This commit is contained in:
chenjianxing 2021-01-13 09:56:52 +08:00
commit 53c2d6f7fa
18 changed files with 292 additions and 110 deletions

View File

@ -325,22 +325,17 @@ public class ApiAutomationService {
} }
/** /**
* 场景测试执行 * 生成HashTree
* *
* @param request * @param apiScenarios 场景
* @return * @param request 请求参数
* @param isSave 是否需要生成报告
* @return hashTree
*/ */
public String run(RunScenarioRequest request) { private HashTree generateHashTree(List<ApiScenarioWithBLOBs> apiScenarios, RunScenarioRequest request, boolean isSave) {
List<ApiScenarioWithBLOBs> apiScenarios = null; HashTree jmeterHashTree = new ListedHashTree();
List<String> ids = request.getScenarioIds();
if (request.isSelectAllDate()) {
ids = this.getAllScenarioIdsByFontedSelect(
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
}
apiScenarios = extApiScenarioMapper.selectIds(ids);
MsTestPlan testPlan = new MsTestPlan(); MsTestPlan testPlan = new MsTestPlan();
testPlan.setHashTree(new LinkedList<>()); testPlan.setHashTree(new LinkedList<>());
HashTree jmeterHashTree = new ListedHashTree();
try { try {
boolean isFirst = true; boolean isFirst = true;
for (ApiScenarioWithBLOBs item : apiScenarios) { for (ApiScenarioWithBLOBs item : apiScenarios) {
@ -380,49 +375,44 @@ public class ApiAutomationService {
LinkedList<MsTestElement> scenarios = new LinkedList<>(); LinkedList<MsTestElement> scenarios = new LinkedList<>();
scenarios.add(scenario); scenarios.add(scenario);
// 创建场景报告 // 创建场景报告
createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(), if (isSave) {
request.getExecuteType(), item.getProjectId(), request.getReportUserID()); createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
request.getExecuteType(), item.getProjectId(), request.getReportUserID());
}
group.setHashTree(scenarios); group.setHashTree(scenarios);
testPlan.getHashTree().add(group); testPlan.getHashTree().add(group);
} }
} catch (Exception ex) { } catch (Exception ex) {
MSException.throwException(ex.getMessage()); MSException.throwException(ex.getMessage());
} }
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig()); testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
return jmeterHashTree;
}
/**
* 场景测试执行
*
* @param request
* @return
*/
public String run(RunScenarioRequest request) {
List<ApiScenarioWithBLOBs> apiScenarios = null;
List<String> ids = request.getScenarioIds();
if (request.isSelectAllDate()) {
ids = this.getAllScenarioIdsByFontedSelect(
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds());
}
apiScenarios = extApiScenarioMapper.selectIds(ids);
String runMode = ApiRunMode.SCENARIO.name(); String runMode = ApiRunMode.SCENARIO.name();
if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) { if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) {
runMode = ApiRunMode.SCENARIO_PLAN.name(); runMode = ApiRunMode.SCENARIO_PLAN.name();
} }
// 调用执行方法 // 调用执行方法
jMeterService.runDefinition(request.getId(), jmeterHashTree, request.getReportId(), runMode); jMeterService.runDefinition(request.getId(), generateHashTree(apiScenarios, request, true), request.getReportId(), runMode);
return request.getId(); return request.getId();
} }
/**
* 获取前台查询条件查询的所有(未经分页筛选)数据ID
*
* @param moduleIds 模块ID_前台查询时所选择的
* @param name 搜索条件_名称_前台查询时所输入的
* @param projectId 所属项目_前台查询时所在项目
* @param filters 过滤集合__前台查询时的过滤条件
* @param unSelectIds 未勾选ID_前台没有勾选的ID
* @return
*/
private List<String> getAllScenarioIdsByFontedSelect(List<String> moduleIds, String name, String projectId, List<String> filters, List<String> unSelectIds) {
ApiScenarioRequest selectRequest = new ApiScenarioRequest();
selectRequest.setModuleIds(moduleIds);
selectRequest.setName(name);
selectRequest.setProjectId(projectId);
selectRequest.setFilters(filters);
selectRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
List<ApiScenarioDTO> list = extApiScenarioMapper.list(selectRequest);
List<String> allIds = list.stream().map(ApiScenarioDTO::getId).collect(Collectors.toList());
List<String> ids = allIds.stream().filter(id -> !unSelectIds.contains(id)).collect(Collectors.toList());
return ids;
}
/** /**
* 场景测试执行 * 场景测试执行
* *
@ -460,6 +450,29 @@ public class ApiAutomationService {
return dto; return dto;
} }
/**
* 获取前台查询条件查询的所有(未经分页筛选)数据ID
*
* @param moduleIds 模块ID_前台查询时所选择的
* @param name 搜索条件_名称_前台查询时所输入的
* @param projectId 所属项目_前台查询时所在项目
* @param filters 过滤集合__前台查询时的过滤条件
* @param unSelectIds 未勾选ID_前台没有勾选的ID
* @return
*/
private List<String> getAllScenarioIdsByFontedSelect(List<String> moduleIds, String name, String projectId, List<String> filters, List<String> unSelectIds) {
ApiScenarioRequest selectRequest = new ApiScenarioRequest();
selectRequest.setModuleIds(moduleIds);
selectRequest.setName(name);
selectRequest.setProjectId(projectId);
selectRequest.setFilters(filters);
selectRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
List<ApiScenarioDTO> list = extApiScenarioMapper.list(selectRequest);
List<String> allIds = list.stream().map(ApiScenarioDTO::getId).collect(Collectors.toList());
List<String> ids = allIds.stream().filter(id -> !unSelectIds.contains(id)).collect(Collectors.toList());
return ids;
}
public String addScenarioToPlan(SaveApiPlanRequest request) { public String addScenarioToPlan(SaveApiPlanRequest request) {
if (CollectionUtils.isEmpty(request.getPlanIds())) { if (CollectionUtils.isEmpty(request.getPlanIds())) {
MSException.throwException(Translator.get("plan id is null ")); MSException.throwException(Translator.get("plan id is null "));
@ -578,51 +591,8 @@ public class ApiAutomationService {
apiScenarios = extApiScenarioMapper.selectIds(ids); apiScenarios = extApiScenarioMapper.selectIds(ids);
MsTestPlan testPlan = new MsTestPlan(); MsTestPlan testPlan = new MsTestPlan();
testPlan.setHashTree(new LinkedList<>()); testPlan.setHashTree(new LinkedList<>());
HashTree jmeterHashTree = new ListedHashTree();
try {
boolean isFirst = true;
for (ApiScenarioWithBLOBs item : apiScenarios) {
if (item.getStepTotal() == 0) {
MSException.throwException(item.getName() + "" + Translator.get("automation_exec_info"));
break;
}
MsThreadGroup group = new MsThreadGroup();
group.setLabel(item.getName());
group.setName(UUID.randomUUID().toString());
// 批量执行的结果直接存储为报告
if (isFirst) {
group.setName(request.getId());
isFirst = false;
}
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JSONObject element = JSON.parseObject(item.getScenarioDefinition());
MsScenario scenario = JSONObject.parseObject(item.getScenarioDefinition(), MsScenario.class);
// 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取 HashTree jmeterHashTree = generateHashTree(apiScenarios, request, false);
if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) {
LinkedList<MsTestElement> elements = mapper.readValue(element.getString("hashTree"),
new TypeReference<LinkedList<MsTestElement>>() {
});
scenario.setHashTree(elements);
}
if (StringUtils.isNotEmpty(element.getString("variables"))) {
LinkedList<ScenarioVariable> variables = mapper.readValue(element.getString("variables"),
new TypeReference<LinkedList<ScenarioVariable>>() {
});
scenario.setVariables(variables);
}
group.setEnableCookieShare(scenario.isEnableCookieShare());
LinkedList<MsTestElement> scenarios = new LinkedList<>();
scenarios.add(scenario);
group.setHashTree(scenarios);
testPlan.getHashTree().add(group);
}
} catch (Exception ex) {
MSException.throwException(ex.getMessage());
}
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
String jmx = testPlan.getJmx(jmeterHashTree); String jmx = testPlan.getJmx(jmeterHashTree);
String name = request.getName() + ".jmx"; String name = request.getName() + ".jmx";

View File

@ -6,6 +6,7 @@ import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.track.dto.TestCaseReportMetricDTO; import io.metersphere.track.dto.TestCaseReportMetricDTO;
import io.metersphere.track.dto.TestPlanCaseDTO; import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.dto.TestPlanDTO; import io.metersphere.track.dto.TestPlanDTO;
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
public abstract class ReportComponent { public abstract class ReportComponent {
@ -26,4 +27,7 @@ public abstract class ReportComponent {
public void readRecord(ApiScenarioDTO testCase) { public void readRecord(ApiScenarioDTO testCase) {
} }
public void readRecord(TestPlanLoadCaseDTO testCase) {
}
} }

View File

@ -4,10 +4,7 @@ import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ScenarioStatus; import io.metersphere.api.dto.automation.ScenarioStatus;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.commons.constants.TestPlanTestCaseStatus; import io.metersphere.commons.constants.TestPlanTestCaseStatus;
import io.metersphere.track.dto.FailureTestCasesAdvanceDTO; import io.metersphere.track.dto.*;
import io.metersphere.track.dto.TestCaseReportMetricDTO;
import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.dto.TestPlanDTO;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
@ -17,6 +14,7 @@ public class ReportFailureAdvanceResultComponent extends ReportComponent {
private List<TestPlanCaseDTO> functionalTestCases = new ArrayList<>(); private List<TestPlanCaseDTO> functionalTestCases = new ArrayList<>();
private List<TestPlanApiCaseDTO> apiTestCases = new ArrayList<>(); private List<TestPlanApiCaseDTO> apiTestCases = new ArrayList<>();
private List<ApiScenarioDTO> scenarioTestCases = new ArrayList<>(); private List<ApiScenarioDTO> scenarioTestCases = new ArrayList<>();
private List<TestPlanLoadCaseDTO> loadTestCases = new ArrayList<>();
public ReportFailureAdvanceResultComponent(TestPlanDTO testPlan) { public ReportFailureAdvanceResultComponent(TestPlanDTO testPlan) {
super(testPlan); super(testPlan);
@ -44,12 +42,20 @@ public class ReportFailureAdvanceResultComponent extends ReportComponent {
} }
} }
@Override
public void readRecord(TestPlanLoadCaseDTO testCase) {
if (StringUtils.equals(testCase.getCaseStatus(), "error")) {
this.loadTestCases.add(testCase);
}
}
@Override @Override
public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) { public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) {
FailureTestCasesAdvanceDTO failureTestCasesAdvanceDTO = new FailureTestCasesAdvanceDTO(); FailureTestCasesAdvanceDTO failureTestCasesAdvanceDTO = new FailureTestCasesAdvanceDTO();
failureTestCasesAdvanceDTO.setFunctionalTestCases(functionalTestCases); failureTestCasesAdvanceDTO.setFunctionalTestCases(functionalTestCases);
failureTestCasesAdvanceDTO.setApiTestCases(apiTestCases); failureTestCasesAdvanceDTO.setApiTestCases(apiTestCases);
failureTestCasesAdvanceDTO.setScenarioTestCases(scenarioTestCases); failureTestCasesAdvanceDTO.setScenarioTestCases(scenarioTestCases);
failureTestCasesAdvanceDTO.setLoadTestCases(loadTestCases);
testCaseReportMetric.setFailureTestCases(failureTestCasesAdvanceDTO); testCaseReportMetric.setFailureTestCases(failureTestCasesAdvanceDTO);
} }
} }

View File

@ -16,15 +16,19 @@ public class ReportResultAdvancedChartComponent extends ReportComponent {
Map<String, TestCaseReportStatusResultDTO> functionalStatusResultMap = new HashMap<>(); Map<String, TestCaseReportStatusResultDTO> functionalStatusResultMap = new HashMap<>();
Map<String, TestCaseReportStatusResultDTO> apiStatusResultMap = new HashMap<>(); Map<String, TestCaseReportStatusResultDTO> apiStatusResultMap = new HashMap<>();
Map<String, TestCaseReportStatusResultDTO> scenarioStatusResultMap = new HashMap<>(); Map<String, TestCaseReportStatusResultDTO> scenarioStatusResultMap = new HashMap<>();
Map<String, TestCaseReportStatusResultDTO> loadStatusResultMap = new HashMap<>();
private static Map<String, String> apiResultMap = new HashMap<>(); private static Map<String, String> apiResultMap = new HashMap<>();
private static Map<String, String> scenarioResultMap = new HashMap<>(); private static Map<String, String> scenarioResultMap = new HashMap<>();
private static Map<String, String> loadResultMap = new HashMap<>();
static { static {
apiResultMap.put("success", TestPlanTestCaseStatus.Pass.name()); apiResultMap.put("success", TestPlanTestCaseStatus.Pass.name());
apiResultMap.put("error", TestPlanTestCaseStatus.Failure.name()); apiResultMap.put("error", TestPlanTestCaseStatus.Failure.name());
scenarioResultMap.put(ScenarioStatus.Success.name(), TestPlanTestCaseStatus.Pass.name()); scenarioResultMap.put(ScenarioStatus.Success.name(), TestPlanTestCaseStatus.Pass.name());
scenarioResultMap.put(ScenarioStatus.Fail.name(), TestPlanTestCaseStatus.Failure.name()); scenarioResultMap.put(ScenarioStatus.Fail.name(), TestPlanTestCaseStatus.Failure.name());
loadResultMap.put("success", TestPlanTestCaseStatus.Pass.name());
loadResultMap.put("error", TestPlanTestCaseStatus.Failure.name());
} }
public ReportResultAdvancedChartComponent(TestPlanDTO testPlan) { public ReportResultAdvancedChartComponent(TestPlanDTO testPlan) {
@ -47,6 +51,11 @@ public class ReportResultAdvancedChartComponent extends ReportComponent {
getStatusResultMap(scenarioStatusResultMap, scenarioResultMap.get(testCase.getLastResult())); getStatusResultMap(scenarioStatusResultMap, scenarioResultMap.get(testCase.getLastResult()));
} }
@Override
public void readRecord(TestPlanLoadCaseDTO testCase) {
getStatusResultMap(loadStatusResultMap, loadResultMap.get(testCase.getCaseStatus()));
}
@Override @Override
public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) { public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) {
testCaseReportMetric.setExecuteResult(getReportStatusResult()); testCaseReportMetric.setExecuteResult(getReportStatusResult());
@ -57,6 +66,7 @@ public class ReportResultAdvancedChartComponent extends ReportComponent {
buildFunctionalStatusResult(reportStatusResult); buildFunctionalStatusResult(reportStatusResult);
buildApiStatusResult(reportStatusResult); buildApiStatusResult(reportStatusResult);
buildScenarioStatusResult(reportStatusResult); buildScenarioStatusResult(reportStatusResult);
buildLoadStatusResult(reportStatusResult);
return reportStatusResult; return reportStatusResult;
} }
@ -87,6 +97,14 @@ public class ReportResultAdvancedChartComponent extends ReportComponent {
reportStatusResult.setScenarioResult(scenarioStatusResult); reportStatusResult.setScenarioResult(scenarioStatusResult);
} }
private void buildLoadStatusResult(TestCaseReportAdvanceStatusResultDTO reportStatusResult) {
List<TestCaseReportStatusResultDTO> loadStatusResult = new ArrayList<>();
addToReportStatusResultList(loadStatusResultMap, loadStatusResult, TestPlanTestCaseStatus.Pass.name());
addToReportStatusResultList(loadStatusResultMap, loadStatusResult, TestPlanTestCaseStatus.Failure.name());
addToReportStatusResultList(loadStatusResultMap, loadStatusResult, TestPlanTestCaseStatus.Underway.name());
reportStatusResult.setLoadResult(loadStatusResult);
}
private void addToReportStatusResultList(Map<String, TestCaseReportStatusResultDTO> resultMap, List<TestCaseReportStatusResultDTO> reportStatusResultList, String status) { private void addToReportStatusResultList(Map<String, TestCaseReportStatusResultDTO> resultMap, List<TestCaseReportStatusResultDTO> reportStatusResultList, String status) {
if (resultMap.get(status) != null) { if (resultMap.get(status) != null) {
reportStatusResultList.add(resultMap.get(status)); reportStatusResultList.add(resultMap.get(status));

View File

@ -13,4 +13,5 @@ public class FailureTestCasesAdvanceDTO {
private List<TestPlanCaseDTO> functionalTestCases; private List<TestPlanCaseDTO> functionalTestCases;
private List<TestPlanApiCaseDTO> apiTestCases; private List<TestPlanApiCaseDTO> apiTestCases;
private List<ApiScenarioDTO> scenarioTestCases; private List<ApiScenarioDTO> scenarioTestCases;
private List<TestPlanLoadCaseDTO> loadTestCases;
} }

View File

@ -11,5 +11,6 @@ public class TestCaseReportAdvanceStatusResultDTO {
private List<TestCaseReportStatusResultDTO> functionalResult; private List<TestCaseReportStatusResultDTO> functionalResult;
private List<TestCaseReportStatusResultDTO> apiResult; private List<TestCaseReportStatusResultDTO> apiResult;
private List<TestCaseReportStatusResultDTO> scenarioResult; private List<TestCaseReportStatusResultDTO> scenarioResult;
private List<TestCaseReportStatusResultDTO> loadResult;
} }

View File

@ -26,13 +26,11 @@ import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService; import io.metersphere.service.SystemParameterService;
import io.metersphere.track.Factory.ReportComponentFactory; import io.metersphere.track.Factory.ReportComponentFactory;
import io.metersphere.track.domain.ReportComponent; import io.metersphere.track.domain.ReportComponent;
import io.metersphere.track.dto.TestCaseReportMetricDTO; import io.metersphere.track.dto.*;
import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.dto.TestPlanDTO;
import io.metersphere.track.dto.TestPlanDTOWithMetric;
import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest; import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest;
import io.metersphere.track.request.testcase.QueryTestPlanRequest; import io.metersphere.track.request.testcase.QueryTestPlanRequest;
import io.metersphere.track.request.testplan.AddTestPlanRequest; import io.metersphere.track.request.testplan.AddTestPlanRequest;
import io.metersphere.track.request.testplan.LoadCaseRequest;
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest; import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -649,6 +647,7 @@ public class TestPlanService {
List<Issues> issues = buildFunctionalCaseReport(planId, components); List<Issues> issues = buildFunctionalCaseReport(planId, components);
buildApiCaseReport(planId, components); buildApiCaseReport(planId, components);
buildScenarioCaseReport(planId, components); buildScenarioCaseReport(planId, components);
buildLoadCaseReport(planId, components);
TestCaseReportMetricDTO testCaseReportMetricDTO = new TestCaseReportMetricDTO(); TestCaseReportMetricDTO testCaseReportMetricDTO = new TestCaseReportMetricDTO();
components.forEach(component -> { components.forEach(component -> {
@ -680,6 +679,17 @@ public class TestPlanService {
} }
} }
public void buildLoadCaseReport(String planId, List<ReportComponent> components) {
LoadCaseRequest request = new LoadCaseRequest();
request.setTestPlanId(planId);
List<TestPlanLoadCaseDTO> loadDTOs = testPlanLoadCaseService.list(request);
for (TestPlanLoadCaseDTO item : loadDTOs) {
for (ReportComponent component : components) {
component.readRecord(item);
}
}
}
public List<Issues> buildFunctionalCaseReport(String planId, List<ReportComponent> components) { public List<Issues> buildFunctionalCaseReport(String planId, List<ReportComponent> components) {
IssuesService issuesService = (IssuesService) CommonBeanFactory.getBean("issuesService"); IssuesService issuesService = (IssuesService) CommonBeanFactory.getBean("issuesService");
List<TestPlanCaseDTO> testPlanTestCases = listTestCaseByPlanId(planId); List<TestPlanCaseDTO> testPlanTestCases = listTestCaseByPlanId(planId);

View File

@ -13,7 +13,7 @@
<slot name="headerLeft"> <slot name="headerLeft">
<i class="icon el-icon-arrow-right" :class="{'is-active': request.active}" <i class="icon el-icon-arrow-right" :class="{'is-active': request.active}"
@click="active(request)"/> @click="active(request)"/>
<el-input v-if="(isShowInput || !request.name) && !isDeletedOrRef" size="small" v-model="request.name" class="name-input" <el-input draggable v-if="(isShowInput || !request.name) && !isDeletedOrRef" size="small" v-model="request.name" class="name-input"
@blur="isShowInput = false" :placeholder="$t('commons.input_name')"/> @blur="isShowInput = false" :placeholder="$t('commons.input_name')"/>
<span v-else> <span v-else>
{{request.name}} {{request.name}}

View File

@ -3,7 +3,7 @@
@copy="copyRow" @copy="copyRow"
@remove="remove" @remove="remove"
:data="jsr223ProcessorData" :data="jsr223ProcessorData"
:draggable="true" :draggable="draggable"
color="#B8741A" color="#B8741A"
background-color="#F9F1EA" background-color="#F9F1EA"
:title="title"> :title="title">
@ -39,6 +39,7 @@
import MsInstructionsIcon from "../../../common/components/MsInstructionsIcon"; import MsInstructionsIcon from "../../../common/components/MsInstructionsIcon";
import MsDropdown from "../../../common/components/MsDropdown"; import MsDropdown from "../../../common/components/MsDropdown";
import ApiBaseComponent from "./common/ApiBaseComponent"; import ApiBaseComponent from "./common/ApiBaseComponent";
export default { export default {
name: "MsJsr233Processor", name: "MsJsr233Processor",
components: {ApiBaseComponent, MsDropdown, MsInstructionsIcon, MsCodeEdit}, components: {ApiBaseComponent, MsDropdown, MsInstructionsIcon, MsCodeEdit},
@ -92,6 +93,10 @@
this.jsr223ProcessorData = this.jsr223Processor; this.jsr223ProcessorData = this.jsr223Processor;
}, },
props: { props: {
draggable: {
type: Boolean,
default: false,
},
isReadOnly: { isReadOnly: {
type: Boolean, type: Boolean,
default: default:
@ -147,26 +152,33 @@
.ace_editor { .ace_editor {
border-radius: 5px; border-radius: 5px;
} }
.script-content { .script-content {
height: calc(100vh - 570px); height: calc(100vh - 570px);
} }
.script-index { .script-index {
padding: 0 20px; padding: 0 20px;
} }
.template-title { .template-title {
margin-bottom: 5px; margin-bottom: 5px;
font-weight: bold; font-weight: bold;
font-size: 15px; font-size: 15px;
} }
.document-url { .document-url {
margin-top: 10px; margin-top: 10px;
} }
.instructions-icon { .instructions-icon {
margin-left: 5px; margin-left: 5px;
} }
.ms-dropdown { .ms-dropdown {
margin-bottom: 20px; margin-bottom: 20px;
} }
/deep/ .el-divider { /deep/ .el-divider {
margin-bottom: 10px; margin-bottom: 10px;
} }

View File

@ -10,6 +10,7 @@
:title="$t('api_test.automation.loop_controller')"> :title="$t('api_test.automation.loop_controller')">
<template v-slot:headerLeft> <template v-slot:headerLeft>
<i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}" @click="active(controller)" style="margin-right: 10px"/>
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="LOOP_COUNT">{{$t('loop.loops_title')}}</el-radio> <el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="LOOP_COUNT">{{$t('loop.loops_title')}}</el-radio>
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="FOREACH">{{$t('loop.foreach')}}</el-radio> <el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="FOREACH">{{$t('loop.foreach')}}</el-radio>
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio> <el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio>
@ -71,6 +72,7 @@
<script> <script>
import ApiBaseComponent from "./common/ApiBaseComponent"; import ApiBaseComponent from "./common/ApiBaseComponent";
export default { export default {
name: "MsLoopController", name: "MsLoopController",
components: {ApiBaseComponent}, components: {ApiBaseComponent},
@ -157,10 +159,15 @@
.ms-span { .ms-span {
margin: 10px; margin: 10px;
} }
.ms-radio { .ms-radio {
color: #606266; color: #606266;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-size: 13px; font-size: 13px;
font-weight: normal; font-weight: normal;
} }
.icon.is-active {
transform: rotate(90deg);
}
</style> </style>

View File

@ -72,9 +72,13 @@
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3" <ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/> :jsr223-processor="row"/>
<!--断言规则--> <!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/> <div style="margin-top: 10px">
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
</div>
<!--提取规则--> <!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/> <div style="margin-top: 10px">
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</div>
</div> </div>
</div> </div>
</el-col> </el-col>

View File

@ -50,9 +50,13 @@
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3" <ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/> :jsr223-processor="row"/>
<!--断言规则--> <!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/> <div style="margin-top: 10px">
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
</div>
<!--提取规则--> <!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/> <div style="margin-top: 10px">
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</div>
</div> </div>
</div> </div>
</el-col> </el-col>

View File

@ -76,9 +76,13 @@
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" <ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')"
:jsr223-processor="row"/> :jsr223-processor="row"/>
<!--断言规则--> <!--断言规则-->
<ms-api-assertions :response="response" v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/> <div style="margin-top: 10px">
<ms-api-assertions :response="response" v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
</div>
<!--提取规则--> <!--提取规则-->
<ms-api-extract :response="response" :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/> <div style="margin-top: 10px">
<ms-api-extract :response="response" :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</div>
</div> </div>
</div> </div>
</el-col> </el-col>

View File

@ -184,6 +184,7 @@ export default {
}, },
methods: { methods: {
initTable() { initTable() {
console.log('init')
this.selectRows = new Set(); this.selectRows = new Set();
let param = {}; let param = {};
param.testPlanId = this.planId; param.testPlanId = this.planId;
@ -198,7 +199,7 @@ export default {
}, },
refreshStatus() { refreshStatus() {
this.refreshScheduler = setInterval(() => { this.refreshScheduler = setInterval(() => {
let arr = this.tableData.filter(data => data.status !== 'Completed' && data.status !== 'Error'); let arr = this.tableData.filter(data => data.status !== 'Completed' && data.status !== 'Error' && data.status !== "Saved");
if (arr.length > 0) { if (arr.length > 0) {
this.initTable(); this.initTable();
} else { } else {

View File

@ -4,6 +4,7 @@
<functional-failure-cases-list v-if="showFunctional" :functional-test-cases="failureTestCases.functionalTestCases"/> <functional-failure-cases-list v-if="showFunctional" :functional-test-cases="failureTestCases.functionalTestCases"/>
<api-failure-cases-list v-if="showApi" :api-test-cases="failureTestCases.apiTestCases"/> <api-failure-cases-list v-if="showApi" :api-test-cases="failureTestCases.apiTestCases"/>
<scenario-failure-cases-list v-if="showScenario" :scenario-test-cases="failureTestCases.scenarioTestCases"/> <scenario-failure-cases-list v-if="showScenario" :scenario-test-cases="failureTestCases.scenarioTestCases"/>
<load-failure-cases-list v-if="showLoad" :load-test-cases="failureTestCases.loadTestCases"/>
</common-component> </common-component>
</template> </template>
@ -18,13 +19,15 @@
import FunctionalFailureCasesList from "./component/FunctionalFailureCasesList"; import FunctionalFailureCasesList from "./component/FunctionalFailureCasesList";
import ApiFailureCasesList from "./component/ApiFailureCasesList"; import ApiFailureCasesList from "./component/ApiFailureCasesList";
import ScenarioFailureCasesList from "./component/ScenarioFailureCasesList"; import ScenarioFailureCasesList from "./component/ScenarioFailureCasesList";
import LoadFailureCasesList
from "@/business/components/track/plan/view/comonents/report/TemplateComponent/component/LoadFailureCasesList";
export default { export default {
name: "FailureResultAdvanceComponent", name: "FailureResultAdvanceComponent",
components: { components: {
ScenarioFailureCasesList, ScenarioFailureCasesList,
ApiFailureCasesList, ApiFailureCasesList,
FunctionalFailureCasesList, FunctionalFailureCasesList,
StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem, CommonComponent}, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem, CommonComponent, LoadFailureCasesList},
props: { props: {
failureTestCases: { failureTestCases: {
type: Object, type: Object,
@ -73,6 +76,15 @@
lastResult: "Failure", lastResult: "Failure",
updateTime: new Date(), updateTime: new Date(),
} }
],
loadTestCases: [
{
caseName: 'testCase4',
projectName: 'P3',
userName: '/module1/module2',
createTime: new Date(),
status: 'error',
}
] ]
} }
} }
@ -80,14 +92,18 @@
}, },
computed: { computed: {
showFunctional() { showFunctional() {
return this.failureTestCases.functionalTestCases.length > 0 || (this.failureTestCases.apiTestCases.length <= 0 && this.failureTestCases.scenarioTestCases.length <= 0); return this.failureTestCases.functionalTestCases.length > 0
|| (this.failureTestCases.apiTestCases.length <= 0 && this.failureTestCases.scenarioTestCases.length <= 0 && this.failureTestCases.loadTestCases.length <= 0);
}, },
showApi() { showApi() {
return this.failureTestCases.apiTestCases.length > 0; return this.failureTestCases.apiTestCases.length > 0;
}, },
showScenario() { showScenario() {
return this.failureTestCases.scenarioTestCases.length > 0; return this.failureTestCases.scenarioTestCases.length > 0;
} },
showLoad() {
return this.failureTestCases.loadTestCases.length > 0;
},
}, },
methods: { methods: {
goFailureTestCase(row) { goFailureTestCase(row) {

View File

@ -17,6 +17,11 @@
<ms-pie-chart v-if="isShow" :text="'场景测试用例'" <ms-pie-chart v-if="isShow" :text="'场景测试用例'"
:name="$t('test_track.plan_view.test_result')" :data="scenarioCharData"/> :name="$t('test_track.plan_view.test_result')" :data="scenarioCharData"/>
</div> </div>
<div class="char-item" v-if="showLoad">
<ms-pie-chart v-if="isShow" :text="'性能测试用例'"
:name="$t('test_track.plan_view.test_result')" :data="loadCharData"/>
</div>
</div> </div>
</common-component> </common-component>
@ -43,6 +48,7 @@
functionalCharData: [], functionalCharData: [],
apiCharData: [], apiCharData: [],
scenarioCharData: [], scenarioCharData: [],
loadCharData: [],
isShow: true isShow: true
} }
}, },
@ -69,19 +75,28 @@
{status: 'Failure', count: '350'}, {status: 'Failure', count: '350'},
{status: 'Underway', count: '110'}, {status: 'Underway', count: '110'},
], ],
loadResult: [
{status: 'Pass', count: '205'},
{status: 'Failure', count: '350'},
{status: 'Underway', count: '110'},
],
} }
} }
} }
}, },
computed: { computed: {
showFunctional() { showFunctional() {
return this.executeResult.functionalResult.length > 0 || (this.executeResult.apiResult.length <= 0 && this.executeResult.scenarioResult.length <= 0); return this.executeResult.functionalResult.length > 0
|| (this.executeResult.apiResult.length <= 0 && this.executeResult.scenarioResult.length <= 0 && this.executeResult.loadResult.length <= 0);
}, },
showApi() { showApi() {
return this.executeResult.apiResult.length > 0; return this.executeResult.apiResult.length > 0;
}, },
showScenario() { showScenario() {
return this.executeResult.scenarioResult.length > 0; return this.executeResult.scenarioResult.length > 0;
},
showLoad() {
return this.executeResult.loadResult.length > 0;
} }
}, },
watch: { watch: {
@ -97,6 +112,7 @@
this.getFunctionalCharData(); this.getFunctionalCharData();
this.getApiCharData(); this.getApiCharData();
this.getScenarioCharData(); this.getScenarioCharData();
this.getLoadCharData();
this.reload(); this.reload();
}, },
getFunctionalCharData() { getFunctionalCharData() {
@ -129,6 +145,16 @@
}); });
} }
}, },
getLoadCharData() {
this.loadCharData = [];
if (this.executeResult.loadResult) {
this.executeResult.loadResult.forEach(item => {
let data = this.dataMap.get(item.status);
data.value = item.count;
this.loadCharData.push(data);
});
}
},
reload() { reload() {
this.isShow = false; this.isShow = false;
this.$nextTick(function () { this.$nextTick(function () {

View File

@ -0,0 +1,96 @@
<template>
<div class="failure-cases-list">
<div class="failure-cases-list-header">
性能测试用例
</div>
<el-table
row-key="id"
@row-click="goFailureTestCase"
:data="loadTestCases">
<el-table-column
prop="caseName"
:label="$t('commons.name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="projectName"
:label="$t('load_test.project_name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="userName"
:label="$t('load_test.user_name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
sortable
prop="createTime"
:label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
prop="status"
column-key="status"
:filters="statusFilters"
:label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-performance-test-status :row="row"/>
</template>
</el-table-column>
<!-- <el-table-column-->
<!-- prop="caseStatus"-->
<!-- :label="$t('test_track.plan.load_case.execution_status')">-->
<!-- <template v-slot:default="{row}">-->
<!-- <el-tag size="mini" type="danger" v-if="row.caseStatus === 'error'">-->
<!-- {{ row.caseStatus }}-->
<!-- </el-tag>-->
<!-- <el-tag size="mini" type="success" v-else-if="row.caseStatus === 'success'">-->
<!-- {{ row.caseStatus }}-->
<!-- </el-tag>-->
<!-- <span v-else>-</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
</el-table>
</div>
</template>
<script>
import MsTag from "@/business/components/common/components/MsTag";
import PriorityTableItem from "@/business/components/track/common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "@/business/components/track/common/tableItems/planview/TypeTableItem";
import MethodTableItem from "@/business/components/track/common/tableItems/planview/MethodTableItem";
import StatusTableItem from "@/business/components/track/common/tableItems/planview/StatusTableItem";
import MsPerformanceTestStatus from "@/business/components/performance/test/PerformanceTestStatus";
export default {
name: "LoadFailureCasesList",
components: {MsTag, PriorityTableItem, TypeTableItem, MethodTableItem, StatusTableItem, MsPerformanceTestStatus},
props: ['loadTestCases'],
data() {
return {
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
],
}
},
methods: {
goFailureTestCase(row) {
this.$emit("openFailureTestCase", row);
}
}
}
</script>
<style scoped>
</style>

View File

@ -205,11 +205,13 @@ export default {
if (this.reportId) { if (this.reportId) {
this.result = this.$get("/performance/report/" + this.reportId, res => { this.result = this.$get("/performance/report/" + this.reportId, res => {
let data = res.data; let data = res.data;
this.status = data.status; if (data) {
this.$set(this.report, "id", this.reportId); this.status = data.status;
this.$set(this.report, "status", data.status); this.$set(this.report, "id", this.reportId);
if (this.status === "Completed") { this.$set(this.report, "status", data.status);
this.initReportTimeInfo(); if (this.status === "Completed") {
this.initReportTimeInfo();
}
} }
}); });
} }