feat(测试跟踪): 支持区分默认环境和执行环境运行

https://www.tapd.cn/55049933/prong/stories/view/1155049933001010410
--user=郭雨琦
This commit is contained in:
guoyuqi 2022-11-07 11:16:45 +08:00 committed by xiaomeinvG
parent 4dc898b8d5
commit 18f5ed8928
12 changed files with 733 additions and 34 deletions

View File

@ -322,18 +322,6 @@ public class TestPlanApiCaseService {
*/
public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) {
if (request.getConfig() != null) {
RunModeConfigDTO config = request.getConfig();
if (config != null) {
String envType = config.getEnvironmentType();
String envGroupId = config.getEnvironmentGroupId();
Map<String, String> envMap = config.getEnvMap();
if ((StringUtils.equals(envType, EnvironmentType.JSON.toString()) && envMap != null && !envMap.isEmpty())) {
setApiCaseEnv(null, request.getPlanIds(), envMap);
} else if ((StringUtils.equals(envType, EnvironmentType.GROUP.toString()) && StringUtils.isNotBlank(envGroupId))) {
Map<String, String> map = environmentGroupProjectService.getEnvMap(envGroupId);
setApiCaseEnv(null, request.getPlanIds(), map);
}
}
return testPlanApiCaseExecuteService.run(request);
}
return null;

View File

@ -1562,6 +1562,8 @@ const message = {
config_environment: "Config Environment",
copy_environment: "copy environment",
environment: "Environment",
default_environment:"Default environment",
choose_new_environment:"Choose new environment",
environment_type: "Environment Type",
environment_json: "Environment Config",
environment_group_id: "Environment Group ID",

View File

@ -1571,6 +1571,8 @@ const message = {
config_environment: "配置环境",
copy_environment: "复制环境",
environment: "环境",
default_environment:"默认环境",
choose_new_environment:"选择新环境",
environment_type: "环境类型",
environment_json: "环境配置",
environment_group_id: "环境组ID",

View File

@ -1568,6 +1568,8 @@ const message = {
config_environment: "配置環境",
copy_environment: "復製環境",
environment: "環境",
default_environment: "默認環境",
choose_new_environment:"選擇新環境",
environment_type: "環境类型",
environment_json: "環境配置",
environment_group_id: "環境組ID",

View File

@ -0,0 +1,18 @@
package io.metersphere.plan.dto;
public enum ExecutionWay {
/**
* 仅运行
*/
RUN,
/**
* 仅保存
*/
SAVE,
/**
* 运行保存
*/
RUN_SAVE
}

View File

@ -4,6 +4,7 @@ import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.HttpHeaderUtils;
import io.metersphere.plan.dto.ExecutionWay;
import io.metersphere.plan.service.TestPlanService;
import io.metersphere.sechedule.MsScheduleJob;
import io.metersphere.service.BaseUserService;
@ -59,7 +60,7 @@ public class TestPlanTestJob extends MsScheduleJob {
// 定时任务指定调用微服务的user
HttpHeaderUtils.runAsUser(baseUserService.getUserDTO(runUserId));
testPlanService.run(runResourceId, runProjectId, runUserId, ReportTriggerMode.SCHEDULE.name(), null, config);
testPlanService.run(runResourceId, runProjectId, runUserId, ReportTriggerMode.SCHEDULE.name(), null, ExecutionWay.RUN.name(), config);
HttpHeaderUtils.clearUser();
}

View File

@ -34,5 +34,8 @@ public class TestPlanRunRequest {
//ui 测试
private String browser;
private boolean headlessEnabled;
//执行方式仅保存保存并执行仅执行
private String executionWay;
}

View File

@ -764,7 +764,7 @@ public class TestPlanService {
return testPlanReportService.genTestPlanReportBySchedule(planReportId, planId, userId, triggerMode, runModeConfigDTO);
}
public String run(String testPlanID, String projectID, String userId, String triggerMode, String planReportId, String apiRunConfig) {
public String run(String testPlanId, String projectId, String userId, String triggerMode, String planReportId,String executionWay, String apiRunConfig) {
RunModeConfigDTO runModeConfig = null;
try {
runModeConfig = JSON.parseObject(apiRunConfig, RunModeConfigDTO.class);
@ -776,9 +776,10 @@ public class TestPlanService {
}
//环境参数为空时依据测试计划保存的环境执行
if ((StringUtils.equals("GROUP", runModeConfig.getEnvironmentType()) && StringUtils.isBlank(runModeConfig.getEnvironmentGroupId()))
|| (!StringUtils.equals("GROUP", runModeConfig.getEnvironmentType()) && MapUtils.isEmpty(runModeConfig.getEnvMap()))) {
TestPlanWithBLOBs testPlanWithBLOBs = testPlanMapper.selectByPrimaryKey(testPlanID);
if (((StringUtils.equals("GROUP", runModeConfig.getEnvironmentType()) && StringUtils.isBlank(runModeConfig.getEnvironmentGroupId()))
|| (!StringUtils.equals("GROUP", runModeConfig.getEnvironmentType()) && MapUtils.isEmpty(runModeConfig.getEnvMap())))
&& !StringUtils.equals(executionWay,ExecutionWay.RUN.name())) {
TestPlanWithBLOBs testPlanWithBLOBs = testPlanMapper.selectByPrimaryKey(testPlanId);
if (StringUtils.isNotEmpty(testPlanWithBLOBs.getRunModeConfig())) {
try {
Map json = JSON.parseMap(testPlanWithBLOBs.getRunModeConfig());
@ -788,7 +789,7 @@ public class TestPlanService {
String envType = testPlanRunRequest.getEnvironmentType();
Map<String, String> envMap = testPlanRunRequest.getEnvMap();
String environmentGroupId = testPlanRunRequest.getEnvironmentGroupId();
runModeConfig = getRunModeConfigDTO(testPlanRunRequest, envType, envMap, environmentGroupId, testPlanID);
runModeConfig = getRunModeConfigDTO(testPlanRunRequest, envType, envMap, environmentGroupId, testPlanId);
if (!testPlanRunRequest.isRunWithinResourcePool()) {
runModeConfig.setResourcePoolId(null);
}
@ -803,9 +804,9 @@ public class TestPlanService {
}
//创建测试报告然后返回的ID重新赋值为resourceID作为后续的参数
TestPlanScheduleReportInfoDTO reportInfoDTO = this.genTestPlanReport(planReportId, testPlanID, userId, triggerMode, runModeConfig);
TestPlanScheduleReportInfoDTO reportInfoDTO = this.genTestPlanReport(planReportId, testPlanId, userId, triggerMode, runModeConfig);
//测试计划准备执行取消测试计划的实际结束时间
extTestPlanMapper.updateActualEndTimeIsNullById(testPlanID);
extTestPlanMapper.updateActualEndTimeIsNullById(testPlanId);
LoggerUtil.info("预生成测试计划报告【" + reportInfoDTO.getTestPlanReport() != null ? reportInfoDTO.getTestPlanReport().getName() : StringUtils.EMPTY + "】计划报告ID[" + planReportId + "]");
@ -821,7 +822,7 @@ public class TestPlanService {
if (reportInfoDTO.getPlanScenarioIdMap() != null) {
//执行场景执行任务
LoggerUtil.info("开始执行测试计划场景用例 " + planReportId);
scenarioReportMap = this.executeScenarioCase(planReportId, testPlanID, projectID, runModeConfig, triggerMode, userId, reportInfoDTO.getPlanScenarioIdMap());
scenarioReportMap = this.executeScenarioCase(planReportId, testPlanId, projectId, runModeConfig, triggerMode, userId, reportInfoDTO.getPlanScenarioIdMap());
}
if (reportInfoDTO.getPerformanceIdMap() != null) {
@ -833,7 +834,7 @@ public class TestPlanService {
if (reportInfoDTO.getUiScenarioIdMap() != null) {
//执行UI场景执行任务
LoggerUtil.info("开始执行测试计划 UI 场景用例 " + planReportId);
uiScenarioReportMap = this.executeUiScenarioCase(planReportId, testPlanID, projectID, runModeConfig, triggerMode, userId, reportInfoDTO.getUiScenarioIdMap());
uiScenarioReportMap = this.executeUiScenarioCase(planReportId, testPlanId, projectId, runModeConfig, triggerMode, userId, reportInfoDTO.getUiScenarioIdMap());
}
LoggerUtil.info("开始生成测试计划报告内容 " + planReportId);
@ -1517,24 +1518,29 @@ public class TestPlanService {
String environmentGroupId = testplanRunRequest.getEnvironmentGroupId();
String testPlanId = testplanRunRequest.getTestPlanId();
RunModeConfigDTO runModeConfig = getRunModeConfigDTO(testplanRunRequest, envType, envMap, environmentGroupId, testPlanId);
if (!testplanRunRequest.isRunWithinResourcePool()) {
runModeConfig.setResourcePoolId(null);
}
String apiRunConfig = JSON.toJSONString(runModeConfig);
return this.run(testPlanId, testplanRunRequest.getProjectId(),
testplanRunRequest.getUserId(), testplanRunRequest.getTriggerMode(), testplanRunRequest.getReportId(), apiRunConfig);
testplanRunRequest.getUserId(), testplanRunRequest.getTriggerMode(), testplanRunRequest.getReportId(),testplanRunRequest.getExecutionWay(), apiRunConfig);
}
private RunModeConfigDTO getRunModeConfigDTO(TestPlanRunRequest testplanRunRequest, String envType, Map<String, String> envMap, String environmentGroupId, String testPlanId) {
RunModeConfigDTO runModeConfig = new RunModeConfigDTO();
if (!testplanRunRequest.isRunWithinResourcePool()) {
runModeConfig.setResourcePoolId(null);
}
runModeConfig.setEnvironmentType(testplanRunRequest.getEnvironmentType());
if (StringUtils.equals(envType, "JSON") && !envMap.isEmpty()) {
runModeConfig.setEnvMap(testplanRunRequest.getEnvMap());
this.setPlanCaseEnv(testPlanId, runModeConfig);
if (!StringUtils.equals(testplanRunRequest.getExecutionWay(),ExecutionWay.RUN.name())){
this.setPlanCaseEnv(testPlanId, runModeConfig);
}
} else if (StringUtils.equals(envType, "GROUP") && StringUtils.isNotBlank(environmentGroupId)) {
runModeConfig.setEnvironmentGroupId(testplanRunRequest.getEnvironmentGroupId());
this.setPlanCaseEnv(testPlanId, runModeConfig);
if (!StringUtils.equals(testplanRunRequest.getExecutionWay(),ExecutionWay.RUN.name())){
this.setPlanCaseEnv(testPlanId, runModeConfig);
}
}
runModeConfig.setMode(testplanRunRequest.getMode());
runModeConfig.setResourcePoolId(testplanRunRequest.getResourcePoolId());
@ -1827,6 +1833,19 @@ public class TestPlanService {
public void updateRunModeConfig(TestPlanRunRequest testplanRunRequest) {
String testPlanId = testplanRunRequest.getTestPlanId();
updatePlan(testplanRunRequest, testPlanId);
RunModeConfigDTO runModeConfig = new RunModeConfigDTO();
if (!testplanRunRequest.isRunWithinResourcePool()) {
runModeConfig.setResourcePoolId(null);
}
runModeConfig.setEnvironmentType(testplanRunRequest.getEnvironmentType());
if (StringUtils.equals(testplanRunRequest.getEnvironmentType(), "JSON") && !testplanRunRequest.getEnvMap().isEmpty()) {
runModeConfig.setEnvMap(testplanRunRequest.getEnvMap());
this.setPlanCaseEnv(testPlanId, runModeConfig);
} else if (StringUtils.equals(testplanRunRequest.getEnvironmentType(), "GROUP") && StringUtils.isNotBlank(testplanRunRequest.getEnvironmentGroupId())) {
runModeConfig.setEnvironmentGroupId(testplanRunRequest.getEnvironmentGroupId());
runModeConfig.setEnvMap(testplanRunRequest.getEnvMap());
this.setPlanCaseEnv(testPlanId, runModeConfig);
}
}
public boolean haveUiCase(String planId) {

View File

@ -52,6 +52,10 @@ export function testPlanRunSave(param) {
return post(BASE_URL + 'run/save', param);
}
export function testPlanRun(param) {
return post(BASE_URL + 'run', param);
}
export function testPlanHaveUiCase(id) {
return get(BASE_URL + `have/ui/case/${id}`);
}

View File

@ -0,0 +1,454 @@
<template>
<el-dialog
destroy-on-close
:title="$t('load_test.runtime_config')"
width="550px"
style="margin-top: -8.65vh;max-height: 87.3vh"
@close="close"
:visible.sync="runModeVisible"
>
<div class="env-container">
<div>
<div>{{ $t("commons.environment") }}</div>
<env-select-popover :project-ids="projectIds"
:project-list="projectList"
:project-env-map="projectEnvListMap"
:group-id="runConfig.environmentGroupId"
@setProjectEnvMap="setProjectEnvMap"
@setEnvGroup="setEnvGroup"
ref="envSelectPopover"
class="mode-row"
></env-select-popover>
</div>
<div v-if="haveUICase">
<div>{{ $t("ui.browser") }}</div>
<div >
<el-select
size="mini"
v-model="runConfig.browser"
style="width: 100% "
class="mode-row"
>
<el-option
v-for="b in browsers"
:key="b.value"
:value="b.value"
:label="b.label"
></el-option>
</el-select>
</div>
</div>
<div>
<div class="mode-row">{{ $t("run_mode.title") }}</div>
<div >
<el-radio-group
v-model="runConfig.mode"
@change="changeMode"
style="width: 100%"
class="radio-change mode-row"
>
<el-radio label="serial">{{ $t("run_mode.serial") }}</el-radio>
<el-radio label="parallel">{{ $t("run_mode.parallel") }}</el-radio>
</el-radio-group>
</div>
</div>
<div >
<div class="mode-row">{{ $t("run_mode.other_config") }}</div>
<div >
<!-- 串行 -->
<div
class="mode-row"
v-if="runConfig.mode === 'serial' && testType === 'API'"
>
<el-checkbox
v-model="runConfig.runWithinResourcePool"
style="padding-right: 10px"
class="radio-change"
>
{{ $t("run_mode.run_with_resource_pool") }}
</el-checkbox><br/>
<el-select
:disabled="!runConfig.runWithinResourcePool"
v-model="runConfig.resourcePoolId"
size="mini"
style="width:100%; margin-top: 8px"
>
<el-option
v-for="item in resourcePools"
:key="item.id"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</div>
<!-- 并行 -->
<div
class="mode-row"
v-if="runConfig.mode === 'parallel' && testType === 'API'"
>
<el-checkbox
v-model="runConfig.runWithinResourcePool"
style="padding-right: 10px"
class="radio-change"
>
{{ $t("run_mode.run_with_resource_pool") }}
</el-checkbox><br/>
<el-select
:disabled="!runConfig.runWithinResourcePool"
v-model="runConfig.resourcePoolId"
size="mini"
style="width:100%; margin-top: 8px"
>
<el-option
v-for="item in resourcePools"
:key="item.id"
:label="item.name"
:disabled="!item.api"
:value="item.id"
>
</el-option>
</el-select>
</div>
<!-- 失败重试 -->
<div class="mode-row" v-if="isHasLicense">
<el-checkbox
v-model="runConfig.retryEnable"
class="radio-change ms-failure-div-right"
>
{{ $t("run_mode.retry_on_failure") }}
</el-checkbox>
<span v-if="runConfig.retryEnable">
<el-tooltip placement="top" style="margin: 0 4px 0 2px">
<div slot="content">{{ $t("run_mode.retry_message") }}</div>
<i class="el-icon-question" style="cursor: pointer"/>
</el-tooltip><br/>
<span>
{{ $t("run_mode.retry") }}
<el-input-number
:value="runConfig.retryNum"
v-model="runConfig.retryNum"
:min="1"
:max="10000000"
size="mini"
style="width: 103px;margin-top: 8px"
/>
&nbsp;
{{ $t("run_mode.retry_frequency") }}
</span>
</span>
</div>
<div class="mode-row" v-if="runConfig.mode === 'serial'">
<el-checkbox v-model="runConfig.onSampleError" class="radio-change">{{
$t("api_test.fail_to_stop")
}}
</el-checkbox>
</div>
<div class="mode-row" v-if="haveUICase">
<el-checkbox v-model="runConfig.headlessEnabled" class="radio-change">
{{ $t("ui.performance_mode") }}
</el-checkbox>
</div>
</div>
</div>
</div>
<template v-slot:footer>
<div class="dialog-footer" v-if="showSave">
<el-button @click="close">{{ $t("commons.cancel") }}</el-button>
<el-dropdown @command="handleCommand" style="margin-left: 5px">
<el-button type="primary">
{{
$t("commons.run")
}}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="run">{{
$t("commons.run")
}}
</el-dropdown-item>
<el-dropdown-item command="runAndSave">{{
$t("load_test.save_and_run")
}}
</el-dropdown-item>
<el-dropdown-item command="save">{{
$t("commons.save")
}}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<ms-dialog-footer v-else @cancel="close" @confirm="handleRunBatch"/>
</template>
</el-dialog>
</template>
<script>
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {strMapToObj} from "metersphere-frontend/src/utils";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import {getOwnerProjects} from "@/business/utils/sdk-utils";
import {getQuotaValidResourcePools} from "@/api/remote/resource-pool";
import EnvGroupPopover from "@/business/plan/env/EnvGroupPopover";
import {getApiCaseEnv} from "@/api/remote/plan/test-plan-api-case";
import {getApiScenarioEnv, getPlanCaseEnv} from "@/api/remote/plan/test-plan";
import EnvGroupWithOption from "../env/EnvGroupWithOption";
import EnvironmentGroup from "@/business/plan/env/EnvironmentGroupList";
import EnvSelectPopover from "@/business/plan/env/EnvSelectPopover";
export default {
name: "MsTestPlanRunModeWithEnv",
components: {EnvGroupPopover, MsDialogFooter,MsTag,EnvGroupWithOption,EnvironmentGroup,EnvSelectPopover},
computed: {
ENV_TYPE() {
return ENV_TYPE;
}
},
data() {
return {
btnStyle: {
width: "260px",
},
result:{loading: false},
runModeVisible: false,
testType: null,
resourcePools: [],
projectEnvListMap: {},
runConfig: {
mode: "serial",
reportType: "iddReport",
onSampleError: false,
runWithinResourcePool: false,
resourcePoolId: null,
envMap: new Map(),
environmentGroupId: "",
environmentType: ENV_TYPE.JSON,
retryEnable: false,
retryNum: 1,
browser: "CHROME",
},
isHasLicense: hasLicense(),
projectList: [],
projectIds: new Set(),
options: [
{
value: "confirmAndRun",
label: this.$t("load_test.save_and_run"),
},
{
value: "save",
label: this.$t("commons.save"),
},
],
value: "confirmAndRun",
browsers: [
{
label: this.$t("chrome"),
value: "CHROME",
},
{
label: this.$t("firefox"),
value: "FIREFOX",
},
],
};
},
props: {
planCaseIds: {
type: Array,
},
type: String,
planId: String,
showSave: {
type: Boolean,
default: false,
},
//ui ui
haveUICase: {
type: Boolean,
default: false,
},
},
methods: {
open(testType, runModeConfig) {
if (runModeConfig) {
this.runConfig = JSON.parse(runModeConfig);
this.runConfig.envMap = new Map();
this.runConfig.onSampleError = this.runConfig.onSampleError === 'true' || this.runConfig.onSampleError === true;
this.runConfig.runWithinResourcePool = this.runConfig.runWithinResourcePool === 'true' || this.runConfig.runWithinResourcePool === true;
}
this.runModeVisible = true;
this.testType = testType;
this.getResourcePools();
this.getWsProjects();
this.showPopover();
},
changeMode() {
this.runConfig.onSampleError = false;
this.runConfig.runWithinResourcePool = false;
this.runConfig.resourcePoolId = null;
},
close() {
this.runConfig = {
mode: "serial",
reportType: "iddReport",
onSampleError: false,
runWithinResourcePool: false,
resourcePoolId: null,
envMap: new Map(),
environmentGroupId: "",
environmentType: ENV_TYPE.JSON,
browser: "CHROME",
};
this.runModeVisible = false;
this.$emit("close");
},
handleRunBatch() {
this.$emit("handleRunBatch", this.runConfig);
this.close();
},
getResourcePools() {
getQuotaValidResourcePools()
.then((response) => {
this.resourcePools = response.data;
});
},
setProjectEnvMap(projectEnvMap) {
this.runConfig.envMap = projectEnvMap;
},
setEnvGroup(id) {
this.runConfig.environmentGroupId = id;
},
getWsProjects() {
getOwnerProjects()
.then((res) => {
this.projectList = res.data;
});
},
showPopover() {
this.projectIds.clear();
let param = undefined;
if (this.type === "apiCase") {
param = this.planCaseIds;
getApiCaseEnv(param).then((res) => {
let data = res.data;
if (data) {
this.projectEnvListMap = data;
for (let d in data) {
this.projectIds.add(d);
}
}
this.$refs.envSelectPopover.open();
});
} else if (this.type === "apiScenario") {
param = this.planCaseIds;
getApiScenarioEnv(param).then((res) => {
let data = res.data;
if (data) {
this.projectEnvListMap = data;
for (let d in data) {
this.projectIds.add(d);
}
}
this.$refs.envSelectPopover.open();
});
} else if (this.type === "plan") {
param = {id: this.planId};
getPlanCaseEnv(param).then((res) => {
let data = res.data;
if (data) {
this.projectEnvListMap = data;
for (let d in data) {
this.projectIds.add(d);
}
}
this.$refs.envSelectPopover.open();
});
}
},
handleCommand(command) {
if (
this.runConfig.runWithinResourcePool &&
this.runConfig.resourcePoolId == null
) {
this.$warning(
this.$t("workspace.env_group.please_select_run_within_resource_pool")
);
return;
}
this.runConfig.envMap =strMapToObj(this.runConfig.envMap)
if (command === "runAndSave") {
this.runConfig.executionWay = "runAndSave";
} else if(command === "save"){
this.runConfig.executionWay = "save";
} else {
this.runConfig.executionWay = "run";
}
this.handleRunBatch();
},
},
};
</script>
<style scoped>
.env-container .title {
width: 100px;
min-width: 100px;
text-align: right;
}
.env-container .content {
width: 163px;
}
.wrap {
display: flex;
align-items: center;
padding: 5px 10px 5px 10px;
}
:deep(.content .el-popover__reference) {
width: 100%;
}
.mode-row {
margin-top: 8px;
}
.other-title {
height: 100%;
margin-top: 25px;
align-items: flex-start;
}
.other-content {
height: 100%;
}
.other-row {
height: 125px;
}
.project-name {
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
width: 150px;
margin-left: 8px;
vertical-align: middle;
}
</style>
<style lang="scss" scoped>
.radio-change:deep(.el-radio__input.is-checked + .el-radio__label) {
color: #606266 !important;
}
.radio-change:deep(.el-checkbox__input.is-checked+.el-checkbox__label) {
color: #606266 !important;
}
</style>

View File

@ -315,7 +315,7 @@
<ms-test-plan-schedule-maintain ref="scheduleMaintain" @refreshTable="initTableData" :plan-case-ids="[]"
:type="'plan'" :have-u-i-case="haveUICase"/>
<ms-test-plan-schedule-batch-switch ref="scheduleBatchSwitch" @refresh="refresh"/>
<plan-run-mode-with-env @handleRunBatch="_handleRun" ref="runMode" :plan-case-ids="[]" :type="'plan'"
<ms-test-plan-run-mode-with-env @handleRunBatch="_handleRun" ref="runMode" :plan-case-ids="[]" :type="'plan'"
:plan-id="currentPlanId" :show-save="true" :have-u-i-case="haveUICase"/>
<test-plan-report-review ref="testCaseReportView"/>
<ms-task-center ref="taskCenter" :show-menu="false"/>
@ -366,13 +366,13 @@ import MsTestPlanScheduleMaintain from "@/business/plan/components/ScheduleMaint
import {getCurrentProjectID, getCurrentUser, getCurrentUserId} from "metersphere-frontend/src/utils/token";
import {hasLicense, hasPermission} from "metersphere-frontend/src/utils/permission";
import {operationConfirm} from "metersphere-frontend/src/utils";
import PlanRunModeWithEnv from "@/business/plan/common/PlanRunModeWithEnv";
import MsTestPlanRunModeWithEnv from "@/business/plan/common/TestPlanRunModeWithEnv";
import MsTaskCenter from "metersphere-frontend/src/components/task/TaskCenter";
import {
getPlanStageOption, testPlanCopy, testPlanDelete, testPlanEdit,
testPlanEditFollows,
testPlanEditRunConfig, testPlanGetEnableScheduleCount, testPlanGetFollow, testPlanGetPrincipal, testPlanHaveExecCase,
testPlanHaveUiCase, testPlanList, testPlanRunBatch,
testPlanHaveUiCase, testPlanList, testPlanRun, testPlanRunBatch,
testPlanRunSave, testPlanUpdateScheduleEnable
} from "@/api/remote/plan/test-plan";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
@ -397,7 +397,7 @@ export default {
MsTestPlanScheduleMaintain,
MsTableOperator, MsTableOperatorButton,
MsDialogFooter, MsTableHeader,
MsTablePagination, PlanRunModeWithEnv, MsTaskCenter,
MsTablePagination, MsTestPlanRunModeWithEnv, MsTaskCenter,
MsTableColumn,
MsTable,
MsTestPlanScheduleBatchSwitch
@ -829,7 +829,8 @@ export default {
param.retryNum = config.retryNum;
param.browser = config.browser;
param.headlessEnabled = config.headlessEnabled;
if (config.isRun === true) {
if (config.executionWay === "runAndSave") {
param.executionWay = "RUN_SAVE"
this.$refs.taskCenter.open();
this.cardLoading = true;
testPlanRunSave(param)
@ -838,7 +839,8 @@ export default {
this.$success(this.$t('commons.run_success'));
});
} else {
} else if (config.executionWay === "save") {
param.executionWay = "SAVE"
this.cardLoading = true;
testPlanEditRunConfig(param)
.then(() => {
@ -846,6 +848,15 @@ export default {
this.initTableData();
this.$success(this.$t('commons.save_success'));
});
} else {
param.executionWay = "RUN"
this.$refs.taskCenter.open();
this.cardLoading = true;
testPlanRun(param)
.then(() => {
this.cardLoading = false;
this.$success(this.$t('commons.run_success'));
});
}
},
saveFollow(row) {

View File

@ -0,0 +1,195 @@
<template>
<div>
<el-radio-group v-model="radio" style="width: 100%" @change="radioChange" class="radio-change">
<el-radio :label="ENV_TYPE.JSON">{{ $t('workspace.env_group.env_list') }}</el-radio>
<el-radio :label="ENV_TYPE.GROUP">{{ $t('workspace.env_group.name') }}<i class="el-icon-tickets mode-span" @click="viewGroup"></i></el-radio>
</el-radio-group>
<div v-for="(pe, pIndex) in eventData" :key="pe.id" v-show="radio === ENV_TYPE.JSON">
<el-card shadow="never" style="margin-top: 8px;background: #F5F6F7;border-radius: 4px;">
<i @click="expandCard(pIndex)" v-if="pe.expendStatus==='close'" class="el-icon-caret-right" style="color: var(--primary_color)"/>
<i @click="expandCard(pIndex)" v-else class="el-icon-caret-bottom" style="color: var(--primary_color)"/>
<span class="project-name" :title="getProjectName(pe.id)">
{{ getProjectName(pe.id) }}
</span><br/>
<div v-if="pe.expendStatus==='open'">
<el-radio-group v-model="pe.envRadio" style="width: 100%;" @change="envRadioChange(pe.envRadio,pIndex)" class="radio-change">
<el-radio label="DEFAULT_ENV" style="margin-top: 7px">{{$t('api_test.environment.default_environment') }}</el-radio>
<el-radio label="CUSTOMIZE_ENV" style="margin-top: 7px">{{$t('api_test.environment.choose_new_environment')}}</el-radio>
</el-radio-group>
<el-tag v-show="!pe.showEnvSelect" v-for="(itemName,index) in selectedEnvName.get(pe.id)" :key="index" size="mini"
style="margin-left: 0; margin-right: 2px;margin-top: 8px">{{ itemName }}</el-tag>
<el-select v-show="pe.showEnvSelect" v-model="pe['selectEnv']" :placeholder="$t('api_test.environment.select_environment')"
style="margin-top: 8px;width: 100%;" size="small" @change="chooseEnv">
<el-option v-for="(environment, index) in pe.envs" :key="index"
:label="environment.name"
:value="environment.id"/>
</el-select>
</div>
</el-card>
</div>
<div v-show="radio === ENV_TYPE.GROUP">
<div>
<el-select v-model="groupId" :placeholder="$t('workspace.env_group.select')" @change="chooseEnvGroup"
style="margin-top: 8px;width: 100%;" size="small">
<el-option v-for="(group, index) in groups" :key="index"
:label="group.name"
:value="group.id"/>
</el-select>
</div>
<el-dialog :visible="visible" append-to-body :title="$t('workspace.env_group.name')" @close="visible = false"
style="height: 800px;">
<template>
<environment-group style="overflow-y: auto;"
:screen-height="'350px'"
:read-only="true"
></environment-group>
</template>
</el-dialog>
</div>
</div>
</template>
<script>
import {ENV_TYPE} from "metersphere-frontend/src/utils/constants";
import {environmentGetALl} from "metersphere-frontend/src/api/environment";
import MsTag from "metersphere-frontend/src/components/MsTag";
import EnvironmentGroup from "@/business/plan/env/EnvironmentGroupList";
import {getEnvironmentByProjectId} from "@/api/remote/api/api-environment";
import {parseEnvironment} from "metersphere-frontend/src/model/EnvironmentModel";
export default {
name: "EnvSelectPopover",
components: {MsTag,EnvironmentGroup},
data(){
return {
radio:ENV_TYPE.JSON,
visible: false,
groups:[],
selectedEnvName:new Map(),
showEnvName:false,
eventData:[],
evnList:[],
selectEnvMap:new Map(),
}
},
computed: {
ENV_TYPE() {
return ENV_TYPE;
}
},
props:{
projectIds: Set,
projectList:Array,
projectEnvMap:Object,
envMap: Map,
groupId: {
type: String,
default() {
return "";
}
},
},
methods: {
open(){
this.initDefaultEnv();
this.getgroups();
},
radioChange(val){
this.radio = val;
},
getProjectName(id) {
const project = this.projectList.find(p => p.id === id);
return project ? project.name : "";
},
envRadioChange(val,index){
this.eventData[index].envRadio = val
this.eventData[index].showEnvSelect = this.eventData[index].envRadio === "CUSTOMIZE_ENV";
},
viewGroup() {
this.visible = true;
},
getgroups(){
environmentGetALl().then(res => {
let data = res.data;
this.groups = data ? data : [];
})
},
chooseEnv(val){
let filter = this.evnList.filter(e => e.id === val);
this.selectEnvMap.set(filter[0].projectId,val);
this.$emit('setProjectEnvMap', this.selectEnvMap);
},
chooseEnvGroup(envGroupId){
this.$emit("setEnvGroup", envGroupId);
},
initDefaultEnv(){
this.selectedEnvName = new Map();
this.evnList = [];
this.projectIds.forEach(d => {
let item = {id: d, envs: [], selectEnv: "",envRadio:"DEFAULT_ENV",showEnvSelect:false,expendStatus:"open"};
this.eventData.push(item);
getEnvironmentByProjectId(d)
.then(res => {
let envs = res.data;
envs.forEach(environment => {
parseEnvironment(environment);
});
//
let temp = this.eventData.find(dt => dt.id === d);
temp.envs = envs;
envs.forEach(t=>{
this.evnList.push(t);
})
if (this.envMap && this.envMap.size > 0) {
let envId = this.envMap.get(id);
//
temp.selectEnv = envs.filter(e => e.id === envId).length === 0 ? null : envId;
}
if (this.projectEnvMap) {
let projectEnvMapElement = this.projectEnvMap[d];
if (projectEnvMapElement.length>0) {
projectEnvMapElement.forEach(envId => {
let filter = envs.filter(e => e.id === envId);
if (!this.selectedEnvName.has(d)) {
let name = [];
name.push(filter[0].name)
this.selectedEnvName.set(d,name);
} else {
this.selectedEnvName.get(d).push(filter[0].name);
}
});
}
}
});
})
},
expandCard(index){
if (this.eventData[index].expendStatus === "open") {
this.eventData[index].expendStatus = "close"
}else {
this.eventData[index].expendStatus = "open"
}
}
}
}
</script>
<style scoped>
.mode-span{
margin-left: 6px;
}
</style>
<style lang="scss" scoped>
.radio-change:deep(.el-radio__input.is-checked + .el-radio__label) {
color: #606266 !important;
}
</style>