Merge branch 'dev' of https://github.com/fit2cloudrd/metersphere-server into dev
This commit is contained in:
commit
6816c13272
|
@ -1,5 +1,7 @@
|
||||||
package io.metersphere.engine;
|
package io.metersphere.engine;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public interface Engine {
|
public interface Engine {
|
||||||
Long getStartTime();
|
Long getStartTime();
|
||||||
|
|
||||||
|
@ -8,4 +10,6 @@ public interface Engine {
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
|
Map<String, String> log();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,9 @@ import io.metersphere.engine.docker.request.TestRequest;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class DockerTestEngine extends AbstractEngine {
|
public class DockerTestEngine extends AbstractEngine {
|
||||||
|
@ -98,7 +100,22 @@ public class DockerTestEngine extends AbstractEngine {
|
||||||
String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port);
|
String uri = String.format(BASE_URL + "/jmeter/container/stop/" + testId, ip, port);
|
||||||
restTemplate.postForObject(uri, request, String.class);
|
restTemplate.postForObject(uri, request, String.class);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> log() {
|
||||||
|
String testId = loadTest.getId();
|
||||||
|
Map<String, String> logs = new HashMap<>();
|
||||||
|
BaseRequest request = new BaseRequest();
|
||||||
|
this.resourceList.forEach(r -> {
|
||||||
|
NodeDTO node = JSON.parseObject(r.getConfiguration(), NodeDTO.class);
|
||||||
|
String ip = node.getIp();
|
||||||
|
Integer port = node.getPort();
|
||||||
|
|
||||||
|
String uri = String.format(BASE_URL + "/jmeter/container/log/" + testId, ip, port);
|
||||||
|
String log = restTemplate.postForObject(uri, request, String.class);
|
||||||
|
logs.put(node.getIp(), log);
|
||||||
|
});
|
||||||
|
return logs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import io.metersphere.i18n.Translator;
|
||||||
import org.apache.commons.collections.MapUtils;
|
import org.apache.commons.collections.MapUtils;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class KubernetesTestEngine extends AbstractEngine {
|
public class KubernetesTestEngine extends AbstractEngine {
|
||||||
|
|
||||||
|
@ -122,4 +123,23 @@ public class KubernetesTestEngine extends AbstractEngine {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> log() {
|
||||||
|
Map<String, String> logs = new HashMap<>();
|
||||||
|
resourceList.forEach(r -> {
|
||||||
|
try {
|
||||||
|
String configuration = r.getConfiguration();
|
||||||
|
ClientCredential clientCredential = JSON.parseObject(configuration, ClientCredential.class);
|
||||||
|
KubernetesProvider provider = new KubernetesProvider(JSON.toJSONString(clientCredential));
|
||||||
|
provider.confirmNamespace(loadTest.getProjectId());
|
||||||
|
String joblog = provider.getKubernetesClient().batch().jobs().inNamespace(loadTest.getProjectId()).withName("job-" + loadTest.getId()).getLog();
|
||||||
|
logs.put(clientCredential.getMasterUrl(), joblog);
|
||||||
|
} catch (Exception e) {
|
||||||
|
MSException.throwException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return logs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,174 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="container">
|
|
||||||
<div class="main-content">
|
|
||||||
<el-card>
|
|
||||||
<el-container class="scenario-container">
|
|
||||||
<el-header>
|
|
||||||
<span class="scenario-title">场景配置</span>
|
|
||||||
</el-header>
|
|
||||||
|
|
||||||
<el-container>
|
|
||||||
<el-aside class="scenario-aside">
|
|
||||||
<div class="scenario-list">
|
|
||||||
<ms-api-collapse v-model="activeName" @change="handleChange" accordion>
|
|
||||||
<ms-api-collapse-item v-for="(scenario, index) in scenarios" :key="index"
|
|
||||||
:title="scenario.name" :name="index">
|
|
||||||
<template slot="title">
|
|
||||||
<div class="scenario-name">{{scenario.name}}</div>
|
|
||||||
<el-dropdown trigger="click" @command="handleCommand">
|
|
||||||
<span class="el-dropdown-link el-icon-more scenario-btn"/>
|
|
||||||
<el-dropdown-menu slot="dropdown">
|
|
||||||
<el-dropdown-item :command="{type:'delete', index:index}">删除场景</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</el-dropdown>
|
|
||||||
</template>
|
|
||||||
<ms-api-request :requests="scenario.requests" :open="select"/>
|
|
||||||
</ms-api-collapse-item>
|
|
||||||
</ms-api-collapse>
|
|
||||||
</div>
|
|
||||||
<el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain
|
|
||||||
@click="createScenario"/>
|
|
||||||
</el-aside>
|
|
||||||
|
|
||||||
<el-main class="scenario-main">
|
|
||||||
<div class="scenario-form">
|
|
||||||
<ms-api-scenario-form :scenario="selected" v-if="isScenario"></ms-api-scenario-form>
|
|
||||||
<ms-api-request-form :request="selected" v-if="isRequest"></ms-api-request-form>
|
|
||||||
</div>
|
|
||||||
</el-main>
|
|
||||||
</el-container>
|
|
||||||
</el-container>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import MsApiCollapseItem from "./components/ApiCollapseItem";
|
|
||||||
import MsApiCollapse from "./components/ApiCollapse";
|
|
||||||
import MsApiRequest from "./components/ApiRequest";
|
|
||||||
import MsApiRequestForm from "./components/ApiRequestForm";
|
|
||||||
import MsApiScenarioForm from "./components/ApiScenarioForm";
|
|
||||||
import {Scenario, Request} from "./model/APIModel";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "MsApiScenarioConfig",
|
|
||||||
|
|
||||||
components: {MsApiScenarioForm, MsApiRequestForm, MsApiRequest, MsApiCollapse, MsApiCollapseItem},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
activeName: 0,
|
|
||||||
scenarios: [],
|
|
||||||
selected: Object
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
createScenario: function () {
|
|
||||||
let scenario = new Scenario({name: "Scenario"});
|
|
||||||
this.scenarios.push(scenario);
|
|
||||||
},
|
|
||||||
deleteScenario: function (index) {
|
|
||||||
this.scenarios.splice(index, 1);
|
|
||||||
if (this.scenarios.length === 0) {
|
|
||||||
this.createScenario();
|
|
||||||
this.select(this.scenarios[0]);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
handleChange: function (index) {
|
|
||||||
this.select(this.scenarios[index]);
|
|
||||||
},
|
|
||||||
handleCommand: function (command) {
|
|
||||||
switch (command.type) {
|
|
||||||
case "delete":
|
|
||||||
this.deleteScenario(command.index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
select: function (obj) {
|
|
||||||
this.selected = obj;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
isScenario() {
|
|
||||||
return this.selected instanceof Scenario;
|
|
||||||
},
|
|
||||||
isRequest() {
|
|
||||||
return this.selected instanceof Request;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
created() {
|
|
||||||
if (this.scenarios.length === 0) {
|
|
||||||
this.createScenario();
|
|
||||||
this.select(this.scenarios[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.scenario-container {
|
|
||||||
height: calc(100vh - 150px);
|
|
||||||
min-height: 600px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-title {
|
|
||||||
font-size: 16px;
|
|
||||||
margin-left: -20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-aside {
|
|
||||||
position: relative;
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #EBEEF5;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-list {
|
|
||||||
overflow-y: auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 28px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-name {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: 14px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-btn {
|
|
||||||
text-align: center;
|
|
||||||
padding: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-create {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-main {
|
|
||||||
position: relative;
|
|
||||||
margin-left: 20px;
|
|
||||||
border: 1px solid #EBEEF5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.scenario-form {
|
|
||||||
padding: 20px;
|
|
||||||
overflow-y: auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
bottom: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
<template>
|
||||||
|
<div class="container">
|
||||||
|
<div class="main-content">
|
||||||
|
<el-card>
|
||||||
|
<el-container class="test-container" v-loading="result.loading">
|
||||||
|
<el-header>
|
||||||
|
<el-row type="flex" align="middle">
|
||||||
|
<el-input class="test-name" v-model="test.name" maxlength="64" :placeholder="$t('api_test.input_name')">
|
||||||
|
<el-select class="test-project" v-model="test.projectId" slot="prepend"
|
||||||
|
:placeholder="$t('api_test.select_project')">
|
||||||
|
<el-option v-for="item in projects" :key="item.id" :label="item.name" :value="item.id"/>
|
||||||
|
</el-select>
|
||||||
|
</el-input>
|
||||||
|
<el-button type="primary" plain :disabled="isDisabled" @click="saveTest">保存</el-button>
|
||||||
|
</el-row>
|
||||||
|
</el-header>
|
||||||
|
<ms-api-scenario-config :scenarios="test.scenario_definition"/>
|
||||||
|
</el-container>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MsApiTestConfig",
|
||||||
|
|
||||||
|
components: {MsApiScenarioConfig},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
result: {},
|
||||||
|
projects: [],
|
||||||
|
change: false,
|
||||||
|
test: {
|
||||||
|
projectId: null,
|
||||||
|
name: null,
|
||||||
|
scenario_definition: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
test: {
|
||||||
|
handler: function () {
|
||||||
|
this.change = true;
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
saveTest: function () {
|
||||||
|
|
||||||
|
this.change = false;
|
||||||
|
this.$message({
|
||||||
|
message: this.$t('commons.save_success'),
|
||||||
|
type: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isDisabled() {
|
||||||
|
return !(this.test.projectId && this.test.name && this.change)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.result = this.$get("/project/listAll", response => {
|
||||||
|
this.projects = response.data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.test-container {
|
||||||
|
height: calc(100vh - 150px);
|
||||||
|
min-height: 600px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-name {
|
||||||
|
width: 600px;
|
||||||
|
margin-left: -20px;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-project {
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-row :gutter="10" type="flex" align="middle">
|
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
|
||||||
<el-col :span="4">
|
<el-col class="assertion-select">
|
||||||
<el-select class="assertion-item" v-model="regex.subject" size="small"
|
<el-select class="assertion-item" v-model="regex.subject" size="small"
|
||||||
:placeholder="$t('api_test.request.assertions.select_subject')">
|
:placeholder="$t('api_test.request.assertions.select_subject')">
|
||||||
<el-option label="Http-Code" value="HTTP-CODE"></el-option>
|
<el-option label="Http-Code" value="HTTP-CODE"></el-option>
|
||||||
|
@ -9,11 +9,11 @@
|
||||||
<el-option label="Body" value="BODY"></el-option>
|
<el-option label="Body" value="BODY"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="19">
|
<el-col>
|
||||||
<el-input v-model="regex.expression" maxlength="255" size="small" show-word-limit
|
<el-input v-model="regex.expression" maxlength="255" size="small" show-word-limit
|
||||||
:placeholder="$t('api_test.request.assertions.expression')"/>
|
:placeholder="$t('api_test.request.assertions.expression')"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1" class="assertion-btn">
|
<el-col class="assertion-btn">
|
||||||
<el-button type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
<el-button type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
||||||
<el-button type="primary" size="small" icon="el-icon-plus" plain @click="add" v-else/>
|
<el-button type="primary" size="small" icon="el-icon-plus" plain @click="add" v-else/>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Regex} from "../model/APIModel";
|
import {Regex} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiAssertionRegex",
|
name: "MsApiAssertionRegex",
|
||||||
|
@ -54,11 +54,16 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.assertion-select {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
.assertion-item {
|
.assertion-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.assertion-btn {
|
.assertion-btn {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
width: 60px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-row :gutter="10" align="middle">
|
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
|
||||||
<el-col :span="23">
|
<el-col>
|
||||||
<el-input v-model="time" step="100" size="small" type="number"
|
<el-input v-model="time" step="100" size="small" type="number"
|
||||||
:placeholder="$t('api_test.request.assertions.response_in_time')"/>
|
:placeholder="$t('api_test.request.assertions.response_in_time')"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1" class="assertion-btn">
|
<el-col class="assertion-btn">
|
||||||
<el-button type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
<el-button type="danger" size="mini" icon="el-icon-delete" circle @click="remove" v-if="edit"/>
|
||||||
<el-button type="primary" size="small" icon="el-icon-plus" plain @click="add" v-else/>
|
<el-button type="primary" size="small" icon="el-icon-plus" plain @click="add" v-else/>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {ResponseTime} from "../model/APIModel";
|
import {ResponseTime} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiAssertionResponseTime",
|
name: "MsApiAssertionResponseTime",
|
||||||
|
@ -47,5 +47,6 @@
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.assertion-btn {
|
.assertion-btn {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
width: 60px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<el-row :gutter="10">
|
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
|
||||||
<el-col :span="4">
|
<el-col class="assertion-select">
|
||||||
<el-select class="assertion-item" v-model="subject" size="small"
|
<el-select class="assertion-item" v-model="subject" size="small"
|
||||||
:placeholder="$t('api_test.request.assertions.select_subject')">
|
:placeholder="$t('api_test.request.assertions.select_subject')">
|
||||||
<el-option label="Http-Code" value="HTTP-CODE"></el-option>
|
<el-option label="Http-Code" value="HTTP-CODE"></el-option>
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<el-option label="Body" value="BODY"></el-option>
|
<el-option label="Body" value="BODY"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="4">
|
<el-col class="assertion-select">
|
||||||
<el-select class="assertion-item" v-model="condition" size="small"
|
<el-select class="assertion-item" v-model="condition" size="small"
|
||||||
:placeholder="$t('api_test.request.assertions.select_contains')">
|
:placeholder="$t('api_test.request.assertions.select_contains')">
|
||||||
<el-option :label="$t('api_test.request.assertions.contains')" value="CONTAINS"></el-option>
|
<el-option :label="$t('api_test.request.assertions.contains')" value="CONTAINS"></el-option>
|
||||||
|
@ -19,11 +19,11 @@
|
||||||
<el-option :label="$t('api_test.request.assertions.end_with')" value="END_WITH"></el-option>
|
<el-option :label="$t('api_test.request.assertions.end_with')" value="END_WITH"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="15">
|
<el-col>
|
||||||
<el-input v-model="value" maxlength="255" size="small" show-word-limit
|
<el-input v-model="value" maxlength="255" size="small" show-word-limit
|
||||||
:placeholder="$t('api_test.request.assertions.value')"/>
|
:placeholder="$t('api_test.request.assertions.value')"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1">
|
<el-col class="assertion-btn">
|
||||||
<el-button type="primary" size="small" icon="el-icon-plus" plain @click="add"/>
|
<el-button type="primary" size="small" icon="el-icon-plus" plain @click="add"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Regex} from "../model/APIModel";
|
import {Regex} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiAssertionText",
|
name: "MsApiAssertionText",
|
||||||
|
@ -89,7 +89,15 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
.assertion-select {
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
.assertion-item {
|
.assertion-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.assertion-btn {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
import MsApiAssertionText from "./ApiAssertionText";
|
import MsApiAssertionText from "./ApiAssertionText";
|
||||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||||
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
||||||
import {ASSERTION_TYPE, Assertions, Regex} from "../model/APIModel";
|
import {ASSERTION_TYPE, Assertions, Regex} from "../model/ScenarioModel";
|
||||||
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
|
import MsApiAssertionsEdit from "./ApiAssertionsEdit";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<script>
|
<script>
|
||||||
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
import MsApiAssertionRegex from "./ApiAssertionRegex";
|
||||||
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
import MsApiAssertionResponseTime from "./ApiAssertionResponseTime";
|
||||||
import {Assertions} from "../model/APIModel";
|
import {Assertions} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiAssertionsEdit",
|
name: "MsApiAssertionsEdit",
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import MsApiKeyValue from "./ApiKeyValue";
|
import MsApiKeyValue from "./ApiKeyValue";
|
||||||
import {Body, BODY_TYPE} from "../model/APIModel";
|
import {Body, BODY_TYPE} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiBody",
|
name: "MsApiBody",
|
||||||
|
|
|
@ -5,13 +5,13 @@
|
||||||
</span>
|
</span>
|
||||||
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
<div class="kv-row" v-for="(item, index) in items" :key="index">
|
||||||
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
|
||||||
<el-col :span="11">
|
<el-col>
|
||||||
<el-input v-model="item.key" placeholder="Key" size="small" maxlength="100" @change="check"/>
|
<el-input v-model="item.key" placeholder="Key" size="small" maxlength="100" @change="check"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="11">
|
<el-col>
|
||||||
<el-input v-model="item.value" placeholder="Value" size="small" maxlength="100" @change="check"/>
|
<el-input v-model="item.value" placeholder="Value" size="small" maxlength="100" @change="check"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1">
|
<el-col class="kv-delete">
|
||||||
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
<el-button size="mini" class="el-icon-delete-solid" circle @click="remove(index)"
|
||||||
:disabled="isDisable(index)"/>
|
:disabled="isDisable(index)"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {KeyValue} from "../model/APIModel";
|
import {KeyValue} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiKeyValue",
|
name: "MsApiKeyValue",
|
||||||
|
@ -83,4 +83,8 @@
|
||||||
.kv-row {
|
.kv-row {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kv-delete {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -25,10 +25,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {Request} from "../model/APIModel";
|
import {Request} from "../model/ScenarioModel";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MsApiRequest",
|
name: "MsApiRequestConfig",
|
||||||
props: {
|
props: {
|
||||||
requests: Array,
|
requests: Array,
|
||||||
open: Function
|
open: Function
|
|
@ -56,7 +56,6 @@
|
||||||
activeName: "parameters",
|
activeName: "parameters",
|
||||||
rules: {
|
rules: {
|
||||||
name: [
|
name: [
|
||||||
{required: true, message: this.$t('api_test.scenario.input_name'), trigger: 'blur'},
|
|
||||||
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
||||||
],
|
],
|
||||||
url: [
|
url: [
|
||||||
|
|
|
@ -0,0 +1,161 @@
|
||||||
|
<template>
|
||||||
|
<el-container>
|
||||||
|
<el-aside class="scenario-aside">
|
||||||
|
<div class="scenario-list">
|
||||||
|
<ms-api-collapse v-model="activeName" @change="handleChange" accordion>
|
||||||
|
<ms-api-collapse-item v-for="(scenario, index) in scenarios" :key="index"
|
||||||
|
:title="scenario.name" :name="index">
|
||||||
|
<template slot="title">
|
||||||
|
<div class="scenario-name">{{scenario.name}}</div>
|
||||||
|
<el-dropdown trigger="click" @command="handleCommand">
|
||||||
|
<span class="el-dropdown-link el-icon-more scenario-btn"/>
|
||||||
|
<el-dropdown-menu slot="dropdown">
|
||||||
|
<el-dropdown-item :command="{type:'delete', index:index}">删除场景</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</el-dropdown>
|
||||||
|
</template>
|
||||||
|
<ms-api-request-config :requests="scenario.requests" :open="select"/>
|
||||||
|
</ms-api-collapse-item>
|
||||||
|
</ms-api-collapse>
|
||||||
|
</div>
|
||||||
|
<el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain
|
||||||
|
@click="createScenario"/>
|
||||||
|
</el-aside>
|
||||||
|
|
||||||
|
<el-main class="scenario-main">
|
||||||
|
<div class="scenario-form">
|
||||||
|
<ms-api-scenario-form :scenario="selected" v-if="isScenario"></ms-api-scenario-form>
|
||||||
|
<ms-api-request-form :request="selected" v-if="isRequest"></ms-api-request-form>
|
||||||
|
</div>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import MsApiCollapseItem from "./ApiCollapseItem";
|
||||||
|
import MsApiCollapse from "./ApiCollapse";
|
||||||
|
import MsApiRequestConfig from "./ApiRequestConfig";
|
||||||
|
import MsApiRequestForm from "./ApiRequestForm";
|
||||||
|
import MsApiScenarioForm from "./ApiScenarioForm";
|
||||||
|
import {Scenario, Request} from "../model/ScenarioModel";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "MsApiScenarioConfig",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
MsApiRequestConfig,
|
||||||
|
MsApiScenarioForm,
|
||||||
|
MsApiRequestForm,
|
||||||
|
MsApiCollapse,
|
||||||
|
MsApiCollapseItem
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
scenarios: Array
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeName: 0,
|
||||||
|
selected: Object
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
createScenario: function () {
|
||||||
|
let scenario = new Scenario({name: "Scenario"});
|
||||||
|
this.scenarios.push(scenario);
|
||||||
|
},
|
||||||
|
deleteScenario: function (index) {
|
||||||
|
this.scenarios.splice(index, 1);
|
||||||
|
if (this.scenarios.length === 0) {
|
||||||
|
this.createScenario();
|
||||||
|
this.select(this.scenarios[0]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleChange: function (index) {
|
||||||
|
this.select(this.scenarios[index]);
|
||||||
|
},
|
||||||
|
handleCommand: function (command) {
|
||||||
|
switch (command.type) {
|
||||||
|
case "delete":
|
||||||
|
this.deleteScenario(command.index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
select: function (obj) {
|
||||||
|
this.selected = obj;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isScenario() {
|
||||||
|
return this.selected instanceof Scenario;
|
||||||
|
},
|
||||||
|
isRequest() {
|
||||||
|
return this.selected instanceof Request;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
if (this.scenarios.length === 0) {
|
||||||
|
this.createScenario();
|
||||||
|
this.select(this.scenarios[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.scenario-aside {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #EBEEF5;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-list {
|
||||||
|
overflow-y: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-name {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-btn {
|
||||||
|
text-align: center;
|
||||||
|
padding: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-create {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-main {
|
||||||
|
position: relative;
|
||||||
|
margin-left: 20px;
|
||||||
|
border: 1px solid #EBEEF5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-form {
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -34,7 +34,6 @@
|
||||||
activeName: "variables",
|
activeName: "variables",
|
||||||
rules: {
|
rules: {
|
||||||
name: [
|
name: [
|
||||||
{required: true, message: this.$t('api_test.scenario.input_name'), trigger: 'blur'},
|
|
||||||
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
{max: 100, message: this.$t('commons.input_limit', [0, 100]), trigger: 'blur'}
|
||||||
],
|
],
|
||||||
url: [
|
url: [
|
||||||
|
|
|
@ -18,7 +18,7 @@ import PerformanceTestReport from "../../performance/report/PerformanceTestRepor
|
||||||
import ApiTestReport from "../../api/report/ApiTestReport";
|
import ApiTestReport from "../../api/report/ApiTestReport";
|
||||||
import ApiTest from "../../api/ApiTest";
|
import ApiTest from "../../api/ApiTest";
|
||||||
import PerformanceTest from "../../performance/PerformanceTest";
|
import PerformanceTest from "../../performance/PerformanceTest";
|
||||||
import ApiScenarioConfig from "../../api/test/ApiScenarioConfig";
|
import ApiTestConfig from "../../api/test/ApiTestConfig";
|
||||||
import PerformanceTestHome from "../../performance/home/PerformanceTestHome";
|
import PerformanceTestHome from "../../performance/home/PerformanceTestHome";
|
||||||
import ApiTestList from "../../api/test/ApiTestList";
|
import ApiTestList from "../../api/test/ApiTestList";
|
||||||
import ApiTestHome from "../../api/home/ApiTestHome";
|
import ApiTestHome from "../../api/home/ApiTestHome";
|
||||||
|
@ -97,12 +97,12 @@ const router = new VueRouter({
|
||||||
{
|
{
|
||||||
path: 'test/create',
|
path: 'test/create',
|
||||||
name: "createAPITest",
|
name: "createAPITest",
|
||||||
component: ApiScenarioConfig,
|
component: ApiTestConfig,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "test/edit/:testId",
|
path: "test/edit/:testId",
|
||||||
name: "editAPITest",
|
name: "editAPITest",
|
||||||
component: ApiScenarioConfig,
|
component: ApiTestConfig,
|
||||||
props: {
|
props: {
|
||||||
content: (route) => {
|
content: (route) => {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -169,6 +169,8 @@ export default {
|
||||||
'resource_pool_is_null': '资源池为空',
|
'resource_pool_is_null': '资源池为空',
|
||||||
},
|
},
|
||||||
api_test: {
|
api_test: {
|
||||||
|
input_name: "请输入测试名称",
|
||||||
|
select_project: "请选择项目",
|
||||||
scenario: {
|
scenario: {
|
||||||
input_name: "请输入场景名称",
|
input_name: "请输入场景名称",
|
||||||
name: "场景名称",
|
name: "场景名称",
|
||||||
|
|
Loading…
Reference in New Issue