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

View File

@ -45,9 +45,11 @@
<el-tabs v-model="activeName"> <el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_test.request.parameters')" name="parameters"> <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" :environment="request.environment"
:scenario="scenario" :scenario="scenario"
:extract="request.extract"
:description="$t('api_test.request.parameters_desc')"/> :description="$t('api_test.request.parameters_desc')"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('api_test.request.headers')" name="headers"> <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", please_save_test: "Please Save Test First",
}, },
scenario: { scenario: {
scenario: "Scenario",
dubbo: "Dubbo Config", dubbo: "Dubbo Config",
config: "Scenario Config", config: "Scenario Config",
input_name: "Please enter the scenario name", input_name: "Please enter the scenario name",
@ -402,7 +403,8 @@ export default {
parameters_filter_tips: "Only support MockJs function result preview", parameters_filter_tips: "Only support MockJs function result preview",
parameters_advance: "Advanced parameter settings", parameters_advance: "Advanced parameter settings",
parameters_preview: "Preview", 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_mock: "Mock Data",
parameters_advance_add_func: "Add Function", parameters_advance_add_func: "Add Function",
parameters_advance_add_func_limit: "Support up to 5 functions", parameters_advance_add_func_limit: "Support up to 5 functions",

View File

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

View File

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