diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java index b243c98a87..d85931cee9 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java @@ -107,14 +107,15 @@ public class ApiDefinitionExecResultService { definitionExecResultMapper.updateByPrimaryKeyWithBLOBs(prevResult); } // 更新用例最后执行结果 - ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = new ApiTestCaseWithBLOBs(); - apiTestCaseWithBLOBs.setId(saveResult.getResourceId()); - apiTestCaseWithBLOBs.setLastResultId(saveResult.getId()); - + ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(saveResult.getResourceId()); + if (apiTestCase != null) { + apiTestCase.setLastResultId(saveResult.getId()); + apiTestCase.setStatus(status); + apiTestCaseMapper.updateByPrimaryKey(apiTestCase); + } if (StringUtils.isNotEmpty(saveResult.getTriggerMode()) && saveResult.getTriggerMode().equals("CASE")) { saveResult.setTriggerMode(TriggerMode.MANUAL.name()); } - apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCaseWithBLOBs); if (!saved) { definitionExecResultMapper.insert(saveResult); } else { @@ -168,7 +169,7 @@ public class ApiDefinitionExecResultService { * @param type */ public void saveApiResultByScheduleTask(TestResult result, String testPlanReportId, String type, String trigeMode) { - testPlanLog.info("TestPlanReportId["+testPlanReportId+"] APICASE OVER."); + testPlanLog.info("TestPlanReportId[" + testPlanReportId + "] APICASE OVER."); String saveResultType = type; if (StringUtils.equalsAny(saveResultType, ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name())) { saveResultType = ApiRunMode.API_PLAN.name(); @@ -249,7 +250,7 @@ public class ApiDefinitionExecResultService { } }); } - testPlanLog.info("TestPlanReportId["+testPlanReportId+"] APICASE OVER. API CASE STATUS:"+ JSONObject.toJSONString(apiIdResultMap)); + testPlanLog.info("TestPlanReportId[" + testPlanReportId + "] APICASE OVER. API CASE STATUS:" + JSONObject.toJSONString(apiIdResultMap)); TestPlanReportService testPlanReportService = CommonBeanFactory.getBean(TestPlanReportService.class); testPlanReportService.updateExecuteApis(testPlanReportId, apiIdResultMap, null, null); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java index a12462ea2f..b484af1a80 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java @@ -20,9 +20,7 @@ import io.metersphere.api.jmeter.JMeterService; import io.metersphere.base.domain.*; import io.metersphere.base.mapper.*; import io.metersphere.base.mapper.ext.*; -import io.metersphere.commons.constants.ApiRunMode; -import io.metersphere.commons.constants.MsTestElementConstants; -import io.metersphere.commons.constants.TestPlanStatus; +import io.metersphere.commons.constants.*; import io.metersphere.commons.exception.MSException; import io.metersphere.commons.utils.*; import io.metersphere.i18n.Translator; @@ -597,16 +595,50 @@ public class ApiTestCaseService { return ids; } + private ApiDefinitionExecResult addResult(String id, String status, ApiDefinitionExecResultMapper batchMapper) { + ApiDefinitionExecResult apiResult = new ApiDefinitionExecResult(); + apiResult.setId(UUID.randomUUID().toString()); + apiResult.setCreateTime(System.currentTimeMillis()); + apiResult.setStartTime(System.currentTimeMillis()); + apiResult.setEndTime(System.currentTimeMillis()); + ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(id); + if (caseWithBLOBs != null) { + apiResult.setName(caseWithBLOBs.getName()); + } + apiResult.setTriggerMode(TriggerMode.BATCH.name()); + apiResult.setActuator("LOCAL"); + apiResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId()); + apiResult.setResourceId(id); + apiResult.setStartTime(System.currentTimeMillis()); + apiResult.setType(ApiRunMode.DEFINITION.name()); + apiResult.setStatus(status); + batchMapper.insert(apiResult); + caseWithBLOBs.setLastResultId(apiResult.getId()); + caseWithBLOBs.setUpdateTime(System.currentTimeMillis()); + caseWithBLOBs.setStatus(APITestStatus.Running.name()); + apiTestCaseMapper.updateByPrimaryKey(caseWithBLOBs); + return apiResult; + } + public void batchRun(ApiCaseBatchRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiTestCaseMapper.selectIdsByQuery(query)); - request.getIds().forEach(id -> { + Map executeQueue = new HashMap<>(); + SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH); + ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class); + request.getIds().forEach(testCaseId -> { + ApiDefinitionExecResult report = addResult(testCaseId, APITestStatus.Running.name(), batchMapper); + executeQueue.put(testCaseId, report); + }); + sqlSession.flushStatements(); + for (String caseId : executeQueue.keySet()) { RunCaseRequest runCaseRequest = new RunCaseRequest(); runCaseRequest.setRunMode(ApiRunMode.DEFINITION.name()); - runCaseRequest.setCaseId(id); + runCaseRequest.setCaseId(caseId); + runCaseRequest.setReportId(executeQueue.get(caseId).getId()); runCaseRequest.setEnvironmentId(request.getEnvironmentId()); run(runCaseRequest); - }); + } } public String run(RunCaseRequest request) { @@ -616,7 +648,6 @@ public class ApiTestCaseService { request.setCaseId(request.getReportId()); } else { testCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getCaseId()); - } if (StringUtils.equals(request.getRunMode(), ApiRunMode.JENKINS.name())) { request.setReportId(request.getEnvironmentId()); @@ -629,11 +660,8 @@ public class ApiTestCaseService { if (testCaseWithBLOBs != null && StringUtils.isNotEmpty(testCaseWithBLOBs.getRequest())) { try { HashTree jmeterHashTree = this.generateHashTree(request, testCaseWithBLOBs); -/* - String runMode = ApiRunMode.JENKINS.name(); -*/ // 调用执行方法 - jMeterService.runLocal(request.getCaseId(), jmeterHashTree, request.getReportId(), request.getRunMode()); + jMeterService.runLocal(request.getReportId(), jmeterHashTree, null, request.getRunMode()); } catch (Exception ex) { LogUtil.error(ex.getMessage(), ex); diff --git a/backend/src/main/java/io/metersphere/api/service/TestResultService.java b/backend/src/main/java/io/metersphere/api/service/TestResultService.java index e0aa8b21fc..3d6bb49a86 100644 --- a/backend/src/main/java/io/metersphere/api/service/TestResultService.java +++ b/backend/src/main/java/io/metersphere/api/service/TestResultService.java @@ -23,7 +23,6 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -68,7 +67,7 @@ public class TestResultService { if (StringUtils.isBlank(debugReportId)) { apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.DEFINITION.name(), TriggerMode.MANUAL.name()); } - //jenkins单接口执行 + //jenkins单接口执行 } else if (StringUtils.equals(runMode, ApiRunMode.JENKINS.name())) { apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.DEFINITION.name(), TriggerMode.API.name()); ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseService.getInfoJenkins(testResult.getTestId()); @@ -90,9 +89,9 @@ public class TestResultService { } else if (StringUtils.equalsAny(runMode, ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name())) { //测试计划定时任务-接口执行逻辑的话,需要同步测试计划的报告数据 if (StringUtils.equals(runMode, ApiRunMode.SCHEDULE_API_PLAN.name())) { - apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult,debugReportId, ApiRunMode.SCHEDULE_API_PLAN.name(),ReportTriggerMode.SCHEDULE.name()); + apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult, debugReportId, ApiRunMode.SCHEDULE_API_PLAN.name(), ReportTriggerMode.SCHEDULE.name()); } else if (StringUtils.equals(runMode, ApiRunMode.JENKINS_API_PLAN.name())) { - apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult,debugReportId, ApiRunMode.JENKINS_API_PLAN.name(),ReportTriggerMode.API.name()); + apiDefinitionExecResultService.saveApiResultByScheduleTask(testResult, debugReportId, ApiRunMode.JENKINS_API_PLAN.name(), ReportTriggerMode.API.name()); } else { apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name(), TriggerMode.MANUAL.name()); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml index 1b08667d4c..840e6ed1a9 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiTestCaseMapper.xml @@ -176,9 +176,9 @@ and (t1.tags is null or t1.tags - - - + + + ) @@ -218,7 +218,7 @@ FROM api_test_case t1 LEFT JOIN api_definition_exec_result t2 ON t1.last_result_id = t2.id - inner join api_definition a on t1.api_definition_id = a.id + inner join api_definition a on t1.api_definition_id = a.id LEFT JOIN `user` u1 ON t1.update_user_id = u1.id LEFT JOIN `user` u2 ON t1.create_user_id = u2.id LEFT JOIN `user` u3 ON t2.user_id = u3.id @@ -235,13 +235,13 @@ SELECT testCase.id,testCase.`name`,ad.`name` AS apiName FROM api_test_case testCase - INNER JOIN api_definition ad ON testCase.api_definition_id = ad.id + INNER JOIN api_definition ad ON testCase.api_definition_id = ad.id WHERE ad.`status` = 'Trash' AND testCase.id IN @@ -542,7 +542,7 @@ SELECT t1.id FROM api_test_case t1 - inner join api_definition a on t1.api_definition_id = a.id + inner join api_definition a on t1.api_definition_id = a.id diff --git a/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue b/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue index aa0d69d653..d616471390 100644 --- a/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue +++ b/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue @@ -239,11 +239,13 @@ export default { this.batchEdit(obj); this.runResult = {testId: getUUID()}; this.$success(this.$t('organization.integration.successful_operation')); + this.$emit("refresh"); }, errorRefresh() { this.batchLoadingIds = []; this.singleLoading = false; this.singleRunId = ""; + this.$emit("refresh"); }, refresh() { this.getApiTest(); @@ -388,6 +390,7 @@ export default { this.runData.push(row.request); /*触发执行操作*/ this.reportId = getUUID().substring(0, 8); + this.$emit("refresh",row.id); }, batchRun() { diff --git a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue index 50f32b24e5..9a9af9c9d0 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue @@ -8,17 +8,21 @@ class="search-input" size="small" v-model="condition.name"/> {{ $t('commons.add') }} - + + + + {{ scope.row.updateTime | timestampFormatDate }} - + @@ -120,15 +138,20 @@ - + @@ -282,6 +305,12 @@ export default { {text: 'P2', value: 'P2'}, {text: 'P3', value: 'P3'} ], + statusFilters: [ + {text: this.$t('api_test.automation.success'), value: 'success'}, + {text: this.$t('api_test.automation.fail'), value: 'error'}, + {text: this.$t('api_test.home_page.detail_card.unexecute'), value: ''}, + {text: '测试中', value: 'Running'} + ], valueArr: { priority: CASE_PRIORITY, method: REQ_METHOD, @@ -390,27 +419,53 @@ export default { } }, methods: { + getStatusClass(status) { + switch (status) { + case "success": + return "ms-success"; + case "error": + return "ms-error"; + case "Running": + return "ms-running"; + default: + return "ms-unexecute"; + } + }, + getStatusTitle(status) { + switch (status) { + case "success": + return this.$t('api_test.automation.success'); + case "error": + return this.$t('api_test.automation.fail'); + case "Running": + return "测试中"; + default: + return this.$t('api_test.home_page.detail_card.unexecute'); + } + }, + handleRunBatch() { this.$refs.batchRun.open(); }, runBatch(environment) { this.condition.environmentId = environment.id; this.condition.ids = this.$refs.caseTable.selectIds; - apiCaseBatchRun(this.condition); - this.condition.ids = []; - this.$refs.batchRun.close(); - this.search(); + this.$post('/api/testcase/batch/run', this.condition, () => { + this.condition.ids = []; + this.$refs.batchRun.close(); + this.search(); + }); }, customHeader() { this.$refs.caseTable.openCustomHeader(); }, - initTable() { + initTable(id) { if (this.$refs.caseTable) { this.$refs.caseTable.clearSelectRows(); } if (this.condition.orders) { - const index = this.condition.orders.findIndex(d => d.name !== undefined && d.name === 'case_path'); - if (index != -1) { + const index = this.condition.orders.findIndex(d => d.name && d.name === 'case_path'); + if (index !== -1) { this.condition.orders.splice(index, 1); } } @@ -420,7 +475,6 @@ export default { this.condition.status = ""; this.condition.moduleIds = this.selectNodeIds; if (this.trashEnable) { - // this.condition.status = "Trash"; this.condition.moduleIds = []; if (this.condition.filters) { if (this.condition.filters.status) { @@ -432,12 +486,9 @@ export default { this.condition.filters = {}; this.condition.filters = {status: ["Trash"]}; } - } else { - if (this.condition.filters) { - if (this.condition.filters.status) { - this.condition.filters.status = []; - } - } + } + if (this.condition.filters && !this.condition.filters.status) { + this.$delete(this.condition.filters, 'status') } if (!this.selectAll) { this.selectAll = false; @@ -463,31 +514,39 @@ export default { this.condition.id = selectParamArr[1]; } } + let isNext = false; if (this.condition.projectId) { this.result = this.$post('/api/testcase/list/' + this.currentPage + "/" + this.pageSize, this.condition, response => { this.total = response.data.itemCount; this.tableData = response.data.listObject; - if (!this.selectAll) { this.unSelection = response.data.listObject.map(s => s.id); } - this.tableData.forEach(item => { if (item.tags && item.tags.length > 0) { item.tags = JSON.parse(item.tags); } + if (id && id === item.id) { + item.status = "Running"; + } + if (item.status === 'Running') { + isNext = true; + } }) - this.$nextTick(function () { if (this.$refs.caseTable) { this.$refs.caseTable.doLayout(); this.$refs.caseTable.checkTableRowIsSelect(); } }) + if (isNext) { + setTimeout(() => { + this.initTable(); + }, 5000); + } }); } }, - open() { this.$refs.searchBar.open(); }, @@ -933,4 +992,18 @@ export default { top: -2px; } +.ms-success { + color: #67C23A; +} + +.ms-error { + color: #F56C6C; +} + +.ms-running { + color: #6D317C; +} + +.ms-unexecute { +} diff --git a/frontend/src/common/js/default-table-header.js b/frontend/src/common/js/default-table-header.js index f8188ba7a5..e11a274370 100644 --- a/frontend/src/common/js/default-table-header.js +++ b/frontend/src/common/js/default-table-header.js @@ -53,11 +53,12 @@ export let CUSTOM_TABLE_HEADER = { {id: 'name', key: '2', label: 'test_track.case.name'}, {id: 'priority', key: '3', label: 'test_track.case.priority'}, {id: 'path', key: '4', label: 'api_test.definition.api_definition_path'}, - {id: 'casePath', key: '5', label: 'api_test.definition.api_case_path'}, - {id: 'tags', key: '6', label: 'commons.tag'}, - {id: 'createUser', key: '7', label: 'api_test.creator'}, - {id: 'updateTime', key: '8', label: 'api_test.definition.api_last_time'}, - {id: 'createTime', key: '9', label: 'commons.create_time'}, + {id: 'status', key: '5', label: 'test_track.plan_view.execute_result'}, + {id: 'casePath', key: '6', label: 'api_test.definition.api_case_path'}, + {id: 'tags', key: '7', label: 'commons.tag'}, + {id: 'createUser', key: '8', label: 'api_test.creator'}, + {id: 'updateTime', key: '9', label: 'api_test.definition.api_last_time'}, + {id: 'createTime', key: '10', label: 'commons.create_time'}, ], //场景测试 API_SCENARIO: [