feat(接口测试): 接口和场景对应公共部分组件完成优化 (#11092)

Co-authored-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
metersphere-bot 2022-03-01 19:21:05 +08:00 committed by GitHub
parent 8b0a682514
commit 20d9002624
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 894 additions and 379 deletions

View File

@ -220,7 +220,14 @@
@stopScenario="stop"
@setDomain="setDomain"
@openScenario="openScenario"
@editScenarioAdvance="editScenarioAdvance"/>
@editScenarioAdvance="editScenarioAdvance"
v-if="stepFilter.get('ALlSamplerStep').indexOf(data.type) ===-1
|| (!node.parent || !node.parent.data || stepFilter.get('AllSamplerProxy').indexOf(node.parent.data.type) === -1)"/>
<div v-else class="el-tree-node is-hidden is-focusable is-leaf" style="display: none;">
{{ hideNode(node) }}
</div>
</span>
</el-tree>
</div>
@ -594,6 +601,23 @@ export default {
}
},
methods: {
hideNode(node) {
node.isLeaf = true;
node.visible = false;
},
hideTreeNode(node, array) {
let isLeaf = true;
array.forEach(item => {
if (!item.isLeaf && !isLeaf) {
isLeaf = false;
}
if (item.childNodes && item.childNodes.length > 0) {
this.hideTreeNode(item, item.childNodes);
}
})
node.isLeaf = isLeaf;
node.clazzName = '.is-leaf';
},
currentUser: () => {
return getCurrentUser();
},
@ -631,6 +655,9 @@ export default {
} else {
this.$store.state.currentApiCase = {resetDataSource: getUUID()};
}
if (this.$refs.stepTree && this.$refs.stepTree.root && this.$refs.stepTree.root.childNodes) {
this.hideTreeNode(this.$refs.stepTree.root, this.$refs.stepTree.root.childNodes);
}
}
}
})
@ -1503,6 +1530,9 @@ export default {
this.resetResourceId(this.scenarioDefinition);
}
this.$store.state.scenarioMap.set(this.currentScenario.id, 0);
if (this.$refs.stepTree && this.$refs.stepTree.root && this.$refs.stepTree.root.childNodes) {
this.hideTreeNode(this.$refs.stepTree.root, this.$refs.stepTree.root.childNodes);
}
})
}
},
@ -2050,4 +2080,5 @@ export default {
background: white;
z-index: 999;
}
</style>

View File

@ -23,8 +23,9 @@ export function STEP() {
['CustomizeReq', getDefaultSamplerMenu()],
['MaxSamplerProxy', getDefaultSamplerMenu()],
['GenericController', getAll()],
['AllSamplerProxy', ['HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler', 'Sampler', 'AbstractSampler', 'JSR223Processor','API']],
['AllSamplerProxy', ['HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler', 'Sampler', 'AbstractSampler', 'JSR223Processor', 'API']],
['DEFINITION', ['HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler']],
['ALlSamplerStep', ['JSR223Processor', 'JSR223PreProcessor', 'JSR223PostProcessor', 'JDBCPreProcessor', 'JDBCPostProcessor', 'Assertions', 'Extract','ConstantTimer']],
['AllCanExecType', ['HTTPSamplerProxy', 'DubboSampler', 'JDBCSampler', 'TCPSampler', 'JSR223Processor', 'AbstractSampler']]]);
return map
}
@ -74,10 +75,10 @@ export const TYPE_TO_C = new Map([
])
export const PLUGIN_ELEMENTS = new Map([
['menu_post_processors', ['HtmlExtractor', 'JMESPathExtractor', 'JSONPostProcessor', 'RegexExtractor', 'BoundaryExtractor', 'JSR223PostProcessor', 'Separator', 'JDBCPostProcessor', 'XPath2Extractor', 'XPathExtractor', 'ResultAction', 'DebugPostProcessor', 'BeanShellPostProcessor']],
['menu_assertions', ['Assertions', 'Extract', 'Assertion', 'JSONPathAssertion', 'SizeAssertion', 'JSR223Assertion', 'XPath2Assertion', 'Separator', 'HTMLAssertion', 'JMESPathAssertion', 'MD5HexAssertion', 'SMIMEAssertion', 'XMLSchemaAssertion', 'XMLAssertion', 'XPathAssertion', 'DurationAssertion', 'CompareAssertion', 'BeanShellAssertion']],
['menu_post_processors', ['HtmlExtractor', 'JMESPathExtractor', 'JSONPostProcessor', 'RegexExtractor', 'BoundaryExtractor', 'Separator', 'XPath2Extractor', 'XPathExtractor', 'ResultAction', 'DebugPostProcessor', 'BeanShellPostProcessor']],
['menu_assertions', ['JSONPathAssertion', 'SizeAssertion', 'JSR223Assertion', 'XPath2Assertion', 'Separator', 'HTMLAssertion', 'JMESPathAssertion', 'MD5HexAssertion', 'SMIMEAssertion', 'XMLSchemaAssertion', 'XMLAssertion', 'XPathAssertion', 'DurationAssertion', 'CompareAssertion', 'BeanShellAssertion']],
['menu_listener', ['AbstractVisualizer', 'AbstractListener', 'ViewResultsFullVisualizer', 'SummaryReport', 'StatVisualizer', 'BackendListener', 'Separator', 'JSR223Listener', 'ResultSaver', 'RespTimeGraphVisualizer', 'GraphVisualizer', 'AssertionVisualizer', 'ComparisonVisualizer', 'StatGraphVisualizer', 'Summariser', 'TableVisualizer', 'SimpleDataWriter', 'MailerVisualizer', 'BeanShellListener']],
['menu_pre_processors', ['AbstractPostProcessor', 'JSR223PreProcessor', 'UserParameters', 'Separator', 'AnchorModifier', 'URLRewritingModifier', 'JDBCPreProcessor', 'SampleTimeout', 'RegExUserParameters', 'BeanShellPreProcessor']],
['menu_pre_processors', ['AbstractPostProcessor', 'UserParameters', 'Separator', 'AnchorModifier', 'URLRewritingModifier', 'SampleTimeout', 'RegExUserParameters', 'BeanShellPreProcessor']],
['menu_logic_controller', ['GenericController', 'scenario', 'IfController', 'LoopController', 'IfControllerPanel', 'TransactionController', 'LoopControlPanel', 'WhileController', 'Separator', 'ForeachControlPanel', 'IncludeController', 'RunTime', 'CriticalSectionController', 'InterleaveControl', 'OnceOnlyController', 'RecordController', 'LogicController', 'RandomControl', 'RandomOrderController', 'ThroughputController', 'SwitchController', 'ModuleController']],
['menu_fragments', ['TestFragmentController']],
['menu_non_test_elements', ['ProxyControl', 'HttpMirrorControl', 'GenerateTree', 'PropertyControl']],
@ -90,7 +91,6 @@ export const PLUGIN_ELEMENTS = new Map([
export function getDefaultSamplerMenu() {
let array = [];
array = array.concat(PLUGIN_ELEMENTS.get('menu_assertions'));
array = array.concat(PLUGIN_ELEMENTS.get('menu_timer'));
array = array.concat(PLUGIN_ELEMENTS.get('menu_pre_processors'));
array = array.concat(PLUGIN_ELEMENTS.get('menu_post_processors'));
array = array.concat(PLUGIN_ELEMENTS.get('menu_config_element'));

View File

@ -35,9 +35,9 @@
<el-switch v-model="data.enable" class="enable-switch" size="mini" :disabled="data.disabled && !data.root&&!showVersion" style="width: 30px"/>
</el-tooltip>
<slot name="button" v-if="showVersion"></slot>
<el-tooltip content="Copy" placement="top" v-if="showVersion">
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" style="padding: 5px" :disabled="data.disabled && !data.root"/>
</el-tooltip>
<el-button v-if="showVersion" size="mini" icon="el-icon-copy-document" circle @click="copyRow" style="padding: 5px" :disabled="data.disabled && !data.root"/>
<el-button v-if="showVersion && stepFilter.get('ALlSamplerStep').indexOf(data.type) !==-1" size="mini" icon="el-icon-delete" type="danger" style="padding: 5px" circle @click="remove"/>
<step-extend-btns style="display: contents"
:data="data"
:environmentType="environmentType"
@ -46,7 +46,7 @@
@copy="copyRow"
@remove="remove"
@openScenario="openScenario"
v-if="showBtn && (!data.disabled || data.root) &&showVersion"/>
v-if="showBtn && (!data.disabled || data.root) && showVersion && stepFilter.get('ALlSamplerStep').indexOf(data.type) ===-1"/>
</div>
</div>

View File

@ -81,6 +81,7 @@
:scenario-definition="scenarioDefinition"
@editScenarioAdvance="editScenarioAdvance"
:isShowEnable="true"
:response="response"
:referenced="true"
:headers="request.headers "
:is-read-only="isCompReadOnly"
@ -202,7 +203,6 @@ export default {
message: String,
environmentGroupId: String,
environmentType: String,
scenarioDefinition: Array,
ifFromVariableAdvance: {
type: Boolean,
@ -232,6 +232,7 @@ export default {
environmentMap: this.envMap,
isShowNum: false,
isSameSpace: true,
response: {},
}
},
created() {
@ -389,6 +390,9 @@ export default {
}
})
}
if (this.request.requestResult && this.request.requestResult.length > 0) {
this.response = this.request.requestResult[0];
}
},
initDataSource() {
let databaseConfigsOptions = [];
@ -634,7 +638,7 @@ export default {
}
}
}
if(!this.request.enable){
if (!this.request.enable) {
this.$warning(this.$t('api_test.automation.debug_message'));
return false;
}
@ -669,6 +673,7 @@ export default {
this.request.requestResult = [data];
this.request.result = undefined;
this.loading = false;
this.response = data;
this.$emit('refReload', this.request, this.node);
},
setDomain() {

View File

@ -16,20 +16,57 @@
<template v-slot:request>
<legend style="width: 100%">
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<div class="ms-form">
<div class="ms-form-create" v-loading="loading">
<formCreate
v-model="pluginForm"
:rule="rules"
:option="option"
:value.sync="data"
@prefix-change="change"
@prefix-click="change"
@display-change="changeDisplay"
@prefix-visible-change="visibleChange"
/>
</div>
</div>
<el-tabs v-model="activeName" class="request-tabs" @tab-click="tabClick">
<!-- 请求头-->
<el-tab-pane label="插件数据" name="base">
<div class="ms-form">
<div class="ms-form-create" v-loading="loading">
<formCreate
v-model="pluginForm"
:rule="rules"
:option="option"
:value.sync="data"
@prefix-change="change"
@prefix-click="change"
@display-change="changeDisplay"
@prefix-visible-change="visibleChange"
/>
</div>
</div>
</el-tab-pane>
<!-- 脚本步骤/断言步骤 -->
<el-tab-pane :label="$t('api_test.definition.request.pre_operation')" name="preOperate">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
{{ $t('api_test.definition.request.pre_operation') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.preSize > 0">
<div class="el-step__icon-inner">{{ request.preSize }}</div>
</div>
</span>
<ms-jmx-step :request="request" :apiId="request.id" :response="response" :tab-type="'pre'" ref="preStep"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.post_operation')" name="postOperate">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
{{ $t('api_test.definition.request.post_operation') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.postSize > 0">
<div class="el-step__icon-inner">{{ request.postSize }}</div>
</div>
</span>
<ms-jmx-step :request="request" :apiId="request.id" :response="response" :tab-type="'post'" ref="postStep"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.assertions_rule')" name="assertionsRule">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
{{ $t('api_test.definition.request.assertions_rule') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.ruleSize > 0">
<div class="el-step__icon-inner">{{ request.ruleSize }}</div>
</div>
</span>
<div style="margin-right: 20px">
<ms-jmx-step :request="request" :apiId="request.id" :response="response" @reload="reload" :tab-type="'assertionsRule'" ref="assertionsRule"/>
</div>
</el-tab-pane>
</el-tabs>
</legend>
</template>
@ -99,6 +136,9 @@ import formCreate from "@form-create/element-ui";
import MsUpload from "../common/MsPluginUpload";
import {PLUGIN_ELEMENTS} from "@/business/components/api/automation/scenario/Setting";
import {getUUID} from "@/common/js/utils";
import MsJmxStep from "../../../definition/components/step/JmxStep";
import {Assertions} from "@/business/components/api/definition/model/ApiTestModel";
import {stepCompute, hisDataProcessing} from "@/business/components/api/definition/api-definition";
formCreate.component("msUpload", MsUpload);
@ -107,6 +147,7 @@ export default {
components: {
ApiBaseComponent,
ApiResponseComponent,
MsJmxStep,
MsRun: () => import("../../../definition/components/Run"),
},
props: {
@ -115,6 +156,7 @@ export default {
default: false,
},
message: String,
response: {},
isReadOnly: {
type: Boolean,
default:
@ -144,6 +186,7 @@ export default {
},
data() {
return {
activeName: "base",
loading: false,
runData: [],
reportId: "",
@ -191,11 +234,18 @@ export default {
}
this.data = this.request;
this.pluginName = this.request.stepName ? this.request.stepName : this.request.type;
if (this.request.hashTree) {
this.initStepSize(this.request.hashTree);
this.historicalDataProcessing(this.request.hashTree);
}
},
watch: {
message() {
this.reload();
},
'request.hashTree'() {
this.initStepSize(this.request.hashTree);
},
data: {
handler: function () {
Object.assign(this.request, this.data);
@ -207,6 +257,24 @@ export default {
}
},
methods: {
tabClick() {
if (this.activeName === 'preOperate') {
this.$refs.preStep.filter();
}
if (this.activeName === 'postOperate') {
this.$refs.postStep.filter();
}
if (this.activeName === 'assertionsRule') {
this.$refs.assertionsRule.filter();
}
},
historicalDataProcessing(array) {
hisDataProcessing(array, this.request);
},
initStepSize(array) {
stepCompute(array, this.request);
this.reload();
},
blur(d) {
},
change(fileName) {
@ -235,7 +303,6 @@ export default {
}
}
this.request.debug = true;
this.request.active = true;
this.loading = true;
this.runData = [];
this.runData.projectId = this.request.projectId;
@ -385,7 +452,7 @@ export default {
width: 100px;
}
/deep/ .el-select {
.ms-form-create >>> .el-select {
width: 100%;
}
@ -427,4 +494,12 @@ export default {
.ms-form-create {
margin: 10px;
}
.ms-header {
background: #783887;
color: white;
height: 18px;
border-radius: 42%;
}
</style>

View File

@ -1,3 +1,6 @@
import {Assertions} from "@/business/components/api/definition/model/ApiTestModel";
import {getUUID} from "@/common/js/utils";
export function getProtocolFilter(protocolType) {
if (protocolType === "HTTP") {
return [
@ -40,3 +43,59 @@ export function getProtocolFilter(protocolType) {
];
}
}
export function hisDataProcessing(array, request) {
let assertions = new Assertions({id: getUUID()});
if (!request.hashTree) {
request.hashTree = [];
}
let isOne = true;
let assertionsIndex = [];
if (array) {
for (let index in array) {
let item = array[index];
if (item.type === "Assertions" && isOne) {
assertions = JSON.parse(JSON.stringify(item));
isOne = false;
assertionsIndex.push(item);
} else if (item.type === "Assertions") {
assertions.jsonPath.push(...item.jsonPath);
assertions.jsr223.push(...item.jsr223);
assertions.regex.push(...item.regex);
assertions.xpath2.push(...item.xpath2);
assertionsIndex.push(item);
if (item.duration && item.duration.value > 0) {
assertions.duration = item.duration;
}
if (item.document && item.document.data && (item.document.data.json.length > 0 || item.document.data.xml.length > 0)) {
assertions.document = item.document;
}
}
}
}
assertionsIndex.forEach(item => {
const rmIndex = request.hashTree.findIndex((d) => d.id === item.id);
request.hashTree.splice(rmIndex, 1);
})
request.hashTree.push(assertions);
}
export function stepCompute(array, request) {
let preSize = 0;
let postSize = 0;
let ruleSize = 0;
array.forEach(item => {
if (["JSR223PreProcessor", "JDBCPreProcessor"].indexOf(item.type) !== -1) {
preSize++;
} else if (["JSR223PostProcessor", "JDBCPostProcessor", "Extract"].indexOf(item.type) !== -1) {
postSize++;
} else if (item.type === "Assertions") {
ruleSize = (item.jsonPath.length + item.jsr223.length + item.regex.length + item.xpath2.length);
ruleSize += item.text ? item.text.length : 0;
}
})
request.preSize = preSize;
request.postSize = postSize;
request.ruleSize = ruleSize;
}

View File

@ -2,9 +2,8 @@
<div class="variable-input">
<el-input :disabled="isReadOnly" :value="value" v-bind="$attrs" :size="size" @change="change" @input="input" @click.native="savePreParams(value)"/>
<div :class="{'hidden': !showVariable}" class="variable-combine" v-if="value">
<div v-if="showCopy" class="variable">{{variable}}</div>
<el-tooltip v-if="showCopy" :content="$t('api_test.copied')" manual v-model="visible" placement="top" :visible-arrow="false">
<i class="el-icon-copy-document copy" @click="copy"/>
<i class="el-icon-copy-document copy" @click="copy" style="margin-top: 10px"/>
</el-tooltip>
</div>
</div>

View File

@ -115,6 +115,7 @@
.assertion-btn {
text-align: center;
width: 60px;
width: 30px;
margin-right: 14px;
}
</style>

View File

@ -67,5 +67,6 @@
.assertion-btn {
text-align: center;
width: 60px;
margin-left: 15px;
}
</style>

View File

@ -1,18 +1,15 @@
<template>
<api-base-component
v-loading="loading"
@copy="copyRow"
@remove="remove"
@active="active"
:data="assertions"
:draggable="draggable"
:is-max="isMax"
:show-btn="showBtn"
:show-version="showVersion"
color="#A30014"
background-color="#F7E6E9"
:title="$t('api_test.definition.request.assertions_rule')">
<el-card>
<el-row>
<span>{{ $t('api_test.request.assertions.description') }}</span>
<span style="float: right">
<api-json-path-suggest-button
:open-tip="$t('api_test.request.assertions.json_path_suggest')"
:clear-tip="$t('api_test.request.assertions.json_path_clear')"
@open="suggestJsonOpen"
@clear="clearJson"/>
</span>
</el-row>
<div class="assertion-add" :draggable="draggable">
<el-row :gutter="10">
<el-col :span="4">
@ -37,6 +34,7 @@
:is-read-only="isReadOnly"
:list="assertions.regex"
:callback="after"
@callback="after"
v-if="type === options.REGEX"
/>
<ms-api-assertion-json-path
@ -78,11 +76,6 @@
</el-row>
</div>
<api-json-path-suggest-button
:open-tip="$t('api_test.request.assertions.json_path_suggest')"
:clear-tip="$t('api_test.request.assertions.json_path_clear')"
@open="suggestJsonOpen"
@clear="clearJson"/>
<ms-api-assertions-edit
:is-read-only="isReadOnly"
@ -96,7 +89,7 @@
@addSuggest="addJsonPathSuggest"
ref="jsonpathSuggest"/>
</api-base-component>
</el-card>
</template>
<script>
@ -171,7 +164,29 @@ export default {
reloadData: "",
}
},
watch: {
'assertions.jsonPath'() {
this.computeStep();
},
'assertions.regex'() {
this.computeStep();
},
'assertions.jsr223'() {
this.computeStep();
},
'assertions.xpath2'() {
this.computeStep();
}
},
methods: {
computeStep() {
let ruleSize = 0;
ruleSize = (this.assertions.jsonPath.length + this.assertions.jsr223.length + this.assertions.regex.length + this.assertions.xpath2.length);
ruleSize += this.assertions.text ? this.assertions.text.length : 0;
this.request.ruleSize = ruleSize;
this.$emit('reload');
},
after() {
this.type = "";
this.reloadData = getUUID().substring(0, 8);

View File

@ -59,9 +59,8 @@
<div class="assertion-item-editing response-time" v-if="isDocument">
<div>
{{ assertions.document.type }}-{{ $t("api_test.definition.request.document_structure") }}
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove" style="float: right"/>
</div>
<ms-document-body :document="assertions.document" :apiId="apiId"/>
<ms-document-body :document="assertions.document" :apiId="apiId" :isReadOnly="isReadOnly" @remove="remove"/>
</div>
</div>

View File

@ -1,26 +1,24 @@
<template>
<el-row :gutter="10" class="json-path-suggest-button">
<span class="json-path-suggest-button">
<el-button size="mini" type="primary" @click="$emit('open')" @click.stop>
{{openTip}}
{{ openTip }}
</el-button>
<el-button size="mini" type="danger" @click="$emit('clear')">
{{clearTip}}
{{ clearTip }}
</el-button>
</el-row>
</span>
</template>
<script>
export default {
name: "ApiJsonPathSuggestButton",
props: ['openTip', 'clearTip']
}
export default {
name: "ApiJsonPathSuggestButton",
props: ['openTip', 'clearTip']
}
</script>
<style scoped>
.json-path-suggest-button {
text-align: right;
margin-top: 10px;
}
.json-path-suggest-button {
}
</style>

View File

@ -1,13 +1,20 @@
<template>
<div class="ms-border" style="margin-top: 10px">
<div style="margin-bottom: 10px">
<span class="ms-import" @click="importData">
<i class="el-icon-edit-outline" style="font-size: 16px"/>
{{ $t('commons.import') }}
</span>
<span v-if="apiId!=='none'">
<el-checkbox v-model="checked" @change="checkedAPI">{{ $t('commons.follow_api') }}</el-checkbox>
</span>
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
<el-col>
<span class="ms-import" @click="importData">
<i class="el-icon-edit-outline" style="font-size: 16px"/>
{{ $t('commons.import') }}
</span>
<span v-if="apiId!=='none'">
<el-checkbox v-model="checked" @change="checkedAPI">{{ $t('commons.follow_api') }}</el-checkbox>
</span>
</el-col>
<el-col class="assertion-btn">
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="removeDoc" style="float: right"/>
</el-col>
</el-row>
</div>
<el-table
:data="tableData"
@ -112,6 +119,10 @@ export default {
document: {},
apiId: String,
showOptionsButton: Boolean,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {
@ -162,6 +173,9 @@ export default {
}
},
methods: {
removeDoc() {
this.$emit('remove');
},
setJSONData(data) {
this.checked = false;
this.document.data.jsonFollowAPI = "";
@ -528,4 +542,9 @@ export default {
cursor: pointer;
border-color: #783887;
}
.assertion-btn {
text-align: center;
width: 60px;
}
</style>

View File

@ -110,7 +110,7 @@
<div v-if="apiCase.active||type==='detail'" v-loading="loading">
<el-divider></el-divider>
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<ms-api-request-form :isShowEnable="true" :showScript="true" :headers="apiCase.request.headers " :request="apiCase.request" v-if="api.protocol==='HTTP'"/>
<ms-api-request-form :isShowEnable="true" :showScript="true" :headers="apiCase.request.headers " :response="apiCase.responseData" :request="apiCase.request" v-if="api.protocol==='HTTP'"/>
<tcp-format-parameters :showScript="true" :request="apiCase.request" v-if="api.method==='TCP' && apiCase.request.esbDataStruct == null"/>
<esb-definition v-xpack :request="apiCase.request" :showScript="true" v-if="isXpack&&api.method==='ESB'" ref="esbDefinition"/>
<ms-sql-basis-parameters :showScript="true" :request="apiCase.request" v-if="api.protocol==='SQL'"/>
@ -124,9 +124,6 @@
<div v-else>
<api-response-component :currentProtocol="apiCase.request.protocol" :api-item="apiCase" :result="runResult"/>
</div>
<ms-jmx-step v-if="apiCase.request.hashTree && apiCase.request.hashTree.length > 0" :request="apiCase.request" :api-id="api.id" :response="apiCase.responseData"/>
</div>
</el-collapse-transition>
<ms-change-history ref="changeHistory"/>
@ -149,7 +146,6 @@ import MsDubboBasisParameters from "../request/dubbo/BasisParameters";
import MsApiExtendBtns from "../reference/ApiExtendBtns";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
import MsRequestResultTail from "../response/RequestResultTail";
import MsJmxStep from "../step/JmxStep";
import ApiResponseComponent from "../../../automation/scenario/component/ApiResponseComponent";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
@ -188,7 +184,6 @@ export default {
MsDubboBasisParameters,
MsApiExtendBtns,
MsRequestResultTail,
MsJmxStep,
ShowMoreBtn,
MsChangeHistory,
"esbDefinition": esbDefinition.default,

View File

@ -29,6 +29,11 @@
<el-button v-if="scenario" size="small" type="primary" @click="handleCommand">
{{ $t('commons.test') }}
</el-button>
<el-button size="small" type="primary" @click.stop @click="generate"
style="margin-left: 10px"
v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API') && hasLicense()">
{{ $t('commons.generate_test_data') }}
</el-button>
</div>
</el-form-item>
@ -36,14 +41,11 @@
<div v-loading="loading">
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<!-- HTTP 请求参数 -->
<ms-api-request-form :isShowEnable="true" :definition-test="true" :headers="request.headers" :request="request" :response="responseData"/>
<ms-api-request-form :isShowEnable="true" :definition-test="true" :headers="request.headers" :request="request" :response="responseData" ref="apiRequestForm"/>
<!-- HTTP 请求返回数据 -->
<p class="tip">{{ $t('api_test.definition.request.res_param') }} </p>
<ms-request-result-tail v-if="!loading" :response="responseData" ref="debugResult"/>
<ms-jmx-step :request="request" :response="responseData"/>
<!-- 执行组件 -->
<ms-run :debug="true" :reportId="reportId" :isStop="isStop" :run-data="runData" @runRefresh="runRefresh" ref="runTest"/>
</div>
@ -61,13 +63,12 @@
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import MsResponseResult from "../response/ResponseResult";
import MsRequestMetric from "../response/RequestMetric";
import {getCurrentUser, getUUID} from "@/common/js/utils";
import {getCurrentUser, getUUID, hasLicense, hasPermission} from "@/common/js/utils";
import MsResponseText from "../response/ResponseText";
import MsRun from "../Run";
import {createComponent} from "../jmeter/components";
import {REQ_METHOD} from "../../model/JsonData";
import MsRequestResultTail from "../response/RequestResultTail";
import MsJmxStep from "../step/JmxStep";
import {KeyValue} from "../../model/ApiTestModel";
import MsApiCaseList from "../case/ApiCaseList";
import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
@ -81,7 +82,6 @@ export default {
MsRequestMetric,
MsResponseText,
MsRun,
MsJmxStep,
MsApiCaseList
},
props: {
@ -152,6 +152,10 @@ export default {
},
},
methods: {
hasPermission, hasLicense,
generate() {
this.$refs.apiRequestForm.generate();
},
handleCommand(e) {
if (e === "save_as") {
this.saveAs();

View File

@ -12,10 +12,14 @@
background-color="#E6EEF2"
:if-from-variable-advance="ifFromVariableAdvance"
:title="$t('api_test.definition.request.extract_param')">
<div style="margin: 20px" v-loading="loading">
<div class="extract-description">
{{ $t('api_test.request.extract.description') }}
</div>
<div style="margin: 10px" v-loading="loading">
<el-row>
<span>{{ $t('api_test.request.extract.description') }}</span>
<span style="float: right">
<api-json-path-suggest-button :open-tip="$t('api_test.request.extract.json_path_suggest')"
:clear-tip="$t('api_test.request.extract.json_path_clear')" @open="suggestJsonOpen" @clear="clearJson"/>
</span>
</el-row>
<div class="extract-add">
<el-row :gutter="10">
<el-col :span="2">
@ -35,8 +39,6 @@
</el-row>
</div>
<api-json-path-suggest-button :open-tip="$t('api_test.request.extract.json_path_suggest')"
:clear-tip="$t('api_test.request.extract.json_path_clear')" @open="suggestJsonOpen" @clear="clearJson"/>
<ms-api-extract-edit :if-from-variable-advance="ifFromVariableAdvance" @savePreParams="savePreParams" :is-read-only="isReadOnly" :reloadData="reloadData" :extract="extract"/>
</div>
@ -45,148 +47,148 @@
</template>
<script>
import {EXTRACT_TYPE} from "../../model/ApiTestModel";
import MsApiExtractEdit from "./ApiExtractEdit";
import MsApiExtractCommon from "./ApiExtractCommon";
import {getUUID} from "@/common/js/utils";
import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton";
import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest";
import {ExtractJSONPath} from "../../../test/model/ScenarioModel";
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
import {EXTRACT_TYPE} from "../../model/ApiTestModel";
import MsApiExtractEdit from "./ApiExtractEdit";
import MsApiExtractCommon from "./ApiExtractCommon";
import {getUUID} from "@/common/js/utils";
import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton";
import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest";
import {ExtractJSONPath} from "../../../test/model/ScenarioModel";
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
export default {
name: "MsApiExtract",
components: {
ApiBaseComponent,
MsApiJsonpathSuggest,
ApiJsonPathSuggestButton,
MsApiExtractCommon,
MsApiExtractEdit,
export default {
name: "MsApiExtract",
components: {
ApiBaseComponent,
MsApiJsonpathSuggest,
ApiJsonPathSuggestButton,
MsApiExtractCommon,
MsApiExtractEdit,
},
props: {
extract: {},
response: {},
node: {},
customizeStyle: {
type: String,
default: "margin-top: 10px"
},
props: {
extract: {},
response: {},
node: {},
customizeStyle: {
type: String,
default: "margin-top: 10px"
},
isReadOnly: {
type: Boolean,
default: false
},
draggable: {
type: Boolean,
default: false,
},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
showVersion: {
type: Boolean,
default: true,
},
ifFromVariableAdvance: {
type: Boolean,
default: false,
},
isReadOnly: {
type: Boolean,
default: false
},
data() {
return {
options: EXTRACT_TYPE,
type: "",
reloadData: "",
loading: false,
}
draggable: {
type: Boolean,
default: false,
},
methods: {
after() {
this.type = "";
this.reloadData = getUUID().substring(0, 8);
},
remove() {
this.$emit('remove', this.extract, this.node);
},
copyRow() {
this.$emit('copyRow', this.extract, this.node);
},
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
active() {
this.extract.active = !this.extract.active;
this.reload();
},
suggestJsonOpen() {
this.$emit('suggestClick');
this.$nextTick(() => {
if (!this.response || !this.response.responseResult || !this.response.responseResult.body) {
this.$message(this.$t('api_test.request.assertions.debug_first'));
return;
}
this.$refs.jsonpathSuggest.open(this.response.responseResult.body);
})
},
addJsonPathSuggest(data) {
let option = {};
option.variable = data.key;
option.expression = data.path;
this.extract.json.push(new ExtractJSONPath(option));
},
clearJson() {
this.extract.json = [];
},
savePreParams(data) {
this.$emit("savePreParams", data);
},
isMax: {
type: Boolean,
default: false,
},
computed: {
list() {
switch (this.type) {
case EXTRACT_TYPE.REGEX:
return this.extract.regex;
case EXTRACT_TYPE.JSON_PATH:
return this.extract.json;
case EXTRACT_TYPE.XPATH:
return this.extract.xpath;
default:
return [];
showBtn: {
type: Boolean,
default: true,
},
showVersion: {
type: Boolean,
default: true,
},
ifFromVariableAdvance: {
type: Boolean,
default: false,
},
},
data() {
return {
options: EXTRACT_TYPE,
type: "",
reloadData: "",
loading: false,
}
},
methods: {
after() {
this.type = "";
this.reloadData = getUUID().substring(0, 8);
},
remove() {
this.$emit('remove', this.extract, this.node);
},
copyRow() {
this.$emit('copyRow', this.extract, this.node);
},
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
active() {
this.extract.active = !this.extract.active;
this.reload();
},
suggestJsonOpen() {
this.$emit('suggestClick');
this.$nextTick(() => {
if (!this.response || !this.response.responseResult || !this.response.responseResult.body) {
this.$message(this.$t('api_test.request.assertions.debug_first'));
return;
}
this.$refs.jsonpathSuggest.open(this.response.responseResult.body);
})
},
addJsonPathSuggest(data) {
let option = {};
option.variable = data.key;
option.expression = data.path;
this.extract.json.push(new ExtractJSONPath(option));
},
clearJson() {
this.extract.json = [];
},
savePreParams(data) {
this.$emit("savePreParams", data);
},
},
computed: {
list() {
switch (this.type) {
case EXTRACT_TYPE.REGEX:
return this.extract.regex;
case EXTRACT_TYPE.JSON_PATH:
return this.extract.json;
case EXTRACT_TYPE.XPATH:
return this.extract.xpath;
default:
return [];
}
}
}
}
</script>
<style scoped>
.extract-description {
font-size: 13px;
margin-bottom: 10px;
}
.extract-description {
font-size: 13px;
margin-bottom: 10px;
}
.extract-item {
width: 100%;
}
.extract-item {
width: 100%;
}
.extract-add {
padding: 10px;
border: #DCDFE6 solid 1px;
margin: 5px 0;
border-radius: 5px;
}
.extract-add {
padding: 10px;
border: #DCDFE6 solid 1px;
margin: 5px 0;
border-radius: 5px;
}
.icon.is-active {
transform: rotate(90deg);
}
.icon.is-active {
transform: rotate(90deg);
}
/deep/ .el-card__body {
padding: 6px 10px;
}
/deep/ .el-card__body {
padding: 6px 10px;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div>
<el-row :gutter="10" type="flex" justify="space-between" align="middle">
<el-col v-if="extractType === 'Regex'" :span="5">
<el-col v-if="extractType === 'Regex'" :span="8">
<el-select :disabled="isReadOnly" class="extract-item" v-model="common.useHeaders" :placeholder="$t('api_test.request.assertions.select_subject')" size="small">
<el-option v-for="item in useHeadersOption" :key="item.value" :label="item.label" :value="item.value"/>
</el-select>
@ -14,7 +14,7 @@
<el-input :disabled="isReadOnly" v-model="common.expression" size="small" show-word-limit
:placeholder="expression" @click.native="savePreParams(common.variable)"/>
</el-col>
<el-col class="multiple_checkbox">
<el-col class="multiple_checkbox" v-if="edit">
<el-checkbox v-model="common.multipleMatching" :disabled="isReadOnly">
{{ $t('api_test.request.extract.multiple_matching') }}
</el-checkbox>
@ -22,7 +22,7 @@
<el-col class="extract-btn">
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove"
v-if="edit"/>
<el-button :disabled="isReadOnly" type="primary" size="small" @click="add" v-else>Add</el-button>
<el-button :disabled="isReadOnly" type="primary" size="small" @click="add" v-else>{{$t('commons.add')}}</el-button>
</el-col>
</el-row>
</div>

View File

@ -1,83 +1,103 @@
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
<div>
<el-row>
<el-col :span="spanCount">
<!-- HTTP 请求参数 -->
<div style="border:1px #DCDFE6 solid; height: 100%;border-radius: 4px ;width: 100%" v-loading="isReloadData">
<el-tabs v-model="activeName" class="request-tabs">
<!-- 请求头-->
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.request.headers')" placement="top-start" slot="label">
<!-- HTTP 请求参数 -->
<div style="border:1px #DCDFE6 solid; height: 100%;border-radius: 4px ;width: 100%" v-loading="isReloadData">
<el-tabs v-model="activeName" class="request-tabs" @tab-click="tabClick">
<!-- 请求头-->
<el-tab-pane :label="$t('api_test.request.headers')" name="headers">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.request.headers')" placement="top-start" slot="label">
<span>{{ $t('api_test.request.headers') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="headers.length>1">
<div class="el-step__icon-inner">{{ headers.length - 1 }}</div>
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}</el-link>
</el-row>
<ms-api-key-value @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" :show-desc="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers" :need-mock="true"/>
</el-tab-pane>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}</el-link>
</el-row>
<ms-api-key-value @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" :show-desc="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :suggestions="headerSuggestions" :items="headers" :need-mock="true"/>
</el-tab-pane>
<!--query 参数-->
<el-tab-pane :label="$t('api_test.definition.request.query_param')" name="parameters">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.query_info')" placement="top-start" slot="label">
<!--query 参数-->
<el-tab-pane :label="$t('api_test.definition.request.query_param')" name="parameters">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.query_info')" placement="top-start" slot="label">
<span>{{ $t('api_test.definition.request.query_param') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.arguments.length>1">
<div class="el-step__icon-inner">{{ request.arguments.length - 1 }}</div>
</div></span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}</el-link>
</el-row>
<ms-api-variable @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" :with-mor-setting="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.arguments"/>
</el-tab-pane>
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}</el-link>
</el-row>
<ms-api-variable @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" :with-mor-setting="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.arguments"/>
</el-tab-pane>
<!--REST 参数-->
<el-tab-pane :label="$t('api_test.definition.request.rest_param')" name="rest">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.rest_info')" placement="top-start" slot="label">
<!--REST 参数-->
<el-tab-pane :label="$t('api_test.definition.request.rest_param')" name="rest">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.rest_info')" placement="top-start" slot="label">
<span>
{{ $t('api_test.definition.request.rest_param') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.rest.length>1">
<div class="el-step__icon-inner">{{ request.rest.length - 1 }}</div>
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}</el-link>
</el-row>
<ms-api-variable @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" :with-mor-setting="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.rest"/>
</el-tab-pane>
</el-tooltip>
<el-row>
<el-link class="ms-el-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}</el-link>
</el-row>
<ms-api-variable @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" :with-mor-setting="true" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :parameters="request.rest"/>
</el-tab-pane>
<!--请求体-->
<el-tab-pane v-if="isBodyShow" :label="$t('api_test.request.body')" name="body" style="overflow: auto">
<ms-api-body @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" @headersChange="reloadBody" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :headers="headers" :body="request.body"/>
</el-tab-pane>
<!--请求体-->
<el-tab-pane v-if="isBodyShow" :label="$t('api_test.request.body')" name="body" style="overflow: auto">
<ms-api-body @editScenarioAdvance="editScenarioAdvance" :scenario-definition="scenarioDefinition" @headersChange="reloadBody" :is-read-only="isReadOnly" :isShowEnable="isShowEnable" :headers="headers" :body="request.body"/>
</el-tab-pane>
<!-- 认证配置 -->
<el-tab-pane :label="$t('api_test.definition.request.auth_config')" name="authConfig">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.auth_config_info')" placement="top-start" slot="label">
<span>{{ $t('api_test.definition.request.auth_config') }}</span>
</el-tooltip>
<!-- 认证配置 -->
<el-tab-pane :label="$t('api_test.definition.request.auth_config')" name="authConfig">
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.definition.request.auth_config_info')" placement="top-start" slot="label">
<span>{{ $t('api_test.definition.request.auth_config') }}</span>
</el-tooltip>
<ms-api-auth-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
<ms-api-auth-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.other_config')" name="advancedConfig">
<ms-api-advanced-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
<el-tab-pane name="create" v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API') && hasLicense() && definitionTest">
<template v-slot:label>
<el-button size="mini" type="primary" @click.stop @click="generate">{{ $t('commons.generate_test_data') }}</el-button>
</template>
</el-tab-pane>
</el-tabs>
</div>
</el-col>
<!--操作按钮-->
<api-definition-step-button :request="request" v-if="!referenced && showScript"/>
</el-row>
<el-tab-pane :label="$t('api_test.definition.request.other_config')" name="advancedConfig">
<ms-api-advanced-config :is-read-only="isReadOnly" :request="request"/>
</el-tab-pane>
<!-- 脚本步骤/断言步骤 -->
<el-tab-pane :label="$t('api_test.definition.request.pre_operation')" name="preOperate" v-if="showScript">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
{{ $t('api_test.definition.request.pre_operation') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.preSize > 0">
<div class="el-step__icon-inner">{{ request.preSize }}</div>
</div>
</span>
<ms-jmx-step :request="request" :apiId="request.id" :response="response" :tab-type="'pre'" ref="preStep"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.post_operation')" name="postOperate" v-if="showScript">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
{{ $t('api_test.definition.request.post_operation') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.postSize > 0">
<div class="el-step__icon-inner">{{ request.postSize }}</div>
</div>
</span>
<ms-jmx-step :request="request" :apiId="request.id" :response="response" :tab-type="'post'" ref="postStep"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.definition.request.assertions_rule')" name="assertionsRule" v-if="showScript">
<span class="item-tabs" effect="dark" placement="top-start" slot="label">
{{ $t('api_test.definition.request.assertions_rule') }}
<div class="el-step__icon is-text ms-api-col ms-header" v-if="request.ruleSize > 0">
<div class="el-step__icon-inner">{{ request.ruleSize }}</div>
</div>
</span>
<ms-jmx-step :request="request" :apiId="request.id" :response="response" @reload="reloadBody" :tab-type="'assertionsRule'" ref="assertionsRule"/>
</el-tab-pane>
</el-tabs>
</div>
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
</div>
</template>
@ -96,14 +116,14 @@ import {hasLicense, getUUID} from "@/common/js/utils";
import BatchAddParameter from "../../basis/BatchAddParameter";
import MsApiAdvancedConfig from "./ApiAdvancedConfig";
import MsJsr233Processor from "../../../../automation/scenario/component/Jsr233Processor";
import ApiDefinitionStepButton from "../components/ApiDefinitionStepButton";
import {hasPermission} from '@/common/js/utils';
import Convert from "@/business/components/common/json-schema/convert/convert";
import MsJmxStep from "../../step/JmxStep";
import {stepCompute, hisDataProcessing} from "@/business/components/api/definition/api-definition";
export default {
name: "MsApiHttpRequestForm",
components: {
ApiDefinitionStepButton,
MsJsr233Processor,
MsApiAdvancedConfig,
BatchAddParameter,
@ -113,7 +133,8 @@ export default {
MsApiAuthConfig,
MsApiBody,
MsApiKeyValue,
MsApiAssertions
MsApiAssertions,
MsJmxStep
},
props: {
method: String,
@ -203,15 +224,29 @@ export default {
if (this.request.arguments && this.request.arguments.length > 1) {
this.activeName = 'parameters';
}
if(this.request.body) {
if (this.request.body) {
this.request.body.typeChange = this.request.changeId;
}
this.reload();
}
},
'request.hashTree'() {
this.initStepSize(this.request.hashTree);
},
},
methods: {
hasPermission,
hasLicense,
tabClick() {
if (this.activeName === 'preOperate') {
this.$refs.preStep.filter();
}
if (this.activeName === 'postOperate') {
this.$refs.postStep.filter();
}
if (this.activeName === 'assertionsRule') {
this.$refs.assertionsRule.filter();
}
},
generate() {
if (this.request.body && (this.request.body.jsonSchema || this.request.body.raw)) {
if (!this.request.body.jsonSchema) {
@ -265,6 +300,17 @@ export default {
if (!this.request.arguments) {
this.request.arguments = [];
}
if (this.request.hashTree) {
this.initStepSize(this.request.hashTree);
this.historicalDataProcessing(this.request.hashTree);
}
},
historicalDataProcessing(array) {
hisDataProcessing(array, this.request);
},
initStepSize(array) {
stepCompute(array, this.request);
this.reloadBody();
},
reloadBody() {
// body

View File

@ -30,9 +30,6 @@
<p class="tip">{{ $t('api_test.definition.request.res_param') }} </p>
<ms-request-result-tail :response="responseData" ref="runResult"/>
</div>
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
</el-card>
<!-- 加载用例 -->
@ -60,7 +57,6 @@ import MsRequestResultTail from "../response/RequestResultTail";
import MsRun from "../Run";
import MsBasisParameters from "../request/dubbo/BasisParameters";
import {REQ_METHOD} from "../../model/JsonData";
import MsJmxStep from "../step/JmxStep";
import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
export default {
@ -72,8 +68,7 @@ export default {
MsRequestResultTail,
ApiEnvironmentConfig,
MsRun,
MsBasisParameters,
MsJmxStep
MsBasisParameters
},
data() {
return {

View File

@ -48,6 +48,12 @@
<el-button size="small" type="primary" v-else @click.once="stop">{{ $t('report.stop_btn') }}</el-button>
<el-button size="small" type="primary" @click.stop @click="generate"
style="margin-left: 10px"
v-if="hasPermission('PROJECT_API_DEFINITION:READ+CREATE_API') && hasLicense()">
{{ $t('commons.generate_test_data') }}
</el-button>
</el-form-item>
@ -55,17 +61,15 @@
<div v-loading="loading">
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<!-- HTTP 请求参数 -->
<ms-api-request-form :isShowEnable="true" :definition-test="true" :headers="api.request.headers"
<ms-api-request-form :isShowEnable="true" :definition-test="true" :headers="api.request.headers" :response="responseData"
v-if="loadRequest"
:request="api.request"/>
:request="api.request" ref="apiRequestForm"/>
<!--返回结果-->
<!-- HTTP 请求返回数据 -->
<p class="tip">{{ $t('api_test.definition.request.res_param') }} </p>
<ms-request-result-tail :response="responseData" ref="runResult"/>
</div>
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
</el-card>
<!-- 加载用例 -->
@ -85,14 +89,13 @@
<script>
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import {getUUID, hasLicense} from "@/common/js/utils";
import {getUUID, hasLicense, hasPermission} from "@/common/js/utils";
import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer";
import MsRequestResultTail from "../response/RequestResultTail";
import MsRun from "../Run";
import {REQ_METHOD} from "../../model/JsonData";
import EnvironmentSelect from "../environment/EnvironmentSelect";
import MsJmxStep from "../step/JmxStep";
import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
export default {
@ -104,7 +107,6 @@ export default {
MsContainer,
MsRequestResultTail,
MsRun,
MsJmxStep
},
data() {
return {
@ -142,6 +144,10 @@ export default {
}
},
methods: {
hasPermission, hasLicense,
generate() {
this.$refs.apiRequestForm.generate();
},
setRequestParam(param, isEnvironmentMock) {
this.init();
if (param) {

View File

@ -30,8 +30,6 @@
<ms-request-result-tail :response="responseData" :currentProtocol="currentProtocol" ref="runResult"/>
</div>
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
</el-card>
<!-- 加载用例 -->
@ -59,7 +57,6 @@ import MsRequestResultTail from "../response/RequestResultTail";
import MsRun from "../Run";
import MsBasisParameters from "../request/database/BasisParameters";
import {REQ_METHOD} from "../../model/JsonData";
import MsJmxStep from "../step/JmxStep";
import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
export default {
@ -71,8 +68,7 @@ export default {
MsRequestResultTail,
ApiEnvironmentConfig,
MsRun,
MsBasisParameters,
MsJmxStep
MsBasisParameters
},
data() {
return {

View File

@ -49,8 +49,6 @@
</div>
</el-form>
<ms-jmx-step :request="api.request" :apiId="api.id" :response="responseData"/>
<div v-if="api.method=='ESB'">
<p class="tip">{{ $t('api_test.definition.request.res_param') }}</p>
<esb-definition-response v-xpack v-if="showXpackCompnent" :is-api-component="false" :show-options-button="false"
@ -83,7 +81,6 @@ import MsRun from "../Run";
import MsTcpFormatParameters from "@/business/components/api/definition/components/request/tcp/TcpFormatParameters";
import {REQ_METHOD} from "../../model/JsonData";
import EnvironmentSelect from "../environment/EnvironmentSelect";
import MsJmxStep from "../step/JmxStep";
import {TYPE_TO_C} from "@/business/components/api/automation/scenario/Setting";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
@ -92,7 +89,6 @@ const esbDefinitionResponse = (requireComponent != null && requireComponent.keys
export default {
name: "RunTestTCPPage",
components: {
MsJmxStep,
EnvironmentSelect,
MsApiRequestForm,
MsApiCaseList,

View File

@ -1,77 +1,109 @@
<template>
<div v-if="loaded">
<p class="tip">
{{ $t('test_track.plan_view.step') }}
<p>
<el-select v-model="preOperate" size="mini" class="ms-select-step" v-if="tabType === 'pre'">
<el-option
v-for="item in preOperates"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<el-select v-model="postOperate" size="mini" class="ms-select-step" v-else-if="tabType === 'post'">
<el-option
v-for="item in postOperates"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<el-button size="mini" @click="add" type="primary" v-if="tabType !== 'assertionsRule'">
{{ $t('api_test.request.assertions.add') }}
</el-button>
</p>
<!-- HTTP 请求参数 -->
<div style="height: 100%;border-radius: 4px ;width: 100%" v-loading="isReloadData" v-if="request.hashTree && request.hashTree.length>0">
<div v-for="row in request.hashTree" :key="row.id">
<el-tree node-key="resourceId"
:props="props"
:data="request.hashTree"
:allow-drop="allowDrop"
:filter-node-method="filterNode"
@node-drag-end="allowDrag"
draggable ref="generalSteps" class="ms-step-tree-cell">
<span class="custom-tree-node father" slot-scope="{node,data}" style="width: 100%">
<!--前置脚本-->
<ms-jsr233-processor
v-if="row.type==='JSR223PreProcessor'"
@remove="remove"
@copyRow="copyRow"
:title="$t('api_test.definition.request.pre_script')"
:jsr223-processor="row"
color="#B8741A"
background-color="#F9F1EA"/>
<!--后置脚本-->
<ms-jsr233-processor
v-if="row.type ==='JSR223PostProcessor'"
@copyRow="copyRow"
@remove="remove"
:is-read-only="false"
:title="$t('api_test.definition.request.post_script')"
:jsr223-processor="row"
color="#783887"
background-color="#F2ECF3"/>
<!--前置SQL-->
<ms-jdbc-processor
v-if="row.type ==='JDBCPreProcessor'"
@copyRow="copyRow"
@remove="remove"
:title="$t('api_test.definition.request.pre_sql')"
:is-read-only="false"
:request="row"
:jdbc-processor="row"
color="#B8741A"
background-color="#F9F1EA"/>
<!--后置SQL-->
<ms-jdbc-processor
v-if="row.type ==='JDBCPostProcessor'"
@copyRow="copyRow"
@remove="remove"
:title="$t('api_test.definition.request.post_sql')"
:is-read-only="false"
:request="row"
:jdbc-processor="row"
color="#783887"
background-color="#F2ECF3"/>
<!--断言规则-->
<div style="margin-top: 10px">
<ms-api-assertions
v-if="row.type==='Assertions'"
<div v-if="tabType === 'pre'">
<ms-jsr233-processor
v-if="data.type==='JSR223PreProcessor'"
@remove="remove"
@copyRow="copyRow"
:title="$t('api_test.definition.request.pre_script')"
:jsr223-processor="data"
color="#B8741A"
background-color="#F9F1EA"/>
<!--前置SQL-->
<ms-jdbc-processor
v-if="data.type ==='JDBCPreProcessor'"
@copyRow="copyRow"
@remove="remove"
:title="$t('api_test.definition.request.pre_sql')"
:is-read-only="false"
:request="data"
:jdbc-processor="data"
color="#B8741A"
background-color="#F9F1EA"/>
<ms-constant-timer :timer="data" :node="node" v-if="data.type ==='ConstantTimer'"/>
</div>
<div v-if="tabType ==='post'">
<!--后置脚本-->
<ms-jsr233-processor
v-if="data.type ==='JSR223PostProcessor'"
@copyRow="copyRow"
@remove="remove"
:is-read-only="false"
:title="$t('api_test.definition.request.post_script')"
:jsr223-processor="data"
color="#783887"
background-color="#F2ECF3"/>
<!--后置SQL-->
<ms-jdbc-processor
v-if="data.type ==='JDBCPostProcessor'"
@copyRow="copyRow"
@remove="remove"
:title="$t('api_test.definition.request.post_sql')"
:is-read-only="false"
:request="data"
:jdbc-processor="data"
color="#783887"
background-color="#F2ECF3"/>
<!--提取规则-->
<ms-api-extract
:response="response"
:is-read-only="isReadOnly"
:extract="data"
@copyRow="copyRow"
@remove="remove"
v-if="data.type==='Extract'"
/>
</div>
<div v-if="tabType === 'assertionsRule'">
<!--断言规则-->
<ms-api-assertions
v-if="data.type==='Assertions'"
@copyRow="copyRow"
@remove="remove"
@reload="reloadRule"
:response="response"
:request="request"
:apiId="apiId"
:is-read-only="isReadOnly"
:assertions="row"/>
</div>
<!--提取规则-->
<div style="margin-top: 10px">
<ms-api-extract
:response="response"
:is-read-only="isReadOnly"
:extract="row"
@copyRow="copyRow"
@remove="remove"
v-if="row.type==='Extract'"
/>
</div>
</div>
</div>
:assertions="data"/>
</div>
</span>
</el-tree>
</div>
</template>
@ -80,10 +112,11 @@ import {REQUEST_HEADERS} from "@/common/js/constants";
import {createComponent} from "../jmeter/components";
import MsApiAssertions from "../assertion/ApiAssertions";
import MsApiExtract from "../extract/ApiExtract";
import {Assertions, Body, Extract, KeyValue} from "../../model/ApiTestModel";
import {Assertions, Body, ConstantTimer, Extract, KeyValue} from "../../model/ApiTestModel";
import {getUUID} from "@/common/js/utils";
import BatchAddParameter from "../basis/BatchAddParameter";
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
import MsConstantTimer from "../../../automation/scenario/component/ConstantTimer";
import MsJdbcProcessor from "@/business/components/api/automation/scenario/component/JDBCProcessor";
export default {
@ -93,10 +126,12 @@ export default {
MsJsr233Processor,
BatchAddParameter,
MsApiExtract,
MsApiAssertions
MsApiAssertions,
MsConstantTimer
},
props: {
request: {},
tabType: String,
response: {},
apiId: String,
showScript: {
@ -123,7 +158,7 @@ export default {
watch: {
'request.body.typeChange'() {
this.showHide();
},
}
},
data() {
let validateURL = (rule, value, callback) => {
@ -134,6 +169,22 @@ export default {
}
};
return {
props: {
label: "name",
children: "hashTree"
},
preOperate: "script",
postOperate: "script",
preOperates: [
{id: 'script', name: this.$t('api_test.definition.request.pre_script')},
{id: 'sql', name: this.$t('api_test.definition.request.pre_sql')},
{id: 'wait_controller', name: this.$t('api_test.automation.wait_controller')}
],
postOperates: [
{id: 'script', name: this.$t('api_test.definition.request.post_script')},
{id: 'sql', name: this.$t('api_test.definition.request.post_sql')},
{id: 'extract', name: this.$t('api_test.definition.request.extract_param')}
],
activeName: "headers",
loaded: true,
rules: {
@ -154,18 +205,80 @@ export default {
dialogVisible: false,
}
},
created() {
this.init();
},
methods: {
initAssertions() {
let ruleSize = 0;
this.request.hashTree.forEach(item => {
if (item.type === "Assertions") {
ruleSize++;
}
})
if (ruleSize === 0) {
this.addAssertions();
}
},
reloadRule() {
this.$emit('reload');
},
add() {
this.request.active = false;
if (this.tabType === 'pre') {
if (this.preOperate === 'script') {
this.addPre();
} else if (this.preOperate === 'sql') {
this.addPreSql();
} else {
this.addWait();
}
} else if (this.tabType === 'post') {
if (this.postOperate === 'script') {
this.addPost();
} else if (this.postOperate === 'sql') {
this.addPostSql();
} else if (this.postOperate === 'extract') {
this.addExtract();
} else {
this.addWait();
}
} else {
this.addAssertions();
}
this.sort();
this.reload();
},
filterNode(value, data) {
if (data.type && value.indexOf(data.type) !== -1) {
return true;
}
return false;
},
filter() {
let vars = [];
if (this.tabType === 'pre') {
vars = ["JSR223PreProcessor", "JDBCPreProcessor", "ConstantTimer"];
} else if (this.tabType === 'post') {
vars = ["JSR223PostProcessor", "JDBCPostProcessor", "Extract"];
} else {
vars = ["Assertions"];
}
this.$nextTick(() => {
if (this.$refs.generalSteps && this.$refs.generalSteps.filter) {
this.$refs.generalSteps.filter(vars);
}
});
this.sort();
},
addPre() {
let jsr223PreProcessor = createComponent("JSR223PreProcessor");
if (!this.request.hashTree) {
this.request.hashTree = [];
}
this.request.hashTree.push(jsr223PreProcessor);
this.sort();
this.reload();
},
addPost() {
@ -174,6 +287,34 @@ export default {
this.request.hashTree = [];
}
this.request.hashTree.push(jsr223PostProcessor);
this.sort();
this.reload();
},
addPreSql() {
let jdbcPreProcessor = createComponent("JDBCPreProcessor");
if (!this.request.hashTree) {
this.request.hashTree = [];
}
this.request.hashTree.push(jdbcPreProcessor);
this.sort();
this.reload();
},
addPostSql() {
let jdbcPostProcessor = createComponent("JDBCPostProcessor");
if (!this.request.hashTree) {
this.request.hashTree = [];
}
this.request.hashTree.push(jdbcPostProcessor);
this.sort();
this.reload();
},
addWait() {
let constant = new ConstantTimer({delay: 1000});
if (!this.request.hashTree) {
this.request.hashTree = [];
}
this.request.hashTree.push(constant);
this.sort();
this.reload();
},
addAssertions() {
@ -182,6 +323,7 @@ export default {
this.request.hashTree = [];
}
this.request.hashTree.push(assertions);
this.sort();
this.reload();
},
addExtract() {
@ -190,24 +332,40 @@ export default {
this.request.hashTree = [];
}
this.request.hashTree.push(jsonPostProcessor);
this.sort();
this.reload();
},
remove(row) {
let index = this.request.hashTree.indexOf(row);
this.request.hashTree.splice(index, 1);
this.sort();
this.reload();
},
copyRow(row) {
let obj = JSON.parse(JSON.stringify(row));
obj.id = getUUID();
const index = this.request.hashTree.findIndex(d => d.id === row.id);
if (index != -1) {
if (index !==-1) {
this.request.hashTree.splice(index, 0, obj);
} else {
this.request.hashTree.push(obj);
}
this.sort();
this.reload();
},
allowDrop(draggingNode, dropNode, dropType) {
//
if (dropType !== "inner") {
return true;
}
return false;
},
allowDrag(draggingNode, dropNode, dropType) {
if (dropNode && draggingNode && dropType) {
this.reload();
this.filter();
}
},
reload() {
this.isReloadData = true
this.$nextTick(() => {
@ -233,6 +391,25 @@ export default {
if (!this.request.arguments) {
this.request.arguments = [];
}
this.sort();
},
sort() {
let index = 1;
for (let i in this.request.hashTree) {
if (this.tabType === 'pre' && (this.request.hashTree[i].type === 'JSR223PreProcessor' ||
this.request.hashTree[i].type === 'JDBCPreProcessor' || this.request.hashTree[i].type === 'ConstantTimer')) {
this.request.hashTree[i].index = Number(index);
index++;
} else if (this.tabType === 'post' && (this.request.hashTree[i].type === 'JSR223PostProcessor' ||
this.request.hashTree[i].type === 'JDBCPostProcessor' ||
this.request.hashTree[i].type === 'Extract')) {
this.request.hashTree[i].index = Number(index);
index++;
} else if (this.tabType === 'assertionsRule' && this.request.hashTree[i].type === 'Assertions') {
this.request.hashTree[i].index = Number(index);
index++;
}
}
},
// body
reloadBody() {
@ -302,4 +479,91 @@ export default {
border: #E6EEF2;
}
.ms-tree >>> .el-tree-node__expand-icon.expanded {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
.ms-tree >>> .el-icon-caret-right:before {
content: '\e723';
font-size: 20px;
}
.ms-tree >>> .el-tree-node__expand-icon.is-leaf {
color: transparent;
}
.ms-tree >>> .el-tree-node__expand-icon {
color: #7C3985;
}
.ms-tree >>> .el-tree-node__expand-icon.expanded.el-icon-caret-right:before {
color: #7C3985;
content: "\e722";
font-size: 20px;
}
/deep/ .el-tree-node__content {
height: 100%;
margin-top: 6px;
vertical-align: center;
}
.ms-step-button {
margin: 6px 0px 8px 30px;
}
.ms-left-cell .el-button:nth-of-type(1) {
color: #B8741A;
background-color: #F9F1EA;
border: #F9F1EA;
}
.ms-left-cell .el-button:nth-of-type(2) {
color: #783887;
background-color: #F2ECF3;
border: #F2ECF3;
}
.ms-left-cell .el-button:nth-of-type(3) {
color: #FE6F71;
background-color: #F9F1EA;
border: #EBF2F2;
}
.ms-left-cell .el-button:nth-of-type(4) {
color: #1483F6;
background-color: #F2ECF3;
border: #F2ECF3;
}
.ms-left-cell .el-button:nth-of-type(5) {
color: #A30014;
background-color: #F7E6E9;
border: #F7E6E9;
}
.ms-left-cell .el-button:nth-of-type(6) {
color: #015478;
background-color: #E6EEF2;
border: #E6EEF2;
}
.ms-left-cell {
margin-top: 0px;
}
.ms-step-tree-cell >>> .el-tree-node__expand-icon {
padding: 0px;
}
.ms-select-step {
margin-left: 15px;
margin-right: 10px;
width: 200px;
}
.ms-assertions-button {
margin-left: 18px;
}
</style>

View File

@ -1234,6 +1234,8 @@ export default {
delete_case_confirm: "Confirm case deletion",
delete_confirm_step: "Confirm deletion step",
assertions_rule: "Assertion rule",
pre_operation: "Pre operation",
post_operation: "Post operation",
response_header: "Response header",
response_body: "Response body",
response_template: "Response template",
@ -1602,7 +1604,8 @@ export default {
expected_results: 'Expected results',
check: 'Check the elements in the array',
add_check: 'Add check',
add_subfield: 'Add subfield'
add_subfield: 'Add subfield',
description: "Add assertion rules to check whether the response result is as expected",
},
extract: {
label: "Extract from response",

View File

@ -1240,6 +1240,8 @@ export default {
delete_case_confirm: "确认删除用例",
delete_confirm_step: "确认删除步骤",
assertions_rule: "断言规则",
pre_operation: "前置操作",
post_operation: "后置操作",
response_header: "响应头",
response_body: "响应体",
response_template: "响应报文模版",
@ -1607,7 +1609,8 @@ export default {
expected_results: '预期结果',
check: '校验组内元素',
add_check: '添加校验',
add_subfield: '添加子字段'
add_subfield: '添加子字段',
description: "添加断言规则校验响应结果是否符合预期",
},
extract: {
label: "提取",

View File

@ -1240,6 +1240,8 @@ export default {
delete_case_confirm: "確認刪除用例",
delete_confirm_step: "確認刪除步驟",
assertions_rule: "斷言規則",
pre_operation: "前置操作",
post_operation: "後置操作",
response_header: "響應頭",
response_body: "響應體",
response_template: "響應報文模版",
@ -1607,7 +1609,8 @@ export default {
expected_results: '預期結果',
check: '校驗組內元素',
add_check: '添加校驗',
add_subfield: '添加子字段'
add_subfield: '添加子字段',
description: "添加斷言規則校驗響應結果是否符合預期",
},
extract: {
label: "提取",