Merge remote-tracking branch 'origin/master'

This commit is contained in:
song.tianyang 2021-03-24 11:10:10 +08:00
commit 2c5c924244
23 changed files with 161 additions and 67 deletions

View File

@ -5,6 +5,7 @@ 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.base.domain.TestPlanReport;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
@ -14,6 +15,7 @@ import io.metersphere.notice.sender.NoticeModel;
import io.metersphere.notice.service.NoticeSendService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.track.service.TestPlanReportService;
import io.metersphere.track.service.TestPlanService;
import io.metersphere.track.service.TestPlanTestCaseService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@ -197,6 +199,9 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult, ApiRunMode.SCHEDULE_API_PLAN.name());
List<String> testPlanReportIdList = new ArrayList<>();
testPlanReportIdList.add(debugReportId);
for(String testPlanReportId : testPlanReportIdList) { // 更新每个测试计划的状态
testPlanReportService.checkTestPlanStatus(testPlanReportId);
}
testPlanReportService.updateReport(testPlanReportIdList, ApiRunMode.SCHEDULE_API_PLAN.name(), ReportTriggerMode.SCHEDULE.name());
} else {
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name());

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants;
public enum TestPlanStatus {
Prepare, Underway, Completed
Prepare, Underway, Completed, Finished
}

View File

@ -22,7 +22,8 @@ public class ScriptEngineUtils {
}
}
public static String calculate(String input) {
// graal.js 禁止多线程同时访问加上 synchronized
public synchronized static String calculate(String input) {
try {
return engine.eval("calculate('" + input + "')").toString();
} catch (ScriptException e) {

View File

@ -2,6 +2,7 @@ package io.metersphere.notice.service;
import com.alibaba.nacos.client.utils.StringUtils;
import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.notice.domain.MessageDetail;
import io.metersphere.notice.sender.NoticeModel;
import io.metersphere.notice.sender.NoticeSender;
@ -44,6 +45,7 @@ public class NoticeSendService {
}
public void send(String taskType, NoticeModel noticeModel) {
try {
List<MessageDetail> messageDetails;
switch (taskType) {
case NoticeConstants.Mode.API:
@ -61,5 +63,8 @@ public class NoticeSendService {
this.getNoticeSender(messageDetail).send(messageDetail, noticeModel);
}
});
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
}
}
}

View File

@ -5,6 +5,7 @@ import io.metersphere.base.domain.MessageTaskExample;
import io.metersphere.base.mapper.MessageTaskMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator;
import io.metersphere.notice.domain.MessageDetail;
@ -102,6 +103,7 @@ public class NoticeService {
}
public List<MessageDetail> searchMessageByType(String type) {
try {
SessionUser user = SessionUtils.getUser();
String orgId = user.getLastOrganizationId();
List<MessageDetail> messageDetails = new ArrayList<>();
@ -125,6 +127,10 @@ public class NoticeService {
.stream()
.distinct()
.collect(Collectors.toList());
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
return new ArrayList<>();
}
}
private MessageDetail getMessageDetail(List<MessageTask> messageTasks) {

View File

@ -37,6 +37,11 @@ public class TestPlanController {
@Resource
CheckPermissionService checkPermissionService;
@PostMapping("/autoCheck/{testPlanId}")
public void autoCheck(@PathVariable String testPlanId){
testPlanService.checkStatus(testPlanId);
}
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<TestPlanDTOWithMetric>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestPlanRequest request) {
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();

View File

@ -11,10 +11,7 @@ import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanReportMapper;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.notice.sender.NoticeModel;
@ -212,6 +209,15 @@ public class TestPlanReportService {
}
}
public void checkTestPlanStatus(String planReportId) {
try {
TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(planReportId);
testPlanService.checkStatus(testPlanReport.getTestPlanId());
} catch (Exception e) {
LogUtil.error(e.getMessage(), e);
}
}
/**
*
* @param planReportId 测试计划报告ID

View File

@ -194,7 +194,10 @@ public class TestPlanService {
!TestPlanStatus.Completed.name().equals(res.getStatus())) {
//已完成写入实际完成时间
testPlan.setActualEndTime(System.currentTimeMillis());
}
} else if (!res.getStatus().equals(TestPlanStatus.Finished.name()) &&
TestPlanStatus.Finished.name().equals(testPlan.getStatus())) {
testPlan.setActualEndTime(System.currentTimeMillis());
} // 非已结束->已结束更新结束时间
}
List<String> userIds = new ArrayList<>();
@ -367,7 +370,7 @@ public class TestPlanService {
testPlan.setTotal(apiExecResults.size() + scenarioExecResults.size() + functionalExecResults.size() + loadResults.size());
testPlan.setPassRate(MathUtils.getPercentWithDecimal(testPlan.getTested() == 0 ? 0 : testPlan.getPassed() * 1.0 / testPlan.getTested()));
testPlan.setPassRate(MathUtils.getPercentWithDecimal(testPlan.getTested() == 0 ? 0 : testPlan.getPassed() * 1.0 / testPlan.getTotal()));
testPlan.setTestRate(MathUtils.getPercentWithDecimal(testPlan.getTotal() == 0 ? 0 : testPlan.getTested() * 1.0 / testPlan.getTotal()));
});
}
@ -383,6 +386,44 @@ public class TestPlanService {
return testPlans;
}
public void checkStatus(String testPlanId) { // 检查执行结果自动更新计划状态
List<String> statusList = new ArrayList<>();
statusList.addAll(extTestPlanTestCaseMapper.getExecResultByPlanId(testPlanId));
statusList.addAll(testPlanApiCaseService.getExecResultByPlanId(testPlanId));
statusList.addAll(testPlanScenarioCaseService.getExecResultByPlanId(testPlanId));
statusList.addAll(testPlanLoadCaseService.getStatus(testPlanId));
// Prepare, Pass, Failure, Blocking, Skip, Underway
TestPlanDTO testPlanDTO = new TestPlanDTO();
testPlanDTO.setId(testPlanId);
if(statusList.size() == 0) { // 原先status不是prepare, 但删除所有关联用例的情况
testPlanDTO.setStatus(TestPlanStatus.Prepare.name());
editTestPlan(testPlanDTO);
return;
}
int passNum = 0, prepareNum = 0, failNum = 0;
for(String res : statusList) {
if(StringUtils.equals(res, TestPlanTestCaseStatus.Pass.name())
|| StringUtils.equals(res, "success")
|| StringUtils.equals(res, ScenarioStatus.Success.name())) {
passNum++;
} else if (res == null) {
prepareNum++;
} else {
failNum++;
}
}
if(passNum == statusList.size()) { // 全部通过
testPlanDTO.setStatus(TestPlanStatus.Completed.name());
this.editTestPlan(testPlanDTO);
} else if(prepareNum == 0 && passNum + failNum == statusList.size()) { // 已结束
testPlanDTO.setStatus(TestPlanStatus.Finished.name());
editTestPlan(testPlanDTO);
} else if(prepareNum != 0) { // 进行中
testPlanDTO.setStatus(TestPlanStatus.Underway.name());
editTestPlan(testPlanDTO);
}
}
public List<TestPlanDTOWithMetric> listTestPlanByProject(QueryTestPlanRequest request) {
List<TestPlanDTOWithMetric> testPlans = extTestPlanMapper.list(request);
return testPlans;

View File

@ -574,14 +574,15 @@
this.isBtnHide = true;
this.$refs.scenarioApiRelevance.open();
},
recursiveSorting(arr) {
recursiveSorting(arr, scenarioProjectId) {
for (let i in arr) {
arr[i].index = Number(i) + 1;
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) {
arr[i].countController.proceed = true;
}
if (!arr[i].projectId) {
arr[i].projectId = this.projectId;
// IDIDIDID
arr[i].projectId = scenarioProjectId ? scenarioProjectId : this.projectId;
}
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
this.recursiveSorting(arr[i].hashTree);
@ -606,7 +607,7 @@
this.scenarioDefinition[i].projectId = this.projectId;
}
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
this.recursiveSorting(this.scenarioDefinition[i].hashTree);
this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId);
}
// debug
if (this.debugResult && this.debugResult.get(this.scenarioDefinition[i].id)) {

View File

@ -490,14 +490,14 @@
apiListImport() {
this.$refs.scenarioApiRelevance.open();
},
recursiveSorting(arr) {
recursiveSorting(arr, scenarioProjectId) {
for (let i in arr) {
arr[i].index = Number(i) + 1;
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) {
arr[i].countController.proceed = true;
}
if (!arr[i].projectId) {
arr[i].projectId = this.projectId;
arr[i].projectId = scenarioProjectId ? scenarioProjectId : this.projectId;
}
if (arr[i].hashTree != undefined && arr[i].hashTree.length > 0) {
this.recursiveSorting(arr[i].hashTree);
@ -522,7 +522,7 @@
this.scenarioDefinition[i].projectId = this.projectId;
}
if (this.scenarioDefinition[i].hashTree != undefined && this.scenarioDefinition[i].hashTree.length > 0) {
this.recursiveSorting(this.scenarioDefinition[i].hashTree);
this.recursiveSorting(this.scenarioDefinition[i].hashTree, this.scenarioDefinition[i].projectId);
}
// debug
if (this.debugResult && this.debugResult.get(this.scenarioDefinition[i].id)) {

View File

@ -86,6 +86,7 @@
this.$fileUpload(url, null, bodyFiles, reqObj, response => {
this.runId = response.data;
this.getResult();
this.$emit('autoCheckStatus'); //
}, error => {
this.$emit('runRefresh', {});
});

View File

@ -129,6 +129,7 @@
import MsJmxStep from "../step/JmxStep";
import ApiResponseComponent from "../../../automation/scenario/component/ApiResponseComponent";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const esbDefinition = (requireComponent != null && requireComponent.keys().length) > 0 ? requireComponent("./apidefinition/EsbDefinition.vue") : {};
const esbDefinitionResponse = (requireComponent != null && requireComponent.keys().length) > 0 ? requireComponent("./apidefinition/EsbDefinitionResponse.vue") : {};
@ -224,7 +225,7 @@
});
},
singleRun(data) {
if (!this.environment) {
if (this.api.protocol != "DUBBO" && this.api.protocol != "dubbo://" && !this.environment) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}

View File

@ -280,7 +280,7 @@
},
singleRun(row) {
if (!this.environment) {
if (this.currentApi.protocol != "DUBBO" && this.currentApi.protocol != "dubbo://" && !this.environment) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
@ -298,7 +298,7 @@
},
batchRun() {
if (!this.environment) {
if (this.currentApi.protocol != "DUBBO" && this.currentApi.protocol != "dubbo://" && !this.environment) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}

View File

@ -345,13 +345,11 @@ export default {
this.$refs.basicConfig.threadGroups = threadGroups;
this.$refs.pressureConfig.threadGroups = threadGroups;
threadGroups.forEach(tg => {
handler.calculateChart(tg);
})
handler.calculateTotalChart();
},
tgTypeChange(threadGroup) {
let handler = this.$refs.pressureConfig;
handler.calculateChart(threadGroup);
handler.calculateTotalChart();
}
}
}

View File

@ -232,7 +232,7 @@ export default {
callback();
} else {
this.$error(this.$t('load_test.project_file_exist'));
this.$error(this.$t('load_test.project_file_exist') + ', name: ' + file.name);
}
});
},

View File

@ -2,6 +2,7 @@
<div>
<ms-tag v-if="value == 'Prepare'" type="info" :content="$t('test_track.plan.plan_status_prepare')"/>
<ms-tag v-if="value == 'Underway'" type="primary" :content="$t('test_track.plan.plan_status_running')"/>
<ms-tag v-if="value == 'Finished'" type="warning" :content="$t('test_track.plan.plan_status_finished')"/>
<ms-tag v-if="value == 'Completed'" type="success" :content="$t('test_track.plan.plan_status_completed')"/>
<ms-tag v-if="value === 'Trash'" type="danger" effect="plain" :content="$t('test_track.plan.plan_status_trash')"/>
</div>

View File

@ -53,6 +53,10 @@
:command="{item: scope.row, status: 'Underway'}">
{{ $t('test_track.plan.plan_status_running') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{item: scope.row, status: 'Finished'}">
{{ $t('test_track.plan.plan_status_finished') }}
</el-dropdown-item>
<el-dropdown-item :disabled="!isTestManagerOrTestUser"
:command="{item: scope.row, status: 'Completed'}">
{{ $t('test_track.plan.plan_status_completed') }}
@ -82,7 +86,7 @@
show-overflow-tooltip
:key="index">
<template v-slot:default="scope">
<el-progress :percentage="scope.row.testRate"></el-progress>
<el-progress :percentage="scope.row.passRate.substring(0, scope.row.passRate.length-1)"></el-progress>
</template>
</el-table-column>
<el-table-column
@ -263,6 +267,7 @@ export default {
statusFilters: [
{text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'},
{text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'},
{text: this.$t('test_track.plan.plan_status_finished'), value: 'Finished'},
{text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'}
],
stageFilters: [

View File

@ -127,7 +127,7 @@
<!-- 执行组件 -->
<ms-run :debug="false" :type="'API_PLAN'" :reportId="reportId" :run-data="runData"
@runRefresh="runRefresh" ref="runTest"/>
@runRefresh="runRefresh" ref="runTest" @autoCheckStatus="autoCheckStatus"/>
<!-- 批量编辑 -->
<batch-edit :dialog-title="$t('test_track.case.batch_edit_case')" :type-arr="typeArr" :value-arr="valueArr"
@ -319,6 +319,7 @@ export default {
this.$emit('isApiListEnableChange', data);
},
initTable() {
this.autoCheckStatus();
this.selectRows = new Set();
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
@ -520,6 +521,10 @@ export default {
this.$fileUpload("/api/definition/run", null, bodyFiles, reqObj, response => {
});
},
autoCheckStatus() { //
this.$post('/test/plan/autoCheck/' + this.planId, (response) => {
});
},
handleDelete(apiCase) {
if (this.planId) {
this.$get('/test/plan/api/case/delete/' + apiCase.id, () => {

View File

@ -93,7 +93,7 @@
:key="index"
>
<template v-slot:default="scope">
<ms-tag v-for="(tag, index) in scope.row.showTags" :key="tag + '_' + index" type="success" effect="plain" :content="tag" style="margin-left: 5px"/>
<ms-tag v-for="(tag, index) in scope.row.showTags" :key="tag + '_' + index" type="success" effect="plain" :content="tag" style="margin-left: 0px; margin-right: 2px"/>
</template>
</el-table-column>
@ -431,6 +431,7 @@ export default {
},
initTableData() {
this.autoCheckStatus();
if (this.planId) {
// param.planId = this.planId;
this.condition.planId = this.planId;
@ -479,6 +480,10 @@ export default {
}
getLabel(this, TEST_PLAN_FUNCTION_TEST_CASE);
},
autoCheckStatus() {
this.$post('/test/plan/autoCheck/' + this.planId, (response) => {
});
},
showDetail(row, event, column) {
this.isReadOnly = true;
this.$refs.testPlanTestCaseEdit.openTestCaseEdit(row);

View File

@ -213,6 +213,7 @@ export default {
this.$refs.headerCustom.open(this.tableLabel)
},
initTable() {
this.autoCheckStatus();
this.selectRows = new Set();
this.condition.testPlanId = this.planId;
if (this.selectProjectId && this.selectProjectId !== 'root') {
@ -247,6 +248,10 @@ export default {
getLabel(this, TEST_PLAN_LOAD_CASE);
},
autoCheckStatus() {
this.$post('/test/plan/autoCheck/' + this.planId, (response) => {
});
},
refreshStatus() {
this.refreshScheduler = setInterval(() => {
//

View File

@ -1242,6 +1242,7 @@ export default {
input_plan_stage: "Please select stage",
plan_status_prepare: "Not started",
plan_status_running: "Starting",
plan_status_finished: "Finished",
plan_status_completed: "Completed",
plan_status_trash: "Trashed",
planned_start_time: "Scheduled Start Time",

View File

@ -1246,6 +1246,7 @@ export default {
input_plan_stage: "请选择测试阶段",
plan_status_prepare: "未开始",
plan_status_running: "进行中",
plan_status_finished: "已结束",
plan_status_completed: "已完成",
plan_status_trash: "废弃",
planned_start_time: "计划开始",

View File

@ -1244,6 +1244,7 @@ export default {
input_plan_stage: "請選擇測試階段",
plan_status_prepare: "未開始",
plan_status_running: "進行中",
plan_status_finished: "已結束",
plan_status_completed: "已完成",
plan_status_trash: "廢棄",
planned_start_time: "計劃開始",