feat(接口自动化): 跨项目添加场景步骤

This commit is contained in:
shiziyuan9527 2021-02-23 18:06:12 +08:00
parent 354892fd9c
commit 0a997065dd
12 changed files with 256 additions and 57 deletions

View File

@ -6,6 +6,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.List; import java.util.List;
import java.util.Map;
@Setter @Setter
@Getter @Getter
@ -34,4 +35,6 @@ public class RunDefinitionRequest {
private Response response; private Response response;
private List<String> bodyUploadIds; private List<String> bodyUploadIds;
private Map<String, String> environmentMap;
} }

View File

@ -50,6 +50,7 @@ import org.apache.jorphan.collections.ListedHashTree;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type") @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type")
@ -104,6 +105,8 @@ public abstract class MsTestElement {
private boolean customizeReq; private boolean customizeReq;
@JSONField(ordinal = 12) @JSONField(ordinal = 12)
private String projectId; private String projectId;
@JSONField(ordinal = 13)
private Map<String, String> environmentMap;
private MsTestElement parent; private MsTestElement parent;
@ -181,9 +184,10 @@ public abstract class MsTestElement {
return null; return null;
} }
protected EnvironmentConfig getEnvironmentConfig(String environmentId) { protected EnvironmentConfig getEnvironmentConfig(String projectId) {
String env = environmentMap.get(projectId);
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class); ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentId); ApiTestEnvironmentWithBLOBs environment = environmentService.get(env);
if (environment != null && environment.getConfig() != null) { if (environment != null && environment.getConfig() != null) {
return JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class); return JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
} }

View File

@ -0,0 +1,130 @@
<template>
<el-dialog
title="环境选择"
:visible.sync="dialogVisible"
width="30%"
:before-close="handleClose">
<div v-for="pe in data" :key="pe.id">
<div>
{{ getProjectName(pe.id) }}
<el-select v-model="pe['selectEnv']" placeholder="请选择环境" style="margin-left:10px; margin-top: 10px;" size="small">
<el-option v-for="(environment, index) in pe.envs" :key="index"
:label="environment.name + (environment.config.httpConfig.socket ? (': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '')"
:value="environment.id"/>
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig">
{{ $t('api_test.environment.environment_config') }}
</el-button>
<template v-slot:empty>
<div class="empty-environment">
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig">
{{ $t('api_test.environment.environment_config') }}
</el-button>
</div>
</template>
</el-select>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleConfirm" size="small"> </el-button>
</span>
</el-dialog>
</template>
<script>
import {parseEnvironment} from "@/business/components/api/test/model/EnvironmentModel";
export default {
name: "ApiScenarioEnv",
props: {
projectIds: Set
},
data() {
return {
dialogVisible: false,
projects: [],
data: [],
environmentId: '',
environments: []
}
},
created() {
this.getWsProjects();
},
methods: {
handleClose(done) {
this.$confirm('确认关闭?')
.then(_ => {
done();
})
.catch(_ => {
});
},
init() {
this.projectIds.forEach(id => {
this.result = this.$get('/api/environment/list/' + id, res => {
let envs = res.data;
envs.forEach(environment => {
parseEnvironment(environment);
});
let item = {};
item.id = id;
item.envs = envs;
item.selectEnv = '';
this.data.push(item)
})
})
},
open() {
this.data = [];
this.dialogVisible = true;
this.init();
},
getWsProjects() {
this.$get("/project/listAll", res => {
this.projects = res.data;
})
},
getProjectName(id) {
const project = this.projects.find(p => p.id === id);
if (project) {
return project.name;
}
return '';
},
openEnvironmentConfig() {
if (!this.projectId) {
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(this.projectId);
},
getProjectEnvMap() {
},
handleConfirm() {
let map = new Map();
let sign = true;
this.data.forEach(dt => {
if (!dt.selectEnv) {
sign = false;
return;
}
map.set(dt.id, dt.selectEnv);
})
if (!sign) {
this.$warning("请为每个项目选择一个运行环境!");
return;
}
this.$emit('setProjectEnvMap', map);
this.dialogVisible = false;
}
}
}
</script>
<style scoped>
</style>

View File

@ -363,7 +363,7 @@
this.changeSelectDataRangeAll(); this.changeSelectDataRangeAll();
this.search(); this.search();
}, },
search() { search(projectId) {
this.selectRows = new Set(); this.selectRows = new Set();
this.getLabel() this.getLabel()
this.condition.moduleIds = this.selectNodeIds; this.condition.moduleIds = this.selectNodeIds;
@ -372,7 +372,10 @@
this.condition.moduleIds = []; this.condition.moduleIds = [];
} }
if (this.projectId != null) { // todo
if (projectId != null) {
this.condition.projectId = projectId;
} else if (this.projectId != null) {
this.condition.projectId = this.projectId; this.condition.projectId = this.projectId;
} }

View File

@ -9,7 +9,7 @@
name: 'MsDebugRun', name: 'MsDebugRun',
components: {}, components: {},
props: { props: {
environment: String, environment: Map,
debug: Boolean, debug: Boolean,
reportId: String, reportId: String,
runData: Object, runData: Object,
@ -112,9 +112,12 @@
threadGroup.hashTree = []; threadGroup.hashTree = [];
threadGroup.name = this.reportId; threadGroup.name = this.reportId;
threadGroup.enableCookieShare = this.runData.enableCookieShare; threadGroup.enableCookieShare = this.runData.enableCookieShare;
let map = this.environment;
this.runData.environmentMap = this.strMapToObj(map);
threadGroup.hashTree.push(this.runData); threadGroup.hashTree.push(this.runData);
testPlan.hashTree.push(threadGroup); testPlan.hashTree.push(threadGroup);
let reqObj = {id: this.reportId, reportId: this.reportId, scenarioName: this.runData.name, scenarioId: this.runData.id, environmentId: this.environment, testElement: testPlan, projectId: getCurrentProjectID()}; let reqObj = {id: this.reportId, reportId: this.reportId, scenarioName: this.runData.name,
scenarioId: this.runData.id, testElement: testPlan, projectId: getCurrentProjectID(), environmentMap: this.strMapToObj(map)};
let bodyFiles = this.getBodyUploadFiles(reqObj); let bodyFiles = this.getBodyUploadFiles(reqObj);
let url = "/api/automation/run/debug"; let url = "/api/automation/run/debug";
this.$fileUpload(url, null, bodyFiles, reqObj, response => { this.$fileUpload(url, null, bodyFiles, reqObj, response => {
@ -122,6 +125,13 @@
this.$emit('runRefresh', {}); this.$emit('runRefresh', {});
}, erro => { }, erro => {
}); });
},
strMapToObj(strMap){
let obj= Object.create(null);
for (let[k,v] of strMap) {
obj[k] = v;
}
return obj;
} }
} }
} }

View File

@ -111,24 +111,26 @@
<el-col :span="3" class="ms-col-one ms-font"> <el-col :span="3" class="ms-col-one ms-font">
<el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox> <el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox>
</el-col> </el-col>
<el-col :span="7" class="ms-font"> <el-col :span="7" class="ms-col-one ms-font">
<el-select v-model="currentEnvironmentId" size="small" class="ms-htt-width" <el-link type="primary" @click="handleEnv">环境配置</el-link>
:placeholder="$t('api_test.definition.request.run_env')" <!-- <el-select v-model="currentEnvironmentId" size="small" class="ms-htt-width"-->
clearable> <!-- :placeholder="$t('api_test.definition.request.run_env')"-->
<el-option v-for="(environment, index) in environments" :key="index" <!-- clearable>-->
:label="environment.name + (environment.config.httpConfig.socket ? (': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '')" <!-- <el-option v-for="(environment, index) in environments" :key="index"-->
:value="environment.id"/> <!-- :label="environment.name + (environment.config.httpConfig.socket ? (': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '')"-->
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig"> <!-- :value="environment.id"/>-->
{{ $t('api_test.environment.environment_config') }} <!-- <el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig">-->
</el-button> <!-- {{ $t('api_test.environment.environment_config') }}-->
<template v-slot:empty> <!-- </el-button>-->
<div class="empty-environment"> <!-- <template v-slot:empty>-->
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig"> <!-- <div class="empty-environment">-->
{{ $t('api_test.environment.environment_config') }} <!-- <el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig">-->
</el-button> <!-- {{ $t('api_test.environment.environment_config') }}-->
</div> <!-- </el-button>-->
</template> <!-- </div>-->
</el-select> <!-- </template>-->
<!-- </el-select>-->
</el-col> </el-col>
<el-col :span="2"> <el-col :span="2">
<el-button :disabled="scenarioDefinition.length < 1" size="small" type="primary" @click="runDebug">{{$t('api_test.request.debug')}}</el-button> <el-button :disabled="scenarioDefinition.length < 1" size="small" type="primary" @click="runDebug">{{$t('api_test.request.debug')}}</el-button>
@ -184,11 +186,13 @@
<!--场景导入 --> <!--场景导入 -->
<scenario-relevance @save="addScenario" ref="scenarioRelevance"/> <scenario-relevance @save="addScenario" ref="scenarioRelevance"/>
<api-scenario-env :project-ids="projectIds" ref="apiScenarioEnv" @setProjectEnvMap="setProjectEnvMap"/>
<!-- 环境 --> <!-- 环境 -->
<api-environment-config ref="environmentConfig" @close="environmentConfigClose"/> <api-environment-config ref="environmentConfig" @close="environmentConfigClose"/>
<!--执行组件--> <!--执行组件-->
<ms-run :debug="true" :environment="currentEnvironmentId" :reportId="reportId" :run-data="debugData" <ms-run :debug="true" :environment="projectEnvMap" :reportId="reportId" :run-data="debugData"
@runRefresh="runRefresh" ref="runTest"/> @runRefresh="runRefresh" ref="runTest"/>
<!-- 调试结果 --> <!-- 调试结果 -->
<el-drawer :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" :modal="false" size="90%"> <el-drawer :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" :modal="false" size="90%">
@ -230,6 +234,8 @@ import ScenarioApiRelevance from "./api/ApiRelevance";
import ScenarioRelevance from "./api/ScenarioRelevance"; import ScenarioRelevance from "./api/ScenarioRelevance";
import MsComponentConfig from "./component/ComponentConfig"; import MsComponentConfig from "./component/ComponentConfig";
import {handleCtrlSEvent} from "../../../../../common/js/utils"; import {handleCtrlSEvent} from "../../../../../common/js/utils";
import {getProject} from "@/business/components/api/automation/scenario/event";
import ApiScenarioEnv from "@/business/components/api/automation/scenario/ApiScenarioEnv";
export default { export default {
name: "EditApiScenario", name: "EditApiScenario",
@ -238,6 +244,7 @@ export default {
currentScenario: {}, currentScenario: {},
}, },
components: { components: {
ApiScenarioEnv,
MsVariableList, MsVariableList,
ScenarioRelevance, ScenarioRelevance,
ScenarioApiRelevance, ScenarioApiRelevance,
@ -291,7 +298,9 @@ export default {
globalOptions: { globalOptions: {
spacing: 30 spacing: 30
}, },
response: {} response: {},
projectIds: new Set,
projectEnvMap: new Map
} }
}, },
created() { created() {
@ -304,7 +313,13 @@ export default {
this.getApiScenario(); this.getApiScenario();
this.addListener(); // ctrl s this.addListener(); // ctrl s
}, },
directives: {OutsideClick}, mounted() {
getProject.$on('addProjectEnv', (projectId, projectEnv) => {
this.projectIds.add(projectId);
this.projectEnvMap.set(projectId, projectEnv);
})
},
directives: {OutsideClick},
computed: { computed: {
buttons() { buttons() {
let buttons = [ let buttons = [
@ -679,9 +694,16 @@ export default {
}, },
runDebug() { runDebug() {
/*触发执行操作*/ /*触发执行操作*/
if (!this.currentEnvironmentId) { // if (!this.currentEnvironmentId) {
this.$error(this.$t('api_test.environment.select_environment')); // this.$error(this.$t('api_test.environment.select_environment'));
return; // return;
// }
let iter = this.projectEnvMap.values();
for (let i of iter) {
if (!i) {
this.$warning("请为每个项目选择一个运行环境!");
return;
}
} }
this.$refs['currentScenario'].validate((valid) => { this.$refs['currentScenario'].validate((valid) => {
if (valid) { if (valid) {
@ -694,7 +716,7 @@ export default {
referenced: 'Created', referenced: 'Created',
enableCookieShare: this.enableCookieShare, enableCookieShare: this.enableCookieShare,
headers: this.currentScenario.headers, headers: this.currentScenario.headers,
environmentId: this.currentEnvironmentId, environmentMap: this.projectEnvMap,
hashTree: this.scenarioDefinition hashTree: this.scenarioDefinition
}; };
this.reportId = getUUID().substring(0, 8); this.reportId = getUUID().substring(0, 8);
@ -933,7 +955,7 @@ export default {
variables: this.currentScenario.variables, variables: this.currentScenario.variables,
headers: this.currentScenario.headers, headers: this.currentScenario.headers,
referenced: 'Created', referenced: 'Created',
environmentId: this.currentEnvironmentId, environmentMap: this.projectEnvMap,
hashTree: this.scenarioDefinition, hashTree: this.scenarioDefinition,
projectId: this.projectId projectId: this.projectId
}; };
@ -973,6 +995,15 @@ export default {
size += this.currentScenario.headers.length - 1; size += this.currentScenario.headers.length - 1;
} }
return size; return size;
},
beforeDestroy() {
getProject.$off('addProjectEnv');
},
handleEnv() {
this.$refs.apiScenarioEnv.open();
},
setProjectEnvMap(projectEnvMap) {
this.projectEnvMap = projectEnvMap;
} }
} }
} }

View File

@ -1,6 +1,7 @@
<template> <template>
<relevance-dialog :title="$t('api_test.automation.scenario_import')" ref="relevanceDialog"> <test-case-relevance-base
@setProject="setProject"
ref="baseRelevance">
<template v-slot:aside> <template v-slot:aside>
<ms-api-scenario-module <ms-api-scenario-module
@nodeSelectEvent="nodeChange" @nodeSelectEvent="nodeChange"
@ -22,8 +23,7 @@
<el-button type="primary" @click="copy" @keydown.enter.native.prevent>{{$t('commons.copy')}}</el-button> <el-button type="primary" @click="copy" @keydown.enter.native.prevent>{{$t('commons.copy')}}</el-button>
<el-button type="primary" @click="reference" @keydown.enter.native.prevent> {{ $t('api_test.scenario.reference') }}</el-button> <el-button type="primary" @click="reference" @keydown.enter.native.prevent> {{ $t('api_test.scenario.reference') }}</el-button>
</template> </template>
</test-case-relevance-base>
</relevance-dialog>
</template> </template>
<script> <script>
@ -37,10 +37,12 @@
import MsApiScenarioList from "../ApiScenarioList"; import MsApiScenarioList from "../ApiScenarioList";
import {getUUID} from "../../../../../../common/js/utils"; import {getUUID} from "../../../../../../common/js/utils";
import RelevanceDialog from "../../../../track/plan/view/comonents/base/RelevanceDialog"; import RelevanceDialog from "../../../../track/plan/view/comonents/base/RelevanceDialog";
import TestCaseRelevanceBase from "@/business/components/track/plan/view/comonents/base/TestCaseRelevanceBase";
export default { export default {
name: "ScenarioRelevance", name: "ScenarioRelevance",
components: { components: {
TestCaseRelevanceBase,
RelevanceDialog, RelevanceDialog,
MsApiScenarioList, MsApiScenarioList,
MsApiScenarioModule, MsApiScenarioModule,
@ -55,6 +57,12 @@
isApiListEnable: true, isApiListEnable: true,
currentScenario: [], currentScenario: [],
currentScenarioIds: [], currentScenarioIds: [],
projectId: ''
}
},
watch: {
projectId() {
this.$refs.apiScenarioList.search(this.projectId);
} }
}, },
methods: { methods: {
@ -69,7 +77,7 @@
scenarios.push(obj); scenarios.push(obj);
}); });
this.$emit('save', scenarios); this.$emit('save', scenarios);
this.close(); this.$refs.baseRelevance.close();
}, },
copy() { copy() {
let scenarios = []; let scenarios = [];
@ -87,7 +95,7 @@
} }
}); });
this.$emit('save', scenarios); this.$emit('save', scenarios);
this.close(); this.$refs.baseRelevance.close();
} }
}) })
}, },
@ -96,9 +104,9 @@
this.$refs.relevanceDialog.close(); this.$refs.relevanceDialog.close();
}, },
open() { open() {
this.$refs.relevanceDialog.open(); this.$refs.baseRelevance.open();
if (this.$refs.apiScenarioList) { if (this.$refs.apiScenarioList) {
this.$refs.apiScenarioList.search(); this.$refs.apiScenarioList.search(this.projectId);
} }
}, },
nodeChange(node, nodeIds, pNodes) { nodeChange(node, nodeIds, pNodes) {
@ -117,6 +125,9 @@
this.currentScenario = Array.from(data).map(row => row); this.currentScenario = Array.from(data).map(row => row);
this.currentScenarioIds = Array.from(data).map(row => row.id); this.currentScenarioIds = Array.from(data).map(row => row.id);
}, },
setProject(projectId) {
this.projectId = projectId;
},
} }
} }
</script> </script>

View File

@ -71,6 +71,7 @@ import {getUUID} from "@/common/js/utils";
import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiResponseComponent from "./ApiResponseComponent"; import ApiResponseComponent from "./ApiResponseComponent";
import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo"; import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo";
import {getProject} from "@/business/components/api/automation/scenario/event";
export default { export default {
name: "MsApiComponent", name: "MsApiComponent",
@ -118,6 +119,7 @@ export default {
} }
} }
} }
getProject.$emit('addProjectEnv', this.request.projectId, this.currentEnvironmentId);
}, },
computed: { computed: {
displayColor() { displayColor() {

View File

@ -29,6 +29,7 @@
import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters"; import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters";
import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm"; import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm";
import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiBaseComponent from "../common/ApiBaseComponent";
import {getProject} from "@/business/components/api/automation/scenario/event";
export default { export default {
name: "ApiScenarioComponent", name: "ApiScenarioComponent",
@ -39,6 +40,7 @@
type: Boolean, type: Boolean,
default: false, default: false,
}, },
currentEnvironmentId: String,
}, },
watch: {}, watch: {},
created() { created() {
@ -56,7 +58,8 @@
this.scenario.disabled = true; this.scenario.disabled = true;
this.scenario.name = response.data.name; this.scenario.name = response.data.name;
const project = this.projects.find(p => p.id === this.scenario.projectId); const project = this.projects.find(p => p.id === this.scenario.projectId);
this.scenario.projectName = project.name; getProject.$emit('addProjectEnv', this.scenario.projectId, this.currentEnvironmentId);
this.scenario.projectName = project ? project.name : '';
this.$emit('refReload'); this.$emit('refReload');
} else { } else {
this.scenario.referenced = "Deleted"; this.scenario.referenced = "Deleted";

View File

@ -0,0 +1,2 @@
import Vue from 'vue';
export const getProject = new Vue();

View File

@ -223,6 +223,7 @@ export class Scenario extends BaseConfig {
this.headers = []; this.headers = [];
this.requests = []; this.requests = [];
this.environmentId = undefined; this.environmentId = undefined;
this.environmentMap = undefined;
this.dubboConfig = undefined; this.dubboConfig = undefined;
this.environment = undefined; this.environment = undefined;
this.enableCookieShare = false; this.enableCookieShare = false;

View File

@ -14,11 +14,12 @@
<slot></slot> <slot></slot>
<template v-slot:footer> <template v-slot:footer>
<ms-dialog-footer @cancel="close" @confirm="save"/> <div v-if="$slots.footer">
</template> <slot name="footer"></slot>
</div>
<template v-slot:footer> <div v-else>
<ms-dialog-footer @cancel="close" @confirm="save"/> <ms-dialog-footer @cancel="close" @confirm="save"/>
</div>
</template> </template>
</relevance-dialog> </relevance-dialog>
@ -75,17 +76,15 @@
}, },
getProject() { getProject() {
if (this.planId) { this.result = this.$get("/project/listAll", res => {
this.result = this.$post("/test/plan/project/", {planId: this.planId}, res => { let data = res.data;
let data = res.data; if (data) {
if (data) { this.projects = data;
this.projects = data; this.projectId = data[0].id;
this.projectId = data[0].id; this.projectName = data[0].name;
this.projectName = data[0].name; this.changeProject(data[0]);
this.changeProject(data[0]); }
} })
})
}
}, },
changeProject(project) { changeProject(project) {