feat(测试计划): UI支持环境运行

--story=1010692 --user=张大海 【UI测试】支持与环境管理打通 https://www.tapd.cn/55049933/s/1315234
This commit is contained in:
zhangdahai112 2022-12-17 03:46:19 +08:00 committed by zhangdahai112
parent 579b2b5d56
commit 00b1297f24
9 changed files with 155 additions and 19 deletions

View File

@ -65,8 +65,6 @@ import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
@ -1550,6 +1548,10 @@ public class TestPlanService {
envMap = planTestPlanApiCaseService.getApiCaseEnv(planId); envMap = planTestPlanApiCaseService.getApiCaseEnv(planId);
Map<String, List<String>> scenarioEnv = planTestPlanScenarioCaseService.getApiScenarioEnv(planId); Map<String, List<String>> scenarioEnv = planTestPlanScenarioCaseService.getApiScenarioEnv(planId);
if (DiscoveryUtil.hasService(MicroServiceName.UI_TEST)) {
scenarioEnv = mergeUiScenarioEnv(planId, scenarioEnv);
}
Set<String> projectIds = scenarioEnv.keySet(); Set<String> projectIds = scenarioEnv.keySet();
for (String projectId : projectIds) { for (String projectId : projectIds) {
if (envMap.containsKey(projectId)) { if (envMap.containsKey(projectId)) {
@ -1572,6 +1574,32 @@ public class TestPlanService {
return envMap; return envMap;
} }
/**
* 合并ui场景的环境信息
* @param planId
* @param scenarioEnv
* @return
*/
private Map<String, List<String>> mergeUiScenarioEnv(String planId, Map<String, List<String>> scenarioEnv) {
Map<String, List<String>> uiScenarioEnv = planTestPlanUiScenarioCaseService.getUiScenarioEnv(planId);
if (MapUtils.isEmpty(scenarioEnv)) {
return uiScenarioEnv;
}
if (MapUtils.isNotEmpty(uiScenarioEnv)) {
uiScenarioEnv.entrySet().forEach(entry -> {
if (scenarioEnv.containsKey(entry.getKey())) {
List<String> environmentIds = scenarioEnv.get(entry.getKey());
entry.getValue().forEach(eId -> {
if (!environmentIds.contains(eId)) {
environmentIds.add(eId);
}
});
}
});
}
return scenarioEnv;
}
public String runPlan(TestPlanRunRequest testplanRunRequest) { public String runPlan(TestPlanRunRequest testplanRunRequest) {
//检查测试计划下有没有可以执行的用例 //检查测试计划下有没有可以执行的用例
if (!haveExecCase(testplanRunRequest.getTestPlanId(), false)) { if (!haveExecCase(testplanRunRequest.getTestPlanId(), false)) {

View File

@ -153,4 +153,8 @@ public class PlanTestPlanUiScenarioCaseService extends UiTestService {
public List<UiScenarioReportWithBLOBs> selectExtForPlanReport(String planId) { public List<UiScenarioReportWithBLOBs> selectExtForPlanReport(String planId) {
return microService.getForDataArray(serviceName, BASE_URL + "/get/report/ext/" + planId, UiScenarioReportWithBLOBs.class); return microService.getForDataArray(serviceName, BASE_URL + "/get/report/ext/" + planId, UiScenarioReportWithBLOBs.class);
} }
public Map<String, List<String>> getUiScenarioEnv(String planId) {
return microService.getForData(serviceName, BASE_URL + "/get/env/" + planId, Map.class);
}
} }

View File

@ -0,0 +1,15 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
const BASE_URL = "/ui/automation/";
export function getUiScenarioEnvByProjectId(id) {
return get(BASE_URL + `env-project-ids/${id}`);
}
export function uiAutomationReduction(param) {
return post(BASE_URL + 'reduction', param);
}
export function uiScenarioEnvMap(params) {
return post(BASE_URL + 'env/map', params);
}

View File

@ -201,7 +201,7 @@ export default {
// //
temp.selectEnv = envs.filter(e => e.id === envId).length === 0 ? null : envId; temp.selectEnv = envs.filter(e => e.id === envId).length === 0 ? null : envId;
} }
if (this.projectEnvMap) { if (this.projectEnvMap && Object.keys(this.projectEnvMap).length > 0) {
let projectEnvMapElement = this.projectEnvMap[d]; let projectEnvMapElement = this.projectEnvMap[d];
if (projectEnvMapElement.length>0) { if (projectEnvMapElement.length>0) {
projectEnvMapElement.forEach(envId => { projectEnvMapElement.forEach(envId => {

View File

@ -1,5 +1,21 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<env-group-popover
:env-map="projectEnvMap"
:project-ids="projectIds"
:show-env-group="false"
@setProjectEnvMap="setProjectEnvMap"
:environment-type.sync="environmentType"
:group-id="envGroupId"
:is-scenario="false"
@setEnvGroup="setEnvGroup"
:show-config-button-with-out-permission="
showConfigButtonWithOutPermission
"
:project-list="projectList"
ref="envPopover"
class="env-popover"
/>
<ms-table-adv-search-bar :condition.sync="condition" class="adv-search-bar" <ms-table-adv-search-bar :condition.sync="condition" class="adv-search-bar"
v-if="condition.components !== undefined && condition.components.length > 0" v-if="condition.components !== undefined && condition.components.length > 0"
@ -99,6 +115,9 @@ import {
getCustomTableWidth getCustomTableWidth
} from "metersphere-frontend/src/utils/tableUtils"; } from "metersphere-frontend/src/utils/tableUtils";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn"; import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
import {getApiScenarioEnvByProjectId} from "@/api/remote/api/api-automation";
import {getUiScenarioEnvByProjectId} from "@/api/remote/ui/ui-automation";
export default { export default {
name: "RelevanceUiScenarioList", name: "RelevanceUiScenarioList",
@ -112,6 +131,7 @@ export default {
MsTag, MsTag,
MsTableAdvSearchBar, MsTableAdvSearchBar,
MsTableColumn, MsTableColumn,
EnvGroupPopover,
}, },
props: { props: {
referenced: { referenced: {
@ -147,6 +167,7 @@ export default {
envGroupId: "", envGroupId: "",
versionFilters: [], versionFilters: [],
fieldsWidth: getCustomTableWidth('TEST_PLAN_UI_SCENARIO_CASE'), fieldsWidth: getCustomTableWidth('TEST_PLAN_UI_SCENARIO_CASE'),
projectIds: new Set()
}; };
}, },
computed: { computed: {
@ -244,10 +265,22 @@ export default {
selectCountChange(data) { selectCountChange(data) {
this.selectRows = this.$refs.scenarioTable.selectRows; this.selectRows = this.$refs.scenarioTable.selectRows;
this.$emit("selectCountChange", data); this.$emit("selectCountChange", data);
this.initProjectIds();
}, },
showReport() { showReport() {
} },
initProjectIds() {
this.projectIds.clear();
// this.map.clear();
this.selectRows.forEach((row) => {
getUiScenarioEnvByProjectId(row.id).then((res) => {
let data = res.data;
data.projectIds.forEach((d) => this.projectIds.add(d));
// this.map.set(row.id, data.projectIds);
});
});
},
} }
}; };
</script> </script>

View File

@ -148,6 +148,10 @@ export default {
let map = this.$refs.apiScenarioList.map; let map = this.$refs.apiScenarioList.map;
let envGroupId = this.$refs.apiScenarioList.envGroupId; let envGroupId = this.$refs.apiScenarioList.envGroupId;
if (!envMap || envMap.size == 0) {
this.$warning(this.$t('api_test.environment.select_environment'));
return;
}
selectRows.forEach(row => { selectRows.forEach(row => {
selectIds.push(row.id); selectIds.push(row.id);
}) })
@ -179,8 +183,6 @@ export default {
this.autoCheckStatus(); this.autoCheckStatus();
this.$refs.baseRelevance.close(); this.$refs.baseRelevance.close();
}); });
}, },
autoCheckStatus() { // autoCheckStatus() { //
if (!this.planId) { if (!this.planId) {

View File

@ -147,7 +147,8 @@
:filters="apiscenariofilters.RESULT_FILTERS" :filters="apiscenariofilters.RESULT_FILTERS"
:label="$t('api_test.automation.last_result')"> :label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}"> <template v-slot:default="{row}">
<el-link @click="showReport(row)" :disabled="!row.lastResult || row.lastResult==='PENDING' || row.lastResult==='UnExecute'"> <el-link @click="showReport(row)"
:disabled="!row.lastResult || row.lastResult==='PENDING' || row.lastResult==='UnExecute'">
<ms-test-plan-api-status :status="row.lastResult==='UnExecute' ? 'PENDING' : row.lastResult"/> <ms-test-plan-api-status :status="row.lastResult==='UnExecute' ? 'PENDING' : row.lastResult"/>
</el-link> </el-link>
</template> </template>
@ -177,7 +178,7 @@
@batchEdit="batchEdit"/> @batchEdit="batchEdit"/>
<ui-run-mode @handleRunBatch="handleRunBatch" ref="runMode" :custom-run-mode="true" <ui-run-mode @handleRunBatch="handleRunBatch" ref="runMode" :custom-run-mode="true"
:custom-serial-on-sample-error="true"/> :custom-serial-on-sample-error="true" :request="conditionRequest"/>
<ms-task-center ref="taskCenter" :show-menu="false"/> <ms-task-center ref="taskCenter" :show-menu="false"/>
</div> </div>
@ -327,6 +328,8 @@ export default {
] ]
}, },
versionFilters: [], versionFilters: [],
//
conditionRequest: {}
} }
}, },
computed: { computed: {
@ -358,14 +361,14 @@ export default {
}, },
search() { search() {
initCondition(this.condition, this.condition.selectAll); initCondition(this.condition, this.condition.selectAll);
if(this.condition && this.condition.filters && this.condition.filters.last_result){ if (this.condition && this.condition.filters && this.condition.filters.last_result) {
if(this.condition.filters.last_result.length > 0){ if (this.condition.filters.last_result.length > 0) {
//PENDING //PENDING
if(this.condition.filters.last_result.includes("PENDING")){ if (this.condition.filters.last_result.includes("PENDING")) {
this.condition.filters.last_result = [...this.condition.filters.last_result, "UnExecute"] this.condition.filters.last_result = [...this.condition.filters.last_result, "UnExecute"]
} }
//ERROR //ERROR
if(this.condition.filters.last_result.includes("ERROR")){ if (this.condition.filters.last_result.includes("ERROR")) {
this.condition.filters.last_result = [...this.condition.filters.last_result, "FAIL"] this.condition.filters.last_result = [...this.condition.filters.last_result, "FAIL"]
} }
} }
@ -437,12 +440,19 @@ export default {
let rows = this.orderBySelectRows(this.$refs.table.selectRows); let rows = this.orderBySelectRows(this.$refs.table.selectRows);
this.planCaseIds = []; this.planCaseIds = [];
rows.forEach(row => { rows.forEach(row => {
this.planCaseIds.push(row.id); this.planCaseIds.push(row.caseId);
}) })
this.conditionRequest.id = getUUID();
this.conditionRequest.ids = this.planCaseIds;
this.conditionRequest.projectId = this.projectId;
this.conditionRequest.condition = this.condition;
this.$refs.runMode.open(); this.$refs.runMode.open();
}, },
orderBySelectRows(rows) { orderBySelectRows(rows) {
let selectIds = Array.from(rows).map(row => row.id); let selectIds = this.$refs.table.selectIds;
if (rows) {
selectIds = Array.from(rows).map(row => row.id);
}
let array = []; let array = [];
for (let i in this.tableData) { for (let i in this.tableData) {
if (selectIds.indexOf(this.tableData[i].id) !== -1) { if (selectIds.indexOf(this.tableData[i].id) !== -1) {

View File

@ -7,6 +7,21 @@
:visible.sync="runModeVisible" :visible.sync="runModeVisible"
> >
<div class="mode-container"> <div class="mode-container">
<div>
<div>{{ $t("commons.environment") }}</div>
<env-select-popover :project-ids="projectIds"
:project-list="projectList"
:project-env-map="projectEnvListMap"
:environment-type="'JSON'"
:has-option-group="false"
:group-id="runConfig.environmentGroupId"
@setProjectEnvMap="setProjectEnvMap"
ref="envSelectPopover"
class="mode-row"
></env-select-popover>
</div>
<!-- 浏览器 --> <!-- 浏览器 -->
<div class="browser-row wrap"> <div class="browser-row wrap">
<div class="title">{{ $t("ui.browser") }}</div> <div class="title">{{ $t("ui.browser") }}</div>
@ -175,11 +190,13 @@
<script> <script>
import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter' import MsDialogFooter from 'metersphere-frontend/src/components/MsDialogFooter'
import {getOwnerProjects} from "@/business/utils/sdk-utils"; import {getCurrentProjectID, getOwnerProjects, strMapToObj} from "@/business/utils/sdk-utils";
import {uiScenarioEnvMap} from "@/api/remote/ui/ui-automation";
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
export default { export default {
name: "UiRunMode", name: "UiRunMode",
components: {MsDialogFooter}, components: {MsDialogFooter, EnvSelectPopover},
data() { data() {
return { return {
runModeVisible: false, runModeVisible: false,
@ -207,6 +224,8 @@ export default {
}, },
projectList: [], projectList: [],
projectIds: new Set(), projectIds: new Set(),
projectEnvListMap: {},
caseIdEnvNameMap: {},
}; };
}, },
props: { props: {
@ -262,6 +281,7 @@ export default {
}; };
this.runModeVisible = true; this.runModeVisible = true;
this.getWsProjects(); this.getWsProjects();
this.showPopover();
}, },
changeMode() { changeMode() {
this.runConfig.runWithinResourcePool = false; this.runConfig.runWithinResourcePool = false;
@ -296,6 +316,29 @@ export default {
this.$emit("handleRunBatch", this.runConfig); this.$emit("handleRunBatch", this.runConfig);
this.close(); this.close();
}, },
setProjectEnvMap(projectEnvMap) {
this.runConfig.envMap = strMapToObj(projectEnvMap);
},
showPopover() {
this.showScenarioPopover();
},
showScenarioPopover() {
let currentProjectID = getCurrentProjectID();
this.projectIds.clear();
uiScenarioEnvMap(this.request).then((res) => {
let data = res.data;
this.projectEnvListMap = data;
if (data) {
for (let d in data) {
this.projectIds.add(d);
}
}
if (this.projectIds.size === 0) {
this.projectIds.add(currentProjectID);
}
this.$refs.envSelectPopover.open();
});
},
}, },
}; };
</script> </script>

View File

@ -88,6 +88,7 @@ const TRACK_HEADER = {
{id: 'name', key: '2', label: 'api_test.automation.scenario_name'}, {id: 'name', key: '2', label: 'api_test.automation.scenario_name'},
{id: 'versionId', key: 'd', label: 'commons.version'}, {id: 'versionId', key: 'd', label: 'commons.version'},
{id: 'level', key: '3', label: 'api_test.automation.case_level'}, {id: 'level', key: '3', label: 'api_test.automation.case_level'},
{id: 'envs', key: '8', label: 'commons.environment'},
{id: 'tagNames', key: '4', label: 'api_test.automation.tag'}, {id: 'tagNames', key: '4', label: 'api_test.automation.tag'},
{id: 'stepTotal', key: '7', label: 'api_test.automation.step'}, {id: 'stepTotal', key: '7', label: 'api_test.automation.step'},
{id: 'passRate', key: '9', label: 'api_test.automation.passing_rate'}, {id: 'passRate', key: '9', label: 'api_test.automation.passing_rate'},