diff --git a/backend/pom.xml b/backend/pom.xml index c54fc88b7c..7c5cb8a95b 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -139,7 +139,7 @@ org.springdoc springdoc-openapi-ui - 1.2.32 + 1.5.6 @@ -280,7 +280,7 @@ io.swagger.parser.v3 swagger-parser - 2.0.22 + 2.0.24 diff --git a/backend/src/main/java/io/metersphere/api/controller/APITestController.java b/backend/src/main/java/io/metersphere/api/controller/APITestController.java index d43ea2acb9..185abbd176 100644 --- a/backend/src/main/java/io/metersphere/api/controller/APITestController.java +++ b/backend/src/main/java/io/metersphere/api/controller/APITestController.java @@ -12,8 +12,7 @@ import io.metersphere.api.dto.datacount.response.TaskInfoResult; import io.metersphere.api.dto.definition.RunDefinitionRequest; import io.metersphere.api.dto.scenario.request.dubbo.RegistryCenter; import io.metersphere.api.service.*; -import io.metersphere.base.domain.ApiTest; -import io.metersphere.base.domain.Schedule; +import io.metersphere.base.domain.*; import io.metersphere.commons.constants.RoleConstants; import io.metersphere.commons.constants.ScheduleGroup; import io.metersphere.commons.utils.CronUtils; @@ -30,13 +29,15 @@ import org.apache.commons.lang3.StringUtils; import org.apache.jorphan.collections.HashTree; import org.apache.shiro.authz.annotation.Logical; import org.apache.shiro.authz.annotation.RequiresRoles; -import org.python.core.AstList; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import java.text.DecimalFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; import static io.metersphere.commons.utils.JsonPathUtils.getListJson; @@ -251,9 +252,7 @@ public class APITestController { @GetMapping("/testSceneInfoCount/{projectId}") public ApiDataCountDTO testSceneInfoCount(@PathVariable String projectId) { - ApiDataCountDTO apiCountResult = new ApiDataCountDTO(); - long scenarioCountNumber = apiAutomationService.countScenarioByProjectID(projectId); apiCountResult.setAllApiDataCountNumber(scenarioCountNumber); @@ -272,17 +271,46 @@ public class APITestController { //未执行、未通过、已通过 List countResultByRunResult = apiAutomationService.countRunResultByProjectID(projectId); apiCountResult.countRunResult(countResultByRunResult); - long allCount = apiCountResult.getUnexecuteCount() + apiCountResult.getExecutionPassCount() + apiCountResult.getExecutionFailedCount(); - + DecimalFormat df = new DecimalFormat("0.0"); if (allCount != 0) { + //通过率 float coverageRageNumber = (float) apiCountResult.getExecutionPassCount() * 100 / allCount; - DecimalFormat df = new DecimalFormat("0.0"); apiCountResult.setPassRage(df.format(coverageRageNumber) + "%"); } - return apiCountResult; + } + @GetMapping("/countInterfaceCoverage/{projectId}") + public String countInterfaceCoverage(@PathVariable String projectId) { + String returnStr = "100%"; + /** + * 接口覆盖率 + * 复制的接口定义/复制或引用的单接口用例/ 添加的自定义请求 url 路径与现有的接口定义一致的请求 + */ + long startTime1 = System.currentTimeMillis(); + List allScenarioInfoList = apiAutomationService.selectIdAndScenarioByProjectId(projectId); + long startTime2 = System.currentTimeMillis(); + System.out.println("Search data time : " + (startTime2 - startTime1)); + startTime1 = System.currentTimeMillis(); + List allEffectiveApiIdList = apiDefinitionService.selectEffectiveIdByProjectId(projectId); + startTime2 = System.currentTimeMillis(); + System.out.println("Search data time (api info) : " + (startTime2 - startTime1)); + List allEffectiveApiCaseList = apiTestCaseService.selectEffectiveTestCaseByProjectId(projectId); + long startTime3 = System.currentTimeMillis(); + System.out.println("Search data time (case info): " + (startTime3 - startTime2)); + + try { + startTime1 = System.currentTimeMillis(); + float intetfaceCoverageRageNumber = apiAutomationService.countInterfaceCoverage(allScenarioInfoList, allEffectiveApiIdList, allEffectiveApiCaseList); + startTime2 = System.currentTimeMillis(); + System.out.println("Count data time : " + (startTime2 - startTime1)); + DecimalFormat df = new DecimalFormat("0.0"); + returnStr = df.format(intetfaceCoverageRageNumber) + "%"; + }catch (Exception e){ + e.printStackTrace(); + } + return returnStr; } @GetMapping("/scheduleTaskInfoCount/{projectId}") @@ -346,11 +374,11 @@ public class APITestController { @GetMapping("/runningTask/{projectID}/{callFrom}") public List runningTask(@PathVariable String projectID, @PathVariable String callFrom) { List typeFilter = new ArrayList<>(); - if(StringUtils.equals(callFrom, "api_test")) { // 接口测试首页显示的运行中定时任务,只要这3种,不需要 性能测试、api_test(旧版) + if (StringUtils.equals(callFrom, "api_test")) { // 接口测试首页显示的运行中定时任务,只要这3种,不需要 性能测试、api_test(旧版) typeFilter.add(ScheduleGroup.API_SCENARIO_TEST.name()); typeFilter.add(ScheduleGroup.SWAGGER_IMPORT.name()); typeFilter.add(ScheduleGroup.TEST_PLAN_TEST.name()); - } else if(StringUtils.equals(callFrom, "track_home")) { // 测试跟踪首页只显示测试计划的定时任务 + } else if (StringUtils.equals(callFrom, "track_home")) { // 测试跟踪首页只显示测试计划的定时任务 typeFilter.add(ScheduleGroup.TEST_PLAN_TEST.name()); } List resultList = scheduleService.findRunningTaskInfoByProjectID(projectID, typeFilter); @@ -386,7 +414,7 @@ public class APITestController { String testName = runRequest.getName(); //将jmx处理封装为通用方法 - JmxInfoDTO dto = apiTestService.updateJmxString(jmxString,testName,false); + JmxInfoDTO dto = apiTestService.updateJmxString(jmxString, testName, false); dto.setName(runRequest.getName() + ".jmx"); return dto; } diff --git a/backend/src/main/java/io/metersphere/api/dto/datacount/response/ApiDataCountDTO.java b/backend/src/main/java/io/metersphere/api/dto/datacount/response/ApiDataCountDTO.java index 1ed49369c7..088cb896f5 100644 --- a/backend/src/main/java/io/metersphere/api/dto/datacount/response/ApiDataCountDTO.java +++ b/backend/src/main/java/io/metersphere/api/dto/datacount/response/ApiDataCountDTO.java @@ -111,6 +111,11 @@ public class ApiDataCountDTO { */ private String successRage = " 0%"; + /** + * 接口覆盖率 + */ + private String interfaceCoverage = " 0%"; + public ApiDataCountDTO(){} /** diff --git a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java index 33626513da..c4c71db6f1 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiAutomationService.java @@ -1,6 +1,7 @@ package io.metersphere.api.service; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONPath; import com.fasterxml.jackson.core.type.TypeReference; @@ -29,6 +30,7 @@ import io.metersphere.commons.utils.*; import io.metersphere.controller.request.ScheduleRequest; import io.metersphere.i18n.Translator; import io.metersphere.job.sechedule.ApiScenarioTestJob; +import io.metersphere.job.sechedule.SwaggerUrlImportJob; import io.metersphere.job.sechedule.TestPlanTestJob; import io.metersphere.service.ScheduleService; import io.metersphere.track.dto.TestPlanDTO; @@ -230,6 +232,7 @@ public class ApiAutomationService { scenario.setPrincipal(request.getPrincipal()); scenario.setStepTotal(request.getStepTotal()); scenario.setUpdateTime(System.currentTimeMillis()); + scenario.setDescription(request.getDescription()); scenario.setScenarioDefinition(JSON.toJSONString(request.getScenarioDefinition())); if (StringUtils.isNotEmpty(request.getStatus())) { scenario.setStatus(request.getStatus()); @@ -710,6 +713,10 @@ public class ApiAutomationService { return extApiScenarioMapper.countByProjectID(projectId); } + public List selectIdAndScenarioByProjectId(String projectId) { + return extApiScenarioMapper.selectIdAndScenarioByProjectId(projectId); + } + public long countScenarioByProjectIDAndCreatInThisWeek(String projectId) { Map startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date()); Date firstTime = startAndEndDateInWeek.get("firstTime"); @@ -811,7 +818,9 @@ public class ApiAutomationService { if (StringUtils.equals(request.getGroup(), ScheduleGroup.TEST_PLAN_TEST.name())) { scheduleService.addOrUpdateCronJob( request, TestPlanTestJob.getJobKey(request.getResourceId()), TestPlanTestJob.getTriggerKey(request.getResourceId()), TestPlanTestJob.class); - } else { + }else if(StringUtils.equals(request.getGroup(), ScheduleGroup.SWAGGER_IMPORT.name())){ + scheduleService.addOrUpdateCronJob(request, SwaggerUrlImportJob.getJobKey(request.getResourceId()), SwaggerUrlImportJob.getTriggerKey(request.getResourceId()), SwaggerUrlImportJob.class); + } else{ scheduleService.addOrUpdateCronJob( request, ApiScenarioTestJob.getJobKey(request.getResourceId()), ApiScenarioTestJob.getTriggerKey(request.getResourceId()), ApiScenarioTestJob.class); } @@ -1031,4 +1040,131 @@ public class ApiAutomationService { this.deleteBatch(request.getIds()); } + + /** + * 统计接口覆盖率 + * 1.场景中复制的接口 + * 2.场景中引用/复制的案例 + * 3.场景中的自定义路径与接口定义中的匹配 + * + * @param allScenarioInfoList 场景集合(id / scenario大字段 必须有数据) + * @param allEffectiveApiList 接口集合(id / path 必须有数据) + * @param allEffectiveApiCaseList 案例集合(id /api_definition_id 必须有数据) + * @return + */ + public float countInterfaceCoverage(List allScenarioInfoList, List allEffectiveApiList, List allEffectiveApiCaseList) { +// float coverageRageNumber = (float) apiCountResult.getExecutionPassCount() * 100 / allCount; + float intetfaceCoverage = 0; + if (allEffectiveApiList == null || allEffectiveApiList.isEmpty()) { + return 100; + } + + /** + * 前置工作: + * 1。将接口集合转化数据结构: map> urlMap 用来做3的筛选 + * 2。将案例集合转化数据结构:map> caseIdMap 用来做2的筛选 + * 3。将接口集合转化数据结构: List allApiIdList 用来做1的筛选 + * 4。自定义List coveragedIdList 已覆盖的id集合。 最终计算公式是 coveragedIdList/allApiIdList + * + * 解析allScenarioList的scenarioDefinition字段。 + * 1。提取每个步骤的url。 在 urlMap筛选 + * 2。提取每个步骤的id. 在caseIdMap 和 allApiIdList。 + */ + Map> urlMap = new HashMap<>(); + List allApiIdList = new ArrayList<>(); + Map> caseIdMap = new HashMap<>(); + for (ApiDefinition model : allEffectiveApiList) { + String url = model.getPath(); + String id = model.getId(); + allApiIdList.add(id); + if (urlMap.containsKey(url)) { + urlMap.get(url).add(id); + } else { + List list = new ArrayList<>(); + list.add(id); + urlMap.put(url, list); + } + } + for (ApiTestCase model : allEffectiveApiCaseList){ + String caseId = model.getId(); + String apiId = model.getApiDefinitionId(); + if (urlMap.containsKey(caseId)) { + urlMap.get(caseId).add(apiId); + } else { + List list = new ArrayList<>(); + list.add(apiId); + urlMap.put(caseId, list); + } + } + + if (allApiIdList.isEmpty()) { + return 100; + } + + List urlList = new ArrayList<>(); + List idList= new ArrayList<>(); + + for (ApiScenarioWithBLOBs model : allScenarioInfoList) { + String scenarioDefiniton = model.getScenarioDefinition(); + this.addUrlAndIdToList(scenarioDefiniton,urlList,idList); + } + + List containsApiIdList = new ArrayList<>(); + + for (String url : urlList) { + List apiIdList = urlMap.get(url); + if(apiIdList!=null ){ + for (String api : apiIdList) { + if(!containsApiIdList.contains(api)){ + containsApiIdList.add(api); + } + } + } + } + + for (String id : idList) { + List apiIdList = caseIdMap.get(id); + if(apiIdList!=null ){ + for (String api : apiIdList) { + if(!containsApiIdList.contains(api)){ + containsApiIdList.add(api); + } + } + } + + if(allApiIdList.contains(id)){ + if(!containsApiIdList.contains(id)){ + containsApiIdList.add(id); + } + } + } + + float coverageRageNumber = (float) containsApiIdList.size() * 100 / allApiIdList.size(); + return coverageRageNumber; + } + + private void addUrlAndIdToList(String scenarioDefiniton, List urlList, List idList) { + try { + JSONObject scenarioObj = JSONObject.parseObject(scenarioDefiniton); + if(scenarioObj.containsKey("hashTree")){ + JSONArray hashArr = scenarioObj.getJSONArray("hashTree"); + for (int i = 0; i < hashArr.size(); i++) { + JSONObject elementObj = hashArr.getJSONObject(i); + if(elementObj.containsKey("id")){ + String id = elementObj.getString("id"); + idList.add(id); + } + if(elementObj.containsKey("url")){ + String url = elementObj.getString("url"); + urlList.add(url); + } + if(elementObj.containsKey("path")){ + String path = elementObj.getString("path"); + urlList.add(path); + } + } + } + }catch (Exception e){ + } + } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 7884538fcc..eb3847aaf5 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -912,4 +912,8 @@ public class ApiDefinitionService { } return apiExportResult; } + + public List selectEffectiveIdByProjectId(String projectId) { + return extApiDefinitionMapper.selectEffectiveIdByProjectId(projectId); + } } 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 aa7f0724a4..341c68747e 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java @@ -648,4 +648,8 @@ public class ApiTestCaseService { public ApiDefinitionExecResult getInfo(String id){ return apiDefinitionExecResultMapper.selectByPrimaryKey(id); } + + public List selectEffectiveTestCaseByProjectId(String projectId) { + return extApiTestCaseMapper.selectEffectiveTestCaseByProjectId(projectId); + } } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java index 44cf4c6abe..3230662b65 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.java @@ -38,4 +38,6 @@ public interface ExtApiDefinitionMapper { List listRelevance(@Param("request")ApiDefinitionRequest request); List listRelevanceReview(@Param("request")ApiDefinitionRequest request); List selectIds(@Param("request") BaseQueryRequest query); + + List selectEffectiveIdByProjectId(String projectId); } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml index bfaa0afcd0..7067ce9a49 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml @@ -465,6 +465,11 @@ + diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java index 6bd9153db2..d7d397f6bb 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.java @@ -6,7 +6,6 @@ import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.base.domain.ApiScenario; import io.metersphere.base.domain.ApiScenarioExample; import io.metersphere.base.domain.ApiScenarioWithBLOBs; -import io.metersphere.controller.request.BaseQueryRequest; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -28,6 +27,8 @@ public interface ExtApiScenarioMapper { long countByProjectID(String projectId); + List selectIdAndScenarioByProjectId(String projectId); + long countByProjectIDAndCreatInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp); List countRunResultByProjectID(String projectId); diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml index c7db6225fe..db83f076af 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiScenarioMapper.xml @@ -304,6 +304,9 @@ + select name from api_test_case where project_id = #{projectId} + \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java b/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java index ba941aad35..f1d2d639c5 100644 --- a/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java +++ b/backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java @@ -67,6 +67,16 @@ public class TestCaseDataListener extends EasyExcelListener { break; } } + //增加字数校验,每一层不能超过100字 + for (int i = 0; i < nodes.length; i++) { + String nodeStr = nodes[i]; + if(StringUtils.isNotEmpty(nodeStr)){ + if(nodeStr.trim().length()>100){ + stringBuilder.append(Translator.get("module") + Translator.get("test_track.length_less_than") + "100:"+nodeStr); + break; + } + } + } } // if (StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) { @@ -142,7 +152,7 @@ public class TestCaseDataListener extends EasyExcelListener { public void saveData() { //excel中用例都有错误时就返回,只要有用例可用于更新或者插入就不返回 - if (!errList.isEmpty() && list.size() == 0 && updateList.size() == 0) { + if (!errList.isEmpty()) { return; } @@ -231,15 +241,23 @@ public class TestCaseDataListener extends EasyExcelListener { */ public String modifyTagPattern(TestCaseExcelData data){ String tags = data.getTags(); - if (tags != null) { - Stream stringStream = Arrays.stream(tags.split("[,;,;]")); //当标签值以中英文的逗号和分号分隔时才能正确解析 - List tagList = stringStream.map(tag -> tag = "\"" + tag + "\"") - .collect(Collectors.toList()); - String modifiedTags = StringUtils.join(tagList, ","); - modifiedTags = "[" + modifiedTags + "]"; - return modifiedTags; - }else { - return null; + try { + if (StringUtils.isNotBlank(tags)) { + JSONArray.parse(tags); + return tags; + } + return "[]"; + } catch (Exception e) { + if (tags != null) { + Stream stringStream = Arrays.stream(tags.split("[,;,;]")); //当标签值以中英文的逗号和分号分隔时才能正确解析 + List tagList = stringStream.map(tag -> tag = "\"" + tag + "\"") + .collect(Collectors.toList()); + String modifiedTags = StringUtils.join(tagList, ","); + modifiedTags = "[" + modifiedTags + "]"; + return modifiedTags; + } else { + return "[]"; + } } } diff --git a/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java b/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java index 68736be502..3942c92a74 100644 --- a/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java +++ b/backend/src/main/java/io/metersphere/track/controller/TestPlanController.java @@ -91,8 +91,8 @@ public class TestPlanController { @PostMapping("/edit") @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) - public void editTestPlan(@RequestBody TestPlanDTO testPlanDTO) { - testPlanService.editTestPlan(testPlanDTO, true); + public String editTestPlan(@RequestBody TestPlanDTO testPlanDTO) { + return testPlanService.editTestPlan(testPlanDTO, true); } @PostMapping("/edit/status/{planId}") diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 7d9c4047ac..e1da5af7a8 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -176,7 +176,7 @@ public class TestPlanService { return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan()); } - public int editTestPlan(TestPlanDTO testPlan, Boolean isSendMessage) { + public String editTestPlan(TestPlanDTO testPlan, Boolean isSendMessage) { checkTestPlanExist(testPlan); TestPlan res = testPlanMapper.selectByPrimaryKey(testPlan.getId()); // 先查一次库 testPlan.setUpdateTime(System.currentTimeMillis()); @@ -230,7 +230,7 @@ public class TestPlanService { .build(); noticeSendService.send(NoticeConstants.TaskType.TEST_PLAN_TASK, noticeModel); } - return i; + return testPlan.getId(); } //计划内容 diff --git a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue index 6340706b4d..27a4cb1918 100644 --- a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue +++ b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue @@ -243,13 +243,11 @@ } }) } else { - // this.checkFullUrl(data); - // sign = this.isFullUrl; sign = false; } - // 校验是否全是全路径 - + //this.checkFullUrl(data); + //sign = this.isFullUrl; } if (!sign) { diff --git a/frontend/src/business/components/api/automation/scenario/variable/VariableList.vue b/frontend/src/business/components/api/automation/scenario/variable/VariableList.vue index 493fae9fb5..8f23c32cb6 100644 --- a/frontend/src/business/components/api/automation/scenario/variable/VariableList.vue +++ b/frontend/src/business/components/api/automation/scenario/variable/VariableList.vue @@ -226,7 +226,7 @@ this.headers = headers; } this.visible = true; - this.editData = {type: "CONSTANT"}; + this.editData = {type: "CONSTANT", delimiter: ","}; this.addParameters(this.editData); this.disabled = disabled; }, diff --git a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue index 227102ac01..ec53fe028a 100644 --- a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue +++ b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue @@ -27,7 +27,7 @@ + @blur="saveTestCase(apiCase,true)" :placeholder="$t('commons.input_name')" ref="nameEdit"/> {{ apiCase.id ? apiCase.name : '' }} @@ -48,7 +48,7 @@
- +
@@ -291,7 +291,7 @@ } }); }, - saveCase(row) { + saveCase(row,hideAlert) { let tmp = JSON.parse(JSON.stringify(row)); this.isShowInput = false; if (this.validate(tmp)) { @@ -328,16 +328,18 @@ row.createTime = data.createTime; row.updateTime = data.updateTime; if (!row.message) { - this.$success(this.$t('commons.save_success')); - this.$emit('refresh'); + if(!hideAlert){ + this.$success(this.$t('commons.save_success')); + this.$emit('refresh'); + } } }); }, - saveTestCase(row) { + saveTestCase(row,hideAlert) { if (this.api.saved) { this.addModule(row); } else { - this.saveCase(row); + this.saveCase(row,hideAlert); } }, showInput(row) { 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 0f27ac344f..a6db51ab96 100644 --- a/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue +++ b/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue @@ -152,6 +152,7 @@ saveApiAndCase(api) { this.visible = true; this.api = api; + this.currentApi = api; this.addCase(); }, setEnvironment(environment) { diff --git a/frontend/src/business/components/api/definition/components/list/ApiList.vue b/frontend/src/business/components/api/definition/components/list/ApiList.vue index c13957ac04..8844291a23 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiList.vue @@ -248,6 +248,7 @@ import {Api_List} from "@/business/components/common/model/JsonData"; import HeaderCustom from "@/business/components/common/head/HeaderCustom"; import HeaderLabelOperate from "@/business/components/common/head/HeaderLabelOperate"; import {Body} from "@/business/components/api/definition/model/ApiTestModel"; +import {buildNodePath} from "@/business/components/api/definition/model/NodeTree"; export default { @@ -768,13 +769,21 @@ export default { }); }, buildApiPath(apis) { - apis.forEach((api) => { + try { + let options = []; this.moduleOptions.forEach(item => { - if (api.moduleId === item.id) { - api.modulePath = item.path; - } + buildNodePath(item, {path: ''}, options); }); - }); + apis.forEach((api) => { + options.forEach((item) => { + if (api.moduleId === item.id) { + api.modulePath = item.path; + } + }) + }); + } catch (e) { + console.log(e); + } }, sort(column) { // 每次只对一个字段排序 diff --git a/frontend/src/business/components/api/homepage/ApiTestHomePage.vue b/frontend/src/business/components/api/homepage/ApiTestHomePage.vue index dd5236a8e8..1bd4373d58 100644 --- a/frontend/src/business/components/api/homepage/ApiTestHomePage.vue +++ b/frontend/src/business/components/api/homepage/ApiTestHomePage.vue @@ -34,7 +34,7 @@ - + @@ -82,6 +82,7 @@ export default { sceneCountData: {}, testCaseCountData: {}, scheduleTaskCountData: {}, + interfaceCoverage: "waitting...", tipsType: "1", result: {}, } @@ -109,6 +110,10 @@ export default { this.sceneCountData = response.data; }); + this.$get("/api/countInterfaceCoverage/" + selectProjectId, response => { + this.interfaceCoverage = response.data; + }); + this.$get("/api/testCaseInfoCount/" + selectProjectId, response => { this.testCaseCountData = response.data; }); diff --git a/frontend/src/business/components/api/homepage/components/SceneInfoCard.vue b/frontend/src/business/components/api/homepage/components/SceneInfoCard.vue index e048e5c050..130628bdf8 100644 --- a/frontend/src/business/components/api/homepage/components/SceneInfoCard.vue +++ b/frontend/src/business/components/api/homepage/components/SceneInfoCard.vue @@ -52,16 +52,29 @@ - - + + {{$t('api_test.home_page.detail_card.rate.pass')+":"}} - - + + {{sceneCountData.passRage}} + + + {{$t('api_test.home_page.detail_card.rate.interface_coverage')+":"}} + + + + + + + {{interfaceCoverage}} + + + @@ -120,6 +133,7 @@ export default { props:{ sceneCountData:{}, + interfaceCoverage:String, }, methods: { @@ -141,6 +155,17 @@ export default { margin:20px auto; } +.rows-count-number{ + font-family:'ArialMT', 'Arial', sans-serif; + font-size:30px; + color: var(--count_number); + margin:20px auto; +} +.lading-icon{ + font-size: 25px; + color: var(--count_number); + font-weight: bold; +} .main-number-show { width: 100px; height: 100px; diff --git a/frontend/src/business/components/api/test/Upgrade.vue b/frontend/src/business/components/api/test/Upgrade.vue index 0c3b568be8..40f70cbd05 100644 --- a/frontend/src/business/components/api/test/Upgrade.vue +++ b/frontend/src/business/components/api/test/Upgrade.vue @@ -11,7 +11,6 @@ { + this.fileList.push(row); + }) if (this.loadType === 'resource') { - rows.forEach(row => { - this.fileList.push(row); - }) this.$success(this.$t('test_track.case.import.success')); this.close(); return; @@ -197,8 +198,6 @@ export default { tg.options = {}; this.scenarios.push(tg); }); - let file = new File([d.jmx], d.name); - this.uploadList.push(file); }); this.$emit('fileChange', this.scenarios); diff --git a/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue b/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue index d6b91f5552..ff3554b01d 100644 --- a/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue +++ b/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseEdit.vue @@ -711,7 +711,7 @@ export default { .border-hidden >>> .el-textarea__inner { border-style: hidden; background-color: white; - color: #606266; + color: #060505; } .cast_label { @@ -785,4 +785,5 @@ p { height: 550px; overflow: auto; } + diff --git a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue index e2aab0f143..eb03657f6b 100644 --- a/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue +++ b/frontend/src/business/components/track/review/view/components/TestReviewTestCaseEdit.vue @@ -549,4 +549,10 @@ export default { .comment-card >>> .el-card__body { height: calc(100vh - 120px); } + +.tb-edit >>> .el-textarea__inner { + border-style: hidden; + background-color: white; + color: #060505; +} diff --git a/frontend/src/business/components/xpack b/frontend/src/business/components/xpack index 07951ba17a..a37e6bb56f 160000 --- a/frontend/src/business/components/xpack +++ b/frontend/src/business/components/xpack @@ -1 +1 @@ -Subproject commit 07951ba17aef6f29e50cfd68e40de3266f9a60cd +Subproject commit a37e6bb56ffaa7ecc4ee128640e9415304ad41b6 diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 94837b728c..3ffb614514 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -1001,6 +1001,7 @@ export default { coverage: "Coverage rate", pass: "Pass rate", success: "Success rate", + interface_coverage: "Interface coverage", }, }, api_details_card: { diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 251d71ac48..5039195966 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -1005,6 +1005,7 @@ export default { coverage: "覆盖率", pass: "通过率", success: "成功率", + interface_coverage: "接口覆盖率", }, }, api_details_card: { diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index e9f8dddcd4..2a6ca86ab7 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -1003,6 +1003,7 @@ export default { coverage: "覆蓋率", pass: "通過率", success: "成功率", + interface_coverage: "接口覆蓋率", }, }, api_details_card: {