Merge branch 'v1.8' of https://github.com/metersphere/metersphere into v1.8
This commit is contained in:
commit
f768f9aac8
|
@ -139,7 +139,7 @@
|
|||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
<artifactId>springdoc-openapi-ui</artifactId>
|
||||
<version>1.2.32</version>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- jmeter -->
|
||||
|
@ -280,7 +280,7 @@
|
|||
<dependency>
|
||||
<groupId>io.swagger.parser.v3</groupId>
|
||||
<artifactId>swagger-parser</artifactId>
|
||||
<version>2.0.22</version>
|
||||
<version>2.0.24</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ApacheJMeter_core scope 是 runtime,这里去掉scope -->
|
||||
|
|
|
@ -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<ApiDataCountResult> 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<ApiScenarioWithBLOBs> allScenarioInfoList = apiAutomationService.selectIdAndScenarioByProjectId(projectId);
|
||||
long startTime2 = System.currentTimeMillis();
|
||||
System.out.println("Search data time : " + (startTime2 - startTime1));
|
||||
startTime1 = System.currentTimeMillis();
|
||||
List<ApiDefinition> allEffectiveApiIdList = apiDefinitionService.selectEffectiveIdByProjectId(projectId);
|
||||
startTime2 = System.currentTimeMillis();
|
||||
System.out.println("Search data time (api info) : " + (startTime2 - startTime1));
|
||||
List<ApiTestCase> 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<TaskInfoResult> runningTask(@PathVariable String projectID, @PathVariable String callFrom) {
|
||||
List<String> 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<TaskInfoResult> 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;
|
||||
}
|
||||
|
|
|
@ -111,6 +111,11 @@ public class ApiDataCountDTO {
|
|||
*/
|
||||
private String successRage = " 0%";
|
||||
|
||||
/**
|
||||
* 接口覆盖率
|
||||
*/
|
||||
private String interfaceCoverage = " 0%";
|
||||
|
||||
public ApiDataCountDTO(){}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
@ -710,6 +711,10 @@ public class ApiAutomationService {
|
|||
return extApiScenarioMapper.countByProjectID(projectId);
|
||||
}
|
||||
|
||||
public List<ApiScenarioWithBLOBs> selectIdAndScenarioByProjectId(String projectId) {
|
||||
return extApiScenarioMapper.selectIdAndScenarioByProjectId(projectId);
|
||||
}
|
||||
|
||||
public long countScenarioByProjectIDAndCreatInThisWeek(String projectId) {
|
||||
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
|
||||
Date firstTime = startAndEndDateInWeek.get("firstTime");
|
||||
|
@ -1031,4 +1036,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<ApiScenarioWithBLOBs> allScenarioInfoList, List<ApiDefinition> allEffectiveApiList, List<ApiTestCase> allEffectiveApiCaseList) {
|
||||
// float coverageRageNumber = (float) apiCountResult.getExecutionPassCount() * 100 / allCount;
|
||||
float intetfaceCoverage = 0;
|
||||
if (allEffectiveApiList == null || allEffectiveApiList.isEmpty()) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* 前置工作:
|
||||
* 1。将接口集合转化数据结构: map<url,List<id>> urlMap 用来做3的筛选
|
||||
* 2。将案例集合转化数据结构:map<testCase.id,List<testCase.apiId>> caseIdMap 用来做2的筛选
|
||||
* 3。将接口集合转化数据结构: List<id> allApiIdList 用来做1的筛选
|
||||
* 4。自定义List<api.id> coveragedIdList 已覆盖的id集合。 最终计算公式是 coveragedIdList/allApiIdList
|
||||
*
|
||||
* 解析allScenarioList的scenarioDefinition字段。
|
||||
* 1。提取每个步骤的url。 在 urlMap筛选
|
||||
* 2。提取每个步骤的id. 在caseIdMap 和 allApiIdList。
|
||||
*/
|
||||
Map<String, List<String>> urlMap = new HashMap<>();
|
||||
List<String> allApiIdList = new ArrayList<>();
|
||||
Map<String,List<String>> 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<String> 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<String> list = new ArrayList<>();
|
||||
list.add(apiId);
|
||||
urlMap.put(caseId, list);
|
||||
}
|
||||
}
|
||||
|
||||
if (allApiIdList.isEmpty()) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
List<String> urlList = new ArrayList<>();
|
||||
List<String> idList= new ArrayList<>();
|
||||
|
||||
for (ApiScenarioWithBLOBs model : allScenarioInfoList) {
|
||||
String scenarioDefiniton = model.getScenarioDefinition();
|
||||
this.addUrlAndIdToList(scenarioDefiniton,urlList,idList);
|
||||
}
|
||||
|
||||
List<String> containsApiIdList = new ArrayList<>();
|
||||
|
||||
for (String url : urlList) {
|
||||
List<String> apiIdList = urlMap.get(url);
|
||||
if(apiIdList!=null ){
|
||||
for (String api : apiIdList) {
|
||||
if(!containsApiIdList.contains(api)){
|
||||
containsApiIdList.add(api);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (String id : idList) {
|
||||
List<String> 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<String> urlList, List<String> 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){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -912,4 +912,8 @@ public class ApiDefinitionService {
|
|||
}
|
||||
return apiExportResult;
|
||||
}
|
||||
|
||||
public List<ApiDefinition> selectEffectiveIdByProjectId(String projectId) {
|
||||
return extApiDefinitionMapper.selectEffectiveIdByProjectId(projectId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -648,4 +648,8 @@ public class ApiTestCaseService {
|
|||
public ApiDefinitionExecResult getInfo(String id){
|
||||
return apiDefinitionExecResultMapper.selectByPrimaryKey(id);
|
||||
}
|
||||
|
||||
public List<ApiTestCase> selectEffectiveTestCaseByProjectId(String projectId) {
|
||||
return extApiTestCaseMapper.selectEffectiveTestCaseByProjectId(projectId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,4 +38,6 @@ public interface ExtApiDefinitionMapper {
|
|||
List<ApiDefinitionResult> listRelevance(@Param("request")ApiDefinitionRequest request);
|
||||
List<ApiDefinitionResult> listRelevanceReview(@Param("request")ApiDefinitionRequest request);
|
||||
List<String> selectIds(@Param("request") BaseQueryRequest query);
|
||||
|
||||
List<ApiDefinition> selectEffectiveIdByProjectId(String projectId);
|
||||
}
|
|
@ -465,6 +465,11 @@
|
|||
<include refid="queryWhereCondition"/>
|
||||
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
|
||||
</select>
|
||||
<select id="selectEffectiveIdByProjectId" resultType="io.metersphere.base.domain.ApiDefinition">
|
||||
select id,path
|
||||
from api_definition
|
||||
WHERE project_id = #{0} AND status != 'Trash'
|
||||
</select>
|
||||
|
||||
<sql id="queryWhereCondition">
|
||||
<where>
|
||||
|
|
|
@ -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<ApiScenarioWithBLOBs> selectIdAndScenarioByProjectId(String projectId);
|
||||
|
||||
long countByProjectIDAndCreatInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
|
||||
|
||||
List<ApiDataCountResult> countRunResultByProjectID(String projectId);
|
||||
|
|
|
@ -304,6 +304,9 @@
|
|||
<select id="countByProjectID" resultType="java.lang.Long">
|
||||
SELECT COUNT(id) AS countNumber FROM api_scenario WHERE project_id = #{0} AND status != 'Trash'
|
||||
</select>
|
||||
<select id="selectIdAndScenarioByProjectId" resultType="io.metersphere.base.domain.ApiScenarioWithBLOBs">
|
||||
SELECT id,scenario_definition FROM api_scenario WHERE project_id = #{0} AND status != 'Trash'
|
||||
</select>
|
||||
<select id="countByProjectIDAndCreatInThisWeek" resultType="java.lang.Long">
|
||||
SELECT count(id) AS countNumber FROM api_scenario
|
||||
WHERE project_id = #{projectId} AND status != 'Trash'
|
||||
|
|
|
@ -31,4 +31,6 @@ public interface ExtApiTestCaseMapper {
|
|||
ApiTestCase getNextNum(@Param("definitionId") String definitionId);
|
||||
|
||||
ApiTestCaseInfo selectApiCaseInfoByPrimaryKey(String id);
|
||||
|
||||
List<ApiTestCase> selectEffectiveTestCaseByProjectId(String projectId);
|
||||
}
|
|
@ -394,5 +394,8 @@
|
|||
<select id="listPorjectAllCaseName" resultType="java.lang.String">
|
||||
select name from api_test_case where project_id = #{projectId}
|
||||
</select>
|
||||
<select id="selectEffectiveTestCaseByProjectId" resultType="io.metersphere.base.domain.ApiTestCase">
|
||||
select id,api_definition_id from api_test_case where project_id = #{projectId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -34,7 +34,7 @@
|
|||
<ms-test-case-info-card @redirectPage="redirectPage" :test-case-count-data="testCaseCountData"/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<ms-scene-info-card @redirectPage="redirectPage" :scene-count-data="sceneCountData"/>
|
||||
<ms-scene-info-card @redirectPage="redirectPage" :scene-count-data="sceneCountData" :interface-coverage="interfaceCoverage"/>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<ms-schedule-task-info-card :schedule-task-count-data="scheduleTaskCountData"/>
|
||||
|
@ -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;
|
||||
});
|
||||
|
|
|
@ -52,16 +52,29 @@
|
|||
<el-main style="padding: 5px;margin-top: 10px">
|
||||
<el-container>
|
||||
<el-aside width="60%" class="count-number-show" style="margin-bottom: 0px;margin-top: 0px">
|
||||
<el-container>
|
||||
<el-aside width="30%">
|
||||
<el-container style="height: 50px;margin-top: 10px">
|
||||
<el-aside width="50%" style="line-height: 40px;">
|
||||
{{$t('api_test.home_page.detail_card.rate.pass')+":"}}
|
||||
</el-aside>
|
||||
<el-main style="padding: 0px 0px 0px 0px; line-height: 100px; text-align: center;">
|
||||
<span class="count-number">
|
||||
<el-main style="padding: 0px 0px 0px 0px; line-height: 40px; text-align: center;">
|
||||
<span class="rows-count-number">
|
||||
{{sceneCountData.passRage}}
|
||||
</span>
|
||||
</el-main>
|
||||
</el-container>
|
||||
<el-container style="height: 50px;margin-top: 1px">
|
||||
<el-aside width="50%" style="line-height: 40px;">
|
||||
<span>{{$t('api_test.home_page.detail_card.rate.interface_coverage')+":"}}</span>
|
||||
</el-aside>
|
||||
<el-main style="padding: 0px 0px 0px 0px; line-height: 40px; text-align: center;">
|
||||
<span v-if="interfaceCoverage === 'waitting...'">
|
||||
{{interfaceCoverage}}
|
||||
</span>
|
||||
<span v-else class="rows-count-number">
|
||||
{{interfaceCoverage}}
|
||||
</span>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-aside>
|
||||
<el-main style="padding: 5px">
|
||||
<el-card class="no-shadow-card" body-style="padding-left:5px;padding-right:5px">
|
||||
|
@ -120,6 +133,7 @@ export default {
|
|||
|
||||
props:{
|
||||
sceneCountData:{},
|
||||
interfaceCoverage:String,
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -141,6 +155,13 @@ export default {
|
|||
margin:20px auto;
|
||||
}
|
||||
|
||||
.rows-count-number{
|
||||
font-family:'ArialMT', 'Arial', sans-serif;
|
||||
font-size:30px;
|
||||
color: var(--count_number);
|
||||
margin:20px auto;
|
||||
}
|
||||
|
||||
.main-number-show {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
|
|
|
@ -1001,6 +1001,7 @@ export default {
|
|||
coverage: "Coverage rate",
|
||||
pass: "Pass rate",
|
||||
success: "Success rate",
|
||||
interface_coverage: "Interface coverage",
|
||||
},
|
||||
},
|
||||
api_details_card: {
|
||||
|
|
|
@ -1005,6 +1005,7 @@ export default {
|
|||
coverage: "覆盖率",
|
||||
pass: "通过率",
|
||||
success: "成功率",
|
||||
interface_coverage: "接口覆盖率",
|
||||
},
|
||||
},
|
||||
api_details_card: {
|
||||
|
|
|
@ -1003,6 +1003,7 @@ export default {
|
|||
coverage: "覆蓋率",
|
||||
pass: "通過率",
|
||||
success: "成功率",
|
||||
interface_coverage: "接口覆蓋率",
|
||||
},
|
||||
},
|
||||
api_details_card: {
|
||||
|
|
Loading…
Reference in New Issue