fix(测试跟踪): 修复测试计划关联场景时全选场景,运行环境中项目显示不全的问题

--bug=1024678 --user=宋天阳
[测试跟踪]github#22858测试计划中添加场景用例,全选所有场景(不止一页数据),非当前页的某个场景含多个项目运行环境时,在测试计划中选择环境时无法选择多个项目环境,导致执行测试计划时一直running
https://www.tapd.cn/55049933/s/1353537
This commit is contained in:
song-tianyang 2023-03-21 11:33:01 +08:00 committed by 建国
parent e891f31070
commit c1c904e183
8 changed files with 138 additions and 27 deletions

View File

@ -85,4 +85,6 @@ public interface ExtApiScenarioMapper {
List<String> selectScenarioIdInExecutionInfoByProjectIdIsNull();
long countSourceIdByProjectIdIsNull();
List<String> selectIdByScenarioRequest(@Param("request") ApiScenarioRequest request);
}

View File

@ -1035,5 +1035,16 @@
FROM scenario_execution_info
WHERE project_id IS NULL
</select>
<select id="selectIdByScenarioRequest" resultType="java.lang.String">
select DISTINCT api_scenario.id
from api_scenario
inner join project on api_scenario.project_id = project.id
left join user on api_scenario.create_user = user.id
left join user deleteUser on api_scenario.delete_user_id = deleteUser.id
left join user u2 on api_scenario.principal = u2.id
inner join project_version on api_scenario.project_id = project_version.project_id AND project_version.id =
api_scenario.version_id
<include refid="queryWhereCondition"/>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
</mapper>

View File

@ -16,6 +16,34 @@ public class BatchProcessingUtil {
private static final int BATCH_PROCESS_QUANTITY = 1000;
public static List<String> getProjectIdsByScenarioIdList(List<String> scenarioIdList, Function<List<String>, List<String>> func) {
List<String> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(scenarioIdList)) {
int unProcessingCount = scenarioIdList.size();
while (scenarioIdList.size() > BATCH_PROCESS_QUANTITY) {
List<String> processingList = new ArrayList<>();
for (int i = 0; i < BATCH_PROCESS_QUANTITY; i++) {
processingList.add(scenarioIdList.get(i));
}
//函数处理
returnList.addAll(func.apply(processingList));
scenarioIdList.removeAll(processingList);
//如果剩余数量没有发生变化则跳出循环防止出现死循环的情况
if (scenarioIdList.size() == unProcessingCount) {
break;
} else {
unProcessingCount = scenarioIdList.size();
}
}
if (CollectionUtils.isNotEmpty(scenarioIdList)) {
//剩余待处理数据进行处理
returnList.addAll(func.apply(scenarioIdList));
}
}
return returnList;
}
public static List<ApiScenarioReportResultWithBLOBs> selectScenarioReportResultByScenarioReportId(List<String> scenarioReportId, Function<List<String>, List<ApiScenarioReportResultWithBLOBs>> func) {
List<ApiScenarioReportResultWithBLOBs> returnList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(scenarioReportId)) {

View File

@ -78,6 +78,11 @@ public class TestPlanScenarioCaseController {
return testPlanScenarioCaseService.relevanceList(request, goPage, pageSize);
}
@PostMapping("/relevance/projectIds")
public List<String> relevanceProjectIds(@RequestBody ApiScenarioRequest request) {
return testPlanScenarioCaseService.relevanceProjectIds(request);
}
@PostMapping("/relevance")
@MsAuditLog(module = OperLogModule.TRACK_TEST_PLAN, type = OperLogConstants.ASSOCIATE_CASE, content = "#msClass.getLogDetails(#request)", msClass = ApiScenarioService.class)
public void testPlanRelevance(@RequestBody ApiCaseRelevanceRequest request) {

View File

@ -178,6 +178,14 @@ public class TestPlanScenarioCaseService {
return PageUtils.setPageInfo(page, apiAutomationService.list(request));
}
public List<String> relevanceProjectIds(ApiScenarioRequest request) {
request.setNotInTestPlan(true);
if (request.getAllowedRepeatCase()) {
request.setNotInTestPlan(false);
}
return apiAutomationService.projectIdInlist(request);
}
public void relevance(ApiCaseRelevanceRequest request) {
if (request.getAllowedRepeatCase()) {
request.getCondition().setNotInTestPlan(false);

View File

@ -2308,4 +2308,37 @@ public class ApiScenarioService {
return scenarioList.get(0);
}
}
public List<String> projectIdInlist(ApiScenarioRequest request) {
request = this.initRequest(request, true, true);
List<String> scenarioIdList = extApiScenarioMapper.selectIdByScenarioRequest(request);
if (CollectionUtils.isNotEmpty(request.getUnSelectIds())) {
scenarioIdList.removeAll(request.getUnSelectIds());
}
List<String> projectIdList = BatchProcessingUtil.getProjectIdsByScenarioIdList(scenarioIdList, this::getProjectIdsByScenarioIdList);
return projectIdList;
}
public List<String> getProjectIdsByScenarioIdList(List<String> scenarioIdList) {
List<String> projectIdList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(scenarioIdList)) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(scenarioIdList);
List<ApiScenarioWithBLOBs> scenarioWithBLOBsList = apiScenarioMapper.selectByExampleWithBLOBs(example);
for (ApiScenarioWithBLOBs scenario : scenarioWithBLOBsList) {
ScenarioEnv scenarioEnv = apiScenarioEnvService.getApiScenarioEnv(scenario.getScenarioDefinition());
if (CollectionUtils.isNotEmpty(scenarioEnv.getProjectIds())) {
scenarioEnv.getProjectIds().forEach(projectId -> {
if (!projectIdList.contains(projectId)) {
projectIdList.add(projectId);
}
});
}
}
}
return projectIdList;
}
}

View File

@ -1,39 +1,43 @@
import {post, get} from "@/business/utils/sdk-utils";
import {buildListPagePath, buildPagePath} from "@/api/base-network";
import { get, post } from "@/business/utils/sdk-utils";
import { buildListPagePath, buildPagePath } from "@/api/base-network";
const BASE_URL = '/test/plan/scenario/case/';
const BASE_URL = "/test/plan/scenario/case/";
export function testPlanScenarioList(page, param) {
return post(BASE_URL + buildListPagePath(page), param);
}
export function scenarioRelevanceList(page, param) {
page.path = 'relevance/list';
page.path = "relevance/list";
return post(BASE_URL + buildPagePath(page), param);
}
export function scenarioRelevanceProjectIds(param) {
return post(BASE_URL + "relevance/projectIds", param);
}
export function scenarioRelevance(param) {
return post(BASE_URL + 'relevance', param);
return post(BASE_URL + "relevance", param);
}
export function testPlanScenarioEnv(param) {
return post(BASE_URL + 'env', param);
return post(BASE_URL + "env", param);
}
export function testPlanScenarioCaseBatchUpdateEnv(param) {
return post(BASE_URL + 'batch/update/env', param);
return post(BASE_URL + "batch/update/env", param);
}
export function testPlanScenarioCaseRun(param) {
return post(BASE_URL + 'run', param);
return post(BASE_URL + "run", param);
}
export function testPlanScenarioCaseBatchDelete(param) {
return post(BASE_URL + 'batch/delete', param);
return post(BASE_URL + "batch/delete", param);
}
export function testPlanScenarioCaseSelectAllTableRows(param) {
return post(BASE_URL + 'select-rows', param);
return post(BASE_URL + "select-rows", param);
}
export function testPlanScenarioCaseDelete(id) {

View File

@ -67,7 +67,7 @@
prop="name"
:label="$t('api_test.automation.scenario_name')"
sortable="custom"
min-width="100px"
min-width="120px"
show-overflow-tooltip
/>
<el-table-column
@ -86,7 +86,7 @@
prop="level"
:label="$t('api_test.automation.case_level')"
sortable="custom"
min-width="100px"
min-width="120px"
show-overflow-tooltip
>
<template v-slot:default="scope">
@ -108,9 +108,12 @@
:key="index"
type="success"
effect="plain"
:show-tooltip="scope.row.tags.length === 1 && itemName.length * 12 <= 100"
:show-tooltip="
scope.row.tags.length === 1 && itemName.length * 12 <= 100
"
:content="itemName"
style="margin-left: 0px; margin-right: 2px" />
style="margin-left: 0px; margin-right: 2px"
/>
</div>
</el-tooltip>
</template>
@ -141,6 +144,7 @@
prop="lastResult"
:label="$t('api_test.automation.last_result')"
sortable="custom"
min-width="120px"
>
<template v-slot:default="{ row }">
<ms-api-report-status :status="row.lastResult" />
@ -180,7 +184,10 @@ import {
import MxVersionSelect from "metersphere-frontend/src/components/version/MxVersionSelect";
import { getProjectApplicationConfig } from "@/api/project-application";
import { getApiScenarioEnvByProjectId } from "@/api/remote/api/api-automation";
import { scenarioRelevanceList } from "@/api/remote/plan/test-plan-scenario";
import {
scenarioRelevanceList,
scenarioRelevanceProjectIds,
} from "@/api/remote/plan/test-plan-scenario";
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
import ApiReportStatus from "@/business/plan/view/comonents/report/detail/api/ApiReportStatus";
import MsApiReportStatus from "@/business/plan/view/comonents/report/detail/api/ApiReportStatus";
@ -214,7 +221,7 @@ export default {
},
data() {
return {
result: {},
result: { loading: false },
showConfigButtonWithOutPermission: false,
condition: {
components: TEST_PLAN_RELEVANCE_API_SCENARIO_CONFIGS,
@ -236,7 +243,7 @@ export default {
environmentType: ENV_TYPE.JSON,
envGroupId: "",
versionFilters: [],
pageRefresh: false
pageRefresh: false,
};
},
computed: {
@ -343,13 +350,26 @@ export default {
initProjectIds() {
this.projectIds.clear();
this.map.clear();
this.selectRows.forEach((row) => {
getApiScenarioEnvByProjectId(row.id).then((res) => {
let data = res.data;
data.projectIds.forEach((d) => this.projectIds.add(d));
this.map.set(row.id, data.projectIds);
if (this.condition && this.condition.selectAll) {
this.result.loading = true;
scenarioRelevanceProjectIds(this.condition)
.then((rsp) => {
this.result.loading = false;
let projectIds = rsp.data;
projectIds.forEach((d) => this.projectIds.add(d));
})
.catch(() => {
this.result.loading = false;
});
} else {
this.selectRows.forEach((row) => {
getApiScenarioEnvByProjectId(row.id).then((res) => {
let data = res.data;
data.projectIds.forEach((d) => this.projectIds.add(d));
this.map.set(row.id, data.projectIds);
});
});
});
}
},
checkEnv() {
return this.$refs.envPopover.checkEnv();
@ -374,13 +394,13 @@ export default {
},
getTagToolTips(tags) {
try {
let showTips = '';
let showTips = "";
tags.forEach((item) => {
showTips += item + ',';
showTips += item + ",";
});
return showTips.substr(0, showTips.length - 1);
} catch (e) {
return '';
return "";
}
},
},