feat(接口测试): 处理变量

This commit is contained in:
Captain.B 2020-08-13 18:48:11 +08:00
parent 01e22c9f1a
commit b63c136fb5
5 changed files with 140 additions and 61 deletions

View File

@ -3,7 +3,7 @@
<span class="kv-description" v-if="description">
{{ description }}
</span>
<div class="kv-row" v-for="(item, index) in items" :key="index">
<div class="kv-row" v-for="(item, index) in parameters" :key="index">
<el-row type="flex" :gutter="20" justify="space-between" align="middle">
<el-col>
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
@ -39,23 +39,19 @@
:visible.sync="itemValueVisible"
class="advanced-item-value"
width="70%">
<el-tabs tab-position="top" style="height: 50vh;">
<el-tabs tab-position="top" style="height: 50vh;" @tab-click="selectTab">
<el-tab-pane :label="$t('api_test.request.parameters_advance_mock')">
<el-row type="flex" :gutter="20" style="overflow-x: auto;">
<el-col :span="6">
<el-autocomplete
:disabled="isReadOnly"
size="small"
class="input-with-autocomplete"
v-model="itemValue"
:fetch-suggestions="funcSearch"
:placeholder="valueText"
value-key="name"
highlight-first-item
@select="change">
</el-autocomplete>
<el-row type="flex" :gutter="20">
<el-col :span="6" class="col-height">
<div>
<el-input size="small" v-model="filterText"
:placeholder="$t('api_test.request.parameters_mock_filter_tips')"/>
<el-tree class="filter-tree" ref="tree" :data="mockFuncs" :props="treeProps"
default-expand-all @node-click="selectVariable"
:filter-node-method="filterNode"></el-tree>
</div>
</el-col>
<el-col :span="6" v-for="(itemFunc, itemIndex) in itemFuncs" :key="itemIndex">
<el-col :span="6" v-for="(itemFunc, itemIndex) in mockVariableFuncs" :key="itemIndex">
<div v-for="(func, funcIndex) in funcs"
:key="`${itemIndex}-${funcIndex}`">
<el-row>
@ -75,28 +71,50 @@
</el-tab-pane>
<el-tab-pane label="变量">
<el-row>
<el-col :span="6">
<el-col :span="6" class="col-height">
<div v-if="environment">
<el-tree :data="environmentParams" :props="{ children: 'children', label: 'name'}"></el-tree>
<p>{{ $t('api_test.environment.environment') }}</p>
<el-tree :data="environmentParams" :props="treeProps" @node-click="selectVariable"></el-tree>
</div>
<div v-if="scenario">
<el-tree :data="scenarioParams" :props="{ children: 'children', label: 'name'}"></el-tree>
<p>{{ $t('api_test.scenario.scenario') }}</p>
<el-tree :data="scenarioParams" :props="treeProps" @node-click="selectVariable"></el-tree>
</div>
<div v-if="preRequestParams">
<p>{{ $t('api_test.request.parameters_pre_request') }}</p>
<el-tree :data="preRequestParams" :props="treeProps" @node-click="selectVariable"></el-tree>
</div>
</el-col>
<el-col :span="6" v-for="(itemFunc, itemIndex) in jmeterVariableFuncs" :key="itemIndex" class="col-height">
<div>
<div v-for="(func, funcIndex) in jmeterFuncs"
:key="`${itemIndex}-${funcIndex}`">
<el-row>
<el-radio size="mini" v-model="itemFunc.name" :label="func.name"
@change="methodChange(itemFunc, func)"/>
</el-row>
</div>
</div>
</el-col>
</el-row>
</el-tab-pane>
</el-tabs>
<el-form>
<el-form-item>
<el-input :placeholder="valueText" size="small"
v-model="itemValue"/>
</el-form-item>
</el-form>
<div style="padding-top: 10px;">
<el-row type="flex" align="middle">
<el-col :span="12">
<el-button size="small" type="primary" plain @click="saveAdvanced()">
{{ $t('commons.save') }}
</el-button>
<el-button size="small" type="info" plain @click="addFunc()">
<el-button size="small" type="info" plain @click="addFunc()" v-if="currentTab === 0">
{{ $t('api_test.request.parameters_advance_add_func') }}
</el-button>
<el-button size="small" type="success" plain @click="showPreview()">
<el-button size="small" type="success" plain @click="showPreview()" v-if="currentTab === 0">
{{ $t('api_test.request.parameters_preview') }}
</el-button>
</el-col>
@ -110,8 +128,8 @@
</template>
<script>
import {KeyValue, Scenario} from "../model/ScenarioModel";
import {MOCKJS_FUNC} from "@/common/js/constants";
import {HttpRequest, KeyValue, Scenario} from "../model/ScenarioModel";
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
import {calculate} from "@/business/components/api/test/model/ScenarioModel";
export default {
@ -121,7 +139,7 @@ export default {
keyPlaceholder: String,
valuePlaceholder: String,
description: String,
items: Array,
request: HttpRequest,
environment: Object,
scenario: Scenario,
isReadOnly: {
@ -131,27 +149,53 @@ export default {
suggestions: Array
},
mounted() {
if (this.request) {
this.parameters = this.request.parameters;
}
if (this.scenario) {
let variables = this.scenario.variables;
this.scenarioParams = [
{
name: this.scenario.name,
children: variables.filter(v => v.name),
children: variables.filter(v => v.name).map(v => {
return {name: v.name, value: '${' + v.name + '}'}
}),
}
];
}
if (this.environment) {
let variables = JSON.parse(this.environment.variables);
this.environmentParams = [
{
name: this.environment.name,
children: variables.filter(v => v.name),
if (this.environment) {
let variables = JSON.parse(this.environment.variables);
this.environmentParams = [
{
name: this.environment.name,
children: variables.filter(v => v.name).map(v => {
return {name: v.name, value: '${' + v.name + '}'}
}),
}
];
}
let i = this.scenario.requests.indexOf(this.request);
this.preRequests = this.scenario.requests.slice(0, i);
this.preRequests.forEach(r => {
let js = r.extract.json.map(v => {
return {name: v.variable, value: v.value}
});
let xs = r.extract.xpath.map(v => {
return {name: v.variable, value: v.value}
});
let rx = r.extract.regex.map(v => {
return {name: v.variable, value: v.value}
});
let vs = [...js, ...xs, ...rx];
if (vs.length > 0) {
this.preRequestParams.push({name: r.name, children: vs});
}
];
});
}
},
data() {
return {
filterText: '',
itemValueVisible: false,
itemValue: null,
funcs: [
@ -178,14 +222,26 @@ export default {
{name: "number"}
],
itemValuePreview: null,
itemFuncs: [],
currentFunc: "",
mockFuncs: MOCKJS_FUNC,
mockVariableFuncs: [],
jmeterVariableFuncs: [],
currentTab: 0,
mockFuncs: MOCKJS_FUNC.map(f => {
return {name: f.name, value: f.name}
}),
jmeterFuncs: JMETER_FUNC,
environmentParams: [],
scenarioParams: [],
parameters: [],
preRequests: [],
preRequestParams: [],
treeProps: {children: 'children', label: 'name'}
}
},
watch: {
filterText(val) {
this.$refs.tree.filter(val);
}
},
computed: {
keyText() {
return this.keyPlaceholder || this.$t("api_test.key");
@ -197,16 +253,16 @@ export default {
methods: {
remove: function (index) {
this.items.splice(index, 1);
this.$emit('change', this.items);
this.parameters.splice(index, 1);
this.$emit('change', this.parameters);
},
change: function () {
let isNeedCreate = true;
let removeIndex = -1;
this.items.forEach((item, index) => {
this.parameters.forEach((item, index) => {
if (!item.name && !item.value) {
//
if (index !== this.items.length - 1) {
if (index !== this.parameters.length - 1) {
removeIndex = index;
}
//
@ -214,13 +270,13 @@ export default {
}
});
if (isNeedCreate) {
this.items.push(new KeyValue());
this.parameters.push(new KeyValue());
}
this.$emit('change', this.items);
this.$emit('change', this.parameters);
// TODO key
},
isDisable: function (index) {
return this.items.length - 1 === index;
return this.parameters.length - 1 === index;
},
querySearch(queryString, cb) {
let suggestions = this.suggestions;
@ -243,6 +299,17 @@ export default {
return (func.name.toLowerCase().indexOf(queryString.toLowerCase()) > -1);
};
},
filterNode(value, data) {
if (!value) return true;
return data.name.indexOf(value) !== -1;
},
selectVariable(node) {
this.itemValue = node.value;
},
selectTab(tab) {
this.currentTab = +tab.index;
this.itemValue = null;
},
showPreview() {
//
if (!this.itemValue) {
@ -253,7 +320,7 @@ export default {
this.itemValue = this.itemValue.substring(0, index).trim();
}
this.itemFuncs.forEach(f => {
this.mockVariableFuncs.forEach(f => {
if (!f.name) {
return;
}
@ -266,19 +333,19 @@ export default {
this.itemValuePreview = calculate(this.itemValue);
},
methodChange(itemFunc, func) {
let index = this.itemFuncs.indexOf(itemFunc);
this.itemFuncs = this.itemFuncs.slice(0, index);
let index = this.mockVariableFuncs.indexOf(itemFunc);
this.mockVariableFuncs = this.mockVariableFuncs.slice(0, index);
// deep copy
this.itemFuncs.push(JSON.parse(JSON.stringify(func)));
this.mockVariableFuncs.push(JSON.parse(JSON.stringify(func)));
this.showPreview();
},
addFunc() {
if (this.itemFuncs.length > 4) {
if (this.mockVariableFuncs.length > 4) {
this.$info(this.$t('api_test.request.parameters_advance_add_func_limit'));
return;
}
if (this.itemFuncs.length > 0) {
let func = this.itemFuncs[this.itemFuncs.length - 1];
if (this.mockVariableFuncs.length > 0) {
let func = this.mockVariableFuncs[this.mockVariableFuncs.length - 1];
if (!func.name) {
this.$warning(this.$t('api_test.request.parameters_advance_add_func_error'));
return;
@ -292,24 +359,24 @@ export default {
}
}
}
this.itemFuncs.push({name: '', params: []});
this.mockVariableFuncs.push({name: '', params: []});
},
advanced(item) {
this.currentItem = item;
this.itemValueVisible = true;
this.itemValue = '';
this.itemValuePreview = null;
this.itemFuncs = [];
this.mockVariableFuncs = [];
},
saveAdvanced() {
this.currentItem.value = this.itemValue;
this.itemValueVisible = false;
this.itemFuncs = [];
this.mockVariableFuncs = [];
}
},
created() {
if (this.items.length === 0) {
this.items.push(new KeyValue());
if (this.parameters.length === 0) {
this.parameters.push(new KeyValue());
}
}
}
@ -340,4 +407,8 @@ export default {
margin-bottom: 5px;
}
.col-height {
height: 40vh;
overflow: auto;
}
</style>

View File

@ -45,9 +45,11 @@
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters">
<ms-api-variable :is-read-only="isReadOnly" :items="request.parameters"
<ms-api-variable :is-read-only="isReadOnly"
:request="request"
:environment="request.environment"
:scenario="scenario"
:extract="request.extract"
:description="$t('api_test.request.parameters_desc')"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">

View File

@ -368,6 +368,7 @@ export default {
please_save_test: "Please Save Test First",
},
scenario: {
scenario: "Scenario",
dubbo: "Dubbo Config",
config: "Scenario Config",
input_name: "Please enter the scenario name",
@ -402,7 +403,8 @@ export default {
parameters_filter_tips: "Only support MockJs function result preview",
parameters_advance: "Advanced parameter settings",
parameters_preview: "Preview",
parameters_preview_warning: "Please enter the template first",
parameters_mock_filter_tips: "Please enter keywords to filter",
parameters_pre_request: "Pre-request extraction",
parameters_advance_mock: "Mock Data",
parameters_advance_add_func: "Add Function",
parameters_advance_add_func_limit: "Support up to 5 functions",

View File

@ -369,6 +369,7 @@ export default {
please_save_test: "请先保存测试",
},
scenario: {
scenario: "场景",
dubbo: "Dubbo配置",
config: "场景配置",
input_name: "请输入场景名称",
@ -405,7 +406,8 @@ export default {
parameters_filter_tips: "只支持 MockJs 函数结果预览",
parameters_advance: "高级参数设置",
parameters_preview: "预览",
parameters_preview_warning: "请先输入模版",
parameters_mock_filter_tips: "请输入关键字进行过滤",
parameters_pre_request: "前置请求提取",
parameters_advance_mock: "Mock 数据",
parameters_advance_add_func: "添加函数",
parameters_advance_add_func_limit: "最多支持5个函数",

View File

@ -367,6 +367,7 @@ export default {
please_save_test: "請先保存測試",
},
scenario: {
scenario: "場景",
dubbo: "Dubbo配寘",
creator: "創建人",
config: "場景配寘",
@ -402,7 +403,8 @@ export default {
parameters_filter_tips: "只支持MockJs函數結果預覽",
parameters_advance: "高級參數設置",
parameters_preview: "預覽",
parameters_preview_warning: "請先輸入模版",
parameters_mock_filter_tips: "請輸入關鍵字進行過濾",
parameters_pre_request: "前置請求提取",
parameters_advance_mock: "Mock 數據",
parameters_advance_add_func: "添加函數",
parameters_advance_add_func_limit: "最多支持5個函數",