fix(接口测试): 接口测试首页-接口数量统计卡片-已覆盖、未覆盖统计增加按照请求类型的详细统计以及跳转

--bug=1020480 --user=宋天阳 【接口测试】github
#20341接口测试首页-未覆盖链接无法根据协议类型准确筛选并显示,很容易造成误解
https://www.tapd.cn/55049933/s/1348871
This commit is contained in:
song-tianyang 2023-03-13 15:42:25 +08:00 committed by fit2-zhao
parent 2effdb1a0c
commit ef8eb44bc6
7 changed files with 427 additions and 101 deletions

View File

@ -1,15 +1,19 @@
package io.metersphere.api.dto.datacount.response;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.base.domain.ApiDefinition;
import io.metersphere.commons.constants.RequestTypeConstants;
import io.metersphere.commons.enums.ApiHomeFilterEnum;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.enums.ApiTestDataStatus;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
/**
* 接口数据统计返回
@ -23,6 +27,19 @@ public class ApiDataCountDTO {
private long rpcCount = 0;
private long sqlCount = 0;
private long createdInWeek = 0;
//不同请求方式的覆盖数
private long httpCovered = 0;
private long tcpCovered = 0;
private long sqlCovered = 0;
private long rpcCovered = 0;
//不同请求方式的未覆盖
private long httpNotCovered = 0;
private long tcpNotCovered = 0;
private long rpcNotCovered = 0;
private long sqlNotCovered = 0;
private long coveredCount = 0;
private long notCoveredCount = 0;
private long runningCount = 0;
@ -49,11 +66,29 @@ public class ApiDataCountDTO {
//通过率
private String passRate = "0%";
/**
* 对Protocol视角对查询结果进行统计
*
* @param countResultList 查询参数
*/
public void countProtocol(Map<String, List<ApiDefinition>> protocalAllApiMap) {
for (Map.Entry<String, List<ApiDefinition>> entry : protocalAllApiMap.entrySet()) {
switch (entry.getKey()) {
case RequestTypeConstants.DUBBO:
this.rpcCount += entry.getValue().size();
break;
case RequestTypeConstants.HTTP:
this.httpCount += entry.getValue().size();
break;
case RequestTypeConstants.SQL:
this.sqlCount += entry.getValue().size();
break;
case RequestTypeConstants.TCP:
this.tcpCount += entry.getValue().size();
break;
default:
break;
}
}
this.total = this.rpcCount + this.httpCount + this.sqlCount + this.tcpCount;
}
public void countProtocol(List<ApiDataCountResult> countResultList) {
for (ApiDataCountResult countResult :
countResultList) {
@ -77,7 +112,6 @@ public class ApiDataCountDTO {
this.total = this.rpcCount + this.httpCount + this.sqlCount + this.tcpCount;
}
/**
* 对Status视角对查询结果进行统计
*
@ -162,4 +196,56 @@ public class ApiDataCountDTO {
}
}
}
/**
* 统计覆盖率相关数据
*
* @param coverageMap 和覆盖率相关的protocol集合
* @param isUnCovered 是否统计的是未覆盖的数据
*/
public void countCovered(Map<String, List<ApiDefinition>> coverageMap, boolean isUnCovered) {
if (MapUtils.isNotEmpty(coverageMap)) {
for (Map.Entry<String, List<ApiDefinition>> entry : coverageMap.entrySet()) {
if (CollectionUtils.isNotEmpty(entry.getValue())) {
switch (entry.getKey()) {
case RequestTypeConstants.DUBBO:
if (isUnCovered) {
this.rpcNotCovered += entry.getValue().size();
} else {
this.rpcCovered += entry.getValue().size();
}
break;
case RequestTypeConstants.HTTP:
if (isUnCovered) {
this.httpNotCovered += entry.getValue().size();
} else {
this.httpCovered += entry.getValue().size();
}
break;
case RequestTypeConstants.SQL:
if (isUnCovered) {
this.sqlNotCovered += entry.getValue().size();
} else {
this.sqlCovered += entry.getValue().size();
}
break;
case RequestTypeConstants.TCP:
if (isUnCovered) {
this.tcpNotCovered += entry.getValue().size();
} else {
this.tcpCovered += entry.getValue().size();
}
break;
default:
break;
}
}
}
}
if (isUnCovered) {
this.notCoveredCount = this.rpcNotCovered + this.httpNotCovered + this.tcpNotCovered + this.sqlNotCovered;
} else {
this.coveredCount = this.rpcCovered + this.httpCovered + this.tcpCovered + this.sqlCovered;
}
}
}

View File

@ -1,7 +1,10 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.ApiComputeResult;
import io.metersphere.api.dto.definition.ApiDefinitionRequest;
import io.metersphere.api.dto.definition.ApiDefinitionResult;
import io.metersphere.api.dto.definition.ApiModuleDTO;
import io.metersphere.base.domain.*;
import io.metersphere.dto.RelationshipGraphData;
import io.metersphere.request.BaseQueryRequest;
@ -25,9 +28,7 @@ public interface ExtApiDefinitionMapper {
int reduction(@Param("ids") List<String> ids);
List<ApiDataCountResult> countProtocolByProjectID(@Param("projectId") String projectId, @Param("versionId") String versionId);
Long countByProjectIDAndCreateInThisWeek(@Param("projectId") String projectId, @Param("versionId") String versionId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
List<ApiDefinition> selectBaseInfoByProjectIDAndVersion(@Param("projectId") String projectId, @Param("versionId") String versionId);
List<ApiDataCountResult> countStateByProjectID(@Param("projectId") String projectId, @Param("versionId") String versionId);
@ -55,7 +56,7 @@ public interface ExtApiDefinitionMapper {
Long getLastOrder(@Param("projectId") String projectId, @Param("baseOrder") Long baseOrder);
long countApiByProjectIdAndHasCase(@Param("projectId") String projectId, @Param("versionId") String versionId);
List<ApiDefinition> selectBaseInfoByProjectIdAndHasCase(@Param("projectId") String projectId, @Param("versionId") String versionId);
List<RelationshipGraphData.Node> getForGraph(@Param("ids") Set<String> ids);

View File

@ -396,8 +396,8 @@
</foreach>
</update>
<select id="countProtocolByProjectID" resultType="io.metersphere.api.dto.datacount.ApiDataCountResult">
SELECT protocol AS groupField, count(DISTINCT ref_id) AS countNumber
<select id="selectBaseInfoByProjectIDAndVersion" resultType="io.metersphere.base.domain.ApiDefinition">
SELECT id,name,method,protocol,path,create_time
FROM api_definition
WHERE project_id = #{projectId}
AND `status` != 'Trash'
@ -407,7 +407,6 @@
<if test="versionId == null">
AND latest = 1
</if>
GROUP BY protocol
</select>
<select id="countStateByProjectID" resultType="io.metersphere.api.dto.datacount.ApiDataCountResult">
SELECT status AS groupField, count(id) AS countNumber
@ -422,19 +421,7 @@
</if>
GROUP BY status
</select>
<select id="countByProjectIDAndCreateInThisWeek" resultType="java.lang.Long">
SELECT count(id) AS countNumber
FROM api_definition
WHERE project_id = #{projectId}
AND create_time BETWEEN #{firstDayTimestamp} AND #{lastDayTimestamp}
AND `status` != 'Trash'
<if test="versionId != null">
AND version_id = #{versionId}
</if>
<if test="versionId == null">
AND latest = 1
</if>
</select>
<select id="countApiCoverageByProjectID" resultType="io.metersphere.api.dto.datacount.ApiDataCountResult">
SELECT count(api.id) AS countNumber,
if(test_case_api.api_definition_id is null, "unCovered", "covered") AS groupField
@ -681,6 +668,23 @@
</if>
</select>
<select id="selectBaseInfoByProjectIdAndHasCase" resultType="io.metersphere.base.domain.ApiDefinition">
select id, path, method, protocol
FROM api_definition
WHERE project_id = #{projectId}
AND `status` != 'Trash'
AND
id IN (
SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash'
)
<if test="versionId != null">
AND version_id = #{versionId}
</if>
<if test="versionId == null">
AND latest = 1
</if>
</select>
<select id="selectEffectiveIdByProjectIdAndHaveNotCase" resultType="io.metersphere.base.domain.ApiDefinition">
select id, path, method, protocol
from api_definition
@ -1008,22 +1012,6 @@
order by `order` desc limit 1;
</select>
<select id="countApiByProjectIdAndHasCase" resultType="java.lang.Long">
SELECT COUNT(id)
FROM api_definition
WHERE project_id = #{projectId}
AND `status` != 'Trash'
AND
id IN (
SELECT api_definition_id FROM api_test_case WHERE `status` is null or `status` != 'Trash'
)
<if test="versionId != null">
AND version_id = #{versionId}
</if>
<if test="versionId == null">
AND latest = 1
</if>
</select>
<select id="getForGraph" resultType="io.metersphere.dto.RelationshipGraphData$Node">
select id,num,`name`
from api_definition

View File

@ -57,10 +57,12 @@ public class ApiHomeController {
public ApiDataCountDTO apiCount(@PathVariable String projectId, @PathVariable String versionId) {
versionId = this.initializationVersionId(versionId);
ApiDataCountDTO apiCountResult = new ApiDataCountDTO();
List<ApiDataCountResult> countResultByProtocolList = apiDefinitionService.countProtocolByProjectID(projectId, versionId);
apiCountResult.countProtocol(countResultByProtocolList);
long dateCountByCreateInThisWeek = apiDefinitionService.countByProjectIDAndCreateInThisWeek(projectId, versionId);
apiCountResult.setCreatedInWeek(dateCountByCreateInThisWeek);
//所有有效接口用于计算不同请求的接口数量本周创建覆盖率
Map<String, List<ApiDefinition>> protocolAllDefinitionMap = apiDefinitionService.countEffectiveByProjectId(projectId, versionId);
apiCountResult.countProtocol(protocolAllDefinitionMap);
//本周创建
apiCountResult.setCreatedInWeek(apiDefinitionService.getApiByCreateInThisWeek(protocolAllDefinitionMap).size());
//查询完成率进行中已完成
List<ApiDataCountResult> countResultByStateList = apiDefinitionService.countStateByProjectID(projectId, versionId);
apiCountResult.countStatus(countResultByStateList);
@ -70,29 +72,33 @@ public class ApiHomeController {
DecimalFormat df = new DecimalFormat("0.0");
apiCountResult.setCompletedRate(df.format(completeRateNumber) + "%");
}
//统计覆盖率
long effectiveApiCount = apiDefinitionService.countEffectiveByProjectId(projectId, versionId);
long apiHasCase = apiDefinitionService.countApiByProjectIdAndHasCase(projectId, versionId);
List<ApiDefinition> apiNoCaseList = apiDefinitionService.selectEffectiveIdByProjectIdAndHaveNotCase(projectId, versionId);
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, null);
int apiInScenario = apiAutomationService.getApiIdInScenario(projectId, scenarioUrlList, apiNoCaseList).size();
if (effectiveApiCount == 0) {
if (apiCountResult.getTotal() == 0) {
//没有任何接口数据
apiCountResult.setCoveredCount(0);
apiCountResult.setNotCoveredCount(0);
} else {
long quotedApiCount = apiHasCase + apiInScenario;
apiCountResult.setCoveredCount(quotedApiCount);
apiCountResult.setNotCoveredCount(effectiveApiCount - quotedApiCount);
//统计覆盖率. 覆盖接口下挂有用例/接口路径被场景引用
//带有用例的接口
List<ApiDefinition> apiDefinitionHasCase = apiDefinitionService.selectBaseInfoByProjectIdAndHasCase(projectId, versionId);
//没有case的接口
List<ApiDefinition> apiNoCaseList = apiDefinitionService.getAPiNotInCollection(protocolAllDefinitionMap, apiDefinitionHasCase);
Map<String, Map<String, String>> scenarioUrlList = apiAutomationService.selectScenarioUseUrlByProjectId(projectId, null);
List<String> apiIdInScenario = apiAutomationService.getApiIdInScenario(projectId, scenarioUrlList, apiNoCaseList);
Map<String, List<ApiDefinition>> unCoverageApiMap = apiDefinitionService.getUnCoverageApiMap(apiNoCaseList, apiIdInScenario);
Map<String, List<ApiDefinition>> coverageApiMap = apiDefinitionService.filterMap(protocolAllDefinitionMap, unCoverageApiMap);
apiCountResult.countCovered(coverageApiMap, false);
apiCountResult.countCovered(unCoverageApiMap, true);
try {
float coveredRateNumber = (float) quotedApiCount * 100 / effectiveApiCount;
float coveredRateNumber = (float) apiCountResult.getCoveredCount() * 100 / apiCountResult.getTotal();
DecimalFormat df = new DecimalFormat("0.0");
apiCountResult.setApiCoveredRate(df.format(coveredRateNumber) + "%");
} catch (Exception e) {
LogUtil.error("转化通过率失败:[" + quotedApiCount + "" + effectiveApiCount + "]", e);
LogUtil.error("转化通过率失败:[" + apiCountResult.getCoveredCount() + "" + apiCountResult.getTotal() + "]", e);
}
}
return apiCountResult;
}

View File

@ -1164,32 +1164,29 @@ public class ApiDefinitionService {
apiTestCaseService.relevanceByApiByReview(request);
}
/**
* 数据统计-接口类型
*
* @param projectId 项目ID
* @return List
*/
public List<ApiDataCountResult> countProtocolByProjectID(String projectId, String versionId) {
return extApiDefinitionMapper.countProtocolByProjectID(projectId, versionId);
}
/**
* 统计本周创建的数据总量
*
* @param projectId
* @return
*/
public long countByProjectIDAndCreateInThisWeek(String projectId, String versionId) {
public List<ApiDefinition> getApiByCreateInThisWeek(Map<String, List<ApiDefinition>> protocalAllApiList) {
Map<String, Date> startAndEndDateInWeek = DateUtils.getWeedFirstTimeAndLastTime(new Date());
Date firstTime = startAndEndDateInWeek.get("firstTime");
Date lastTime = startAndEndDateInWeek.get("lastTime");
if (firstTime == null || lastTime == null) {
return 0;
return new ArrayList<>();
} else {
return extApiDefinitionMapper.countByProjectIDAndCreateInThisWeek(projectId, versionId, firstTime.getTime(), lastTime.getTime());
List<ApiDefinition> apiCreatedInWeekList = new ArrayList<>();
for (List<ApiDefinition> apiList : protocalAllApiList.values()) {
for (ApiDefinition api : apiList) {
if (api.getCreateTime() >= firstTime.getTime() && api.getCreateTime() <= lastTime.getTime()) {
apiCreatedInWeekList.add(api);
}
}
}
return apiCreatedInWeekList;
}
}
@ -1668,24 +1665,17 @@ public class ApiDefinitionService {
}
public long countEffectiveByProjectId(String projectId, String versionId) {
public Map<String, List<ApiDefinition>> countEffectiveByProjectId(String projectId, String versionId) {
if (StringUtils.isEmpty(projectId)) {
return 0;
return new HashMap<>();
} else {
ApiDefinitionExample example = new ApiDefinitionExample();
ApiDefinitionExample.Criteria criteria = example.createCriteria();
criteria.andProjectIdEqualTo(projectId).andStatusNotEqualTo("Trash");
if (StringUtils.isNotBlank(versionId)) {
criteria.andVersionIdEqualTo(versionId);
} else {
criteria.andLatestEqualTo(true);
}
return apiDefinitionMapper.countByExample(example);
List<ApiDefinition> apiDefinitionList = extApiDefinitionMapper.selectBaseInfoByProjectIDAndVersion(projectId, versionId);
return apiDefinitionList.stream().collect(Collectors.groupingBy(ApiDefinition::getProtocol));
}
}
public long countApiByProjectIdAndHasCase(String projectId, String versionId) {
return extApiDefinitionMapper.countApiByProjectIdAndHasCase(projectId, versionId);
public List<ApiDefinition> selectBaseInfoByProjectIdAndHasCase(String projectId, String versionId) {
return extApiDefinitionMapper.selectBaseInfoByProjectIdAndHasCase(projectId, versionId);
}
public int getRelationshipCount(String id) {
@ -2401,4 +2391,61 @@ public class ApiDefinitionService {
updateCaseList.forEach(apiTestCaseBatchMapper::updateByPrimaryKeyWithBLOBs);
}
}
public List<ApiDefinition> getAPiNotInCollection(Map<String, List<ApiDefinition>> protocolAllDefinitionMap, List<ApiDefinition> checkCollection) {
List<ApiDefinition> returnList = new ArrayList<>();
if (MapUtils.isEmpty(protocolAllDefinitionMap)) {
return new ArrayList<>();
}
List<String> idInCheckCollection = new ArrayList<>();
if (CollectionUtils.isNotEmpty(checkCollection)) {
idInCheckCollection = checkCollection.stream().map(ApiDefinition::getId).collect(Collectors.toList());
}
for (List<ApiDefinition> apiList : protocolAllDefinitionMap.values()) {
for (ApiDefinition api : apiList) {
if (!idInCheckCollection.contains(api.getId())) {
returnList.add(api);
}
}
}
return returnList;
}
public Map<String, List<ApiDefinition>> getUnCoverageApiMap(List<ApiDefinition> apiNoCaseList, List<String> apiIdInScenario) {
Map<String, List<ApiDefinition>> returnMap = new HashMap<>();
if (CollectionUtils.isNotEmpty(apiNoCaseList)) {
if (apiIdInScenario == null) {
apiIdInScenario = new ArrayList<>();
}
for (ApiDefinition api : apiNoCaseList) {
if (!apiIdInScenario.contains(api.getId())) {
if (returnMap.containsKey(api.getProtocol())) {
returnMap.get(api.getProtocol()).add(api);
} else {
returnMap.put(api.getProtocol(), new ArrayList<>() {{
this.add(api);
}});
}
}
}
}
return returnMap;
}
public Map<String, List<ApiDefinition>> filterMap(Map<String, List<ApiDefinition>> targetMap, Map<String, List<ApiDefinition>> filter) {
Map<String, List<ApiDefinition>> returnMap = new HashMap<>();
if (MapUtils.isNotEmpty(targetMap)) {
for (Map.Entry<String, List<ApiDefinition>> entry : targetMap.entrySet()) {
List<ApiDefinition> filterList = filter.get(entry.getKey());
List<ApiDefinition> resultList = null;
if (CollectionUtils.isNotEmpty(filterList)) {
resultList = new ArrayList<>(CollectionUtils.subtract(entry.getValue(), filterList));
} else {
resultList = entry.getValue();
}
returnMap.put(entry.getKey(), resultList);
}
}
return returnMap;
}
}

View File

@ -15,7 +15,10 @@
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<api-dashboard @redirectPage="redirectPage" ref="apiDashboard" />
<api-dashboard
@redirectPage="redirectPage"
@redirectPageWithDataType="redirectPageWithDataType"
ref="apiDashboard" />
</el-col>
<el-col :span="12">
<api-case-dashboard @redirectPage="redirectPage" ref="apiCaseDashboard" />
@ -130,6 +133,7 @@ export default {
});
}
},
/**
*
* @param redirectPage 要跳转的页面
@ -189,6 +193,34 @@ export default {
window.open(home.href, '_blank');
}
},
redirectPageWithDataType(redirectPage, dataType, selectRange, selectParam, type) {
let uuid = getUUID();
let home;
let selectVersionId = this.versionId;
if (!selectVersionId || selectVersionId === '') {
selectVersionId = 'default';
}
switch (redirectPage) {
case 'api':
home = this.$router.resolve({
name: 'ApiDefinitionWithQuery',
params: {
versionId: selectVersionId,
redirectID: uuid,
dataType: dataType,
dataSelectRange: selectRange,
projectId: this.projectId,
type: type,
},
});
break;
}
if (home) {
window.open(home.href, '_blank');
}
},
},
};
</script>

View File

@ -38,12 +38,87 @@
{{ $t('home.dashboard.public.covered') }}
</span>
<div class="common-amount">
<el-link
class="addition-info-num"
v-permission-disable="['PROJECT_API_DEFINITION:READ']"
@click="redirectPage('api', 'api', 'covered', null)">
{{ formatAmount(apiData.coveredCount) }}
</el-link>
<el-popover placement="top-start" width="200" trigger="hover">
<div>
<el-row class="addition-info-title">
<el-col>
<div
style="
width: 8px;
height: 8px;
background-color: #aa4fbf;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">HTTP:</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'covered', null, 'HTTP')"
>{{ apiData.httpCovered }}</el-link
>
</div>
</el-col>
</el-row>
<el-row class="addition-info-title">
<div
style="
width: 8px;
height: 8px;
background-color: #fad355;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">RPC :</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'covered', null, 'DUBBO')"
>{{ apiData.rpcCovered }}</el-link
>
</div>
</el-row>
<el-row class="addition-info-title">
<div
style="
width: 8px;
height: 8px;
background-color: #14e1c6;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">TCP :</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'covered', null, 'TCP')"
>{{ apiData.tcpCovered }}
</el-link>
</div>
</el-row>
<el-row class="addition-info-title">
<div
style="
width: 8px;
height: 8px;
background-color: #4e83fd;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">SQL :</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'covered', null, 'SQL')"
>{{ apiData.sqlCovered }}</el-link
>
</div>
</el-row>
</div>
<el-link slot="reference" class="addition-info-num">
{{ formatAmount(apiData.coveredCount) }}
</el-link>
</el-popover>
</div>
</el-col>
<el-col :span="12">
@ -51,12 +126,85 @@
{{ $t('home.dashboard.public.not_covered') }}
</span>
<div class="common-amount">
<el-link
class="addition-info-num"
v-permission-disable="['PROJECT_API_DEFINITION:READ']"
@click="redirectPage('api', 'api', 'notCovered', null)">
{{ formatAmount(apiData.notCoveredCount) }}
</el-link>
<el-popover placement="top-start" width="200" trigger="hover">
<div>
<el-row class="addition-info-title">
<div
style="
width: 8px;
height: 8px;
background-color: #aa4fbf;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">HTTP:</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'notCovered', null, 'HTTP')"
>{{ apiData.httpNotCovered }}</el-link
>
</div>
</el-row>
<el-row class="addition-info-title">
<div
style="
width: 8px;
height: 8px;
background-color: #fad355;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">RPC :</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'notCovered', null, 'DUBBO')"
>{{ apiData.rpcNotCovered }}</el-link
>
</div>
</el-row>
<el-row class="addition-info-title">
<div
style="
width: 8px;
height: 8px;
background-color: #14e1c6;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">TCP :</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'notCovered', null, 'TCP')"
>{{ apiData.tcpNotCovered }}</el-link
>
</div>
</el-row>
<el-row class="addition-info-title">
<div
style="
width: 8px;
height: 8px;
background-color: #4e83fd;
margin: 7px;
float: left;
" />
<span style="line-height: 22px">SQL :</span>
<div style="float: right">
<el-link
class="coverage-num-link"
@click="redirectPageWithDataType('api', 'api', 'notCovered', null, 'SQL')"
>{{ apiData.sqlNotCovered }}</el-link
>
</div>
</el-row>
</div>
<el-link slot="reference" class="addition-info-num">
{{ formatAmount(apiData.notCoveredCount) }}
</el-link>
</el-popover>
</div>
</el-col>
</el-row>
@ -155,6 +303,14 @@ export default {
runningCount: 0,
finishedCount: 0,
notRunCount: 0,
httpCovered: 0,
rpcCovered: 0,
tcpCovered: 0,
sqlCovered: 0,
httpNotCovered: 0,
rpcNotCovered: 0,
tcpNotCovered: 0,
sqlNotCovered: 0,
},
};
},
@ -183,8 +339,18 @@ export default {
redirectPage(redirectPage, dataType, selectRange, selectParam) {
this.$emit('redirectPage', redirectPage, dataType, selectRange, selectParam);
},
redirectPageWithDataType(redirectPage, dataType, selectRange, selectParam, type) {
this.$emit('redirectPageWithDataType', redirectPage, dataType, selectRange, selectParam, type);
},
},
};
</script>
<style scoped></style>
<style scoped>
.coverage-num-link {
line-height: 22px;
color: #783887 !important;
font-size: 14px;
font-weight: 500;
}
</style>