refactor(接口测试): 执行结果显示优化,执行完成立即展示详情

--story=1010816 --user=赵勇 单步调试用例或者场景步骤时响应内容自动展开 https://www.tapd.cn/55049933/s/1319380
This commit is contained in:
fit2-zhao 2022-12-26 14:52:30 +08:00 committed by fit2-zhao
parent 8601141288
commit 9559c2be3d
10 changed files with 11 additions and 1327 deletions

View File

@ -46,6 +46,7 @@ export default {
this.getExecResult();
} else {
this.response = this.result;
this.isActive = true;
}
if (this.apiActive) {
this.isActive = false;
@ -58,7 +59,7 @@ export default {
} else {
this.getExecResult();
}
this.isActive = false;
this.isActive = true;
},
apiItem() {
this.getExecResult();
@ -98,7 +99,7 @@ export default {
}
:deep(.el-card__body) {
padding: 15px;
padding: 10px;
}
.icon.is-active {

View File

@ -285,7 +285,6 @@ import ApiCaseSimpleList from './components/list/ApiCaseSimpleList';
import ApiDocumentsPage from '@/business/definition/components/list/ApiDocumentsPage';
import MsTableButton from 'metersphere-frontend/src/components/MsTableButton';
import MsTabButton from '@/business/commons/MsTabs';
import MockConfig from '@/business/definition/components/mock/MockConfig';
import ApiSchedule from '@/business/definition/components/import/ApiSchedule';
import MsEditCompleteContainer from './components/EditCompleteContainer';
import MsEnvironmentSelect from './components/case/MsEnvironmentSelect';
@ -353,7 +352,6 @@ export default {
MsRunTestSqlPage,
MsRunTestDubboPage,
ApiDocumentsPage,
MockConfig,
MsEditCompleteContainer,
MsEnvironmentSelect,
MockEditDrawer,

View File

@ -96,15 +96,15 @@
v-if="currentProtocol === 'DUBBO'" />
</div>
<div v-if="showMock && (currentProtocol === 'HTTP' || currentProtocol === 'TCP')">
<el-card v-if="showMock && (currentProtocol === 'HTTP' || currentProtocol === 'TCP')">
<mock-tab
:base-mock-config-data="baseMockConfigData"
@redirectToTest="redirectToTest"
:version-name="currentApi.versionName"
:form="currentApi"
:is-tcp="currentProtocol === 'TCP'" />
</div>
<div v-if="showTestCaseList">
</el-card>
<el-card v-if="showTestCaseList">
<!--测试用例列表-->
<api-case-simple-list
:apiDefinitionId="currentApi.id"
@ -116,7 +116,7 @@
@refreshTable="refresh"
@showExecResult="showExecResult"
ref="trashCaseList" />
</div>
</el-card>
<!-- 加载用例 -->
<ms-api-case-list :createCase="createCase" :currentApi="api" @reLoadCase="reLoadCase" ref="caseList" />
</ms-main-container>
@ -132,7 +132,6 @@ import MsRunTestTcpPage from './runtest/RunTestTCPPage';
import MsRunTestSqlPage from './runtest/RunTestSQLPage';
import MsRunTestDubboPage from './runtest/RunTestDubboPage';
import MockTab from '@/business/definition/components/mock/MockTab';
import TcpMockConfig from '@/business/definition/components/mock/TcpMockConfig';
import ApiCaseSimpleList from './list/ApiCaseSimpleList';
import MsApiCaseList from './case/EditApiCase';
import { getUUID } from 'metersphere-frontend/src/utils';
@ -153,7 +152,6 @@ export default {
MsRunTestSqlPage,
MsRunTestDubboPage,
MockTab,
TcpMockConfig,
ApiCaseSimpleList,
MsApiCaseList,
ApiBaseInfo,

View File

@ -1,72 +0,0 @@
<template>
<div role="tablist" aria-multiselectable="true">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'MsApiCollapse',
componentName: 'MsApiCollapse',
props: {
accordion: Boolean,
value: {
type: [Array, String, Number],
default() {
return [];
},
},
},
data() {
return {
activeNames: [].concat(this.value),
};
},
provide() {
return {
collapse: this,
};
},
watch: {
value(value) {
this.activeNames = [].concat(value);
},
},
methods: {
setActiveNames(activeNames, item) {
activeNames = [].concat(activeNames);
let value = this.accordion ? activeNames[0] : activeNames;
this.activeNames = activeNames;
this.$emit('input', value);
},
handleItemCollapseClick(item) {
if (this.accordion) {
this.setActiveNames((this.activeNames[0] || this.activeNames[0] === 0) && item.name, item);
} else {
let activeNames = this.activeNames.slice(0);
let index = activeNames.indexOf(item.name);
if (index > -1) {
activeNames.splice(index, 1);
} else {
activeNames.push(item.name);
}
this.setActiveNames(activeNames, item);
}
},
handleItemClick(item) {
this.$emit('change', item.name);
},
},
created() {
this.$on('item-click', this.handleItemClick);
this.$on('collapse-click', this.handleItemCollapseClick);
},
};
</script>

View File

@ -1,127 +0,0 @@
<template>
<div class="el-collapse-item" :class="{ 'is-active': isActive, 'is-disabled': disabled }">
<div
role="tab"
:aria-expanded="isActive"
:aria-controls="`el-collapse-content-${id}`"
:aria-describedby="`el-collapse-content-${id}`"
@click="handleHeaderClick">
<div
class="el-collapse-item__header"
role="button"
:id="`el-collapse-head-${id}`"
:tabindex="disabled ? undefined : 0"
@keyup.space.enter.stop="handleEnterClick"
:class="{
focusing: focusing,
'is-active': isActive,
}"
@focus="handleFocus"
@blur="focusing = false">
<div @click.stop="handleCollapseClick">
<i class="el-collapse-item__arrow el-icon-arrow-right" :class="{ 'is-active': isActive }"> </i>
</div>
<slot name="title">{{ title }}</slot>
</div>
</div>
<el-collapse-transition>
<div
class="el-collapse-item__wrap"
v-show="isActive"
role="tabpanel"
:aria-hidden="!isActive"
:aria-labelledby="`el-collapse-head-${id}`"
:id="`el-collapse-content-${id}`">
<div class="el-collapse-item__content">
<slot></slot>
</div>
</div>
</el-collapse-transition>
</div>
</template>
<script>
import Emitter from 'element-ui/src/mixins/emitter';
import { generateId } from 'element-ui/src/utils/util';
export default {
name: 'MsApiCollapseItem',
componentName: 'MsApiCollapseItem',
mixins: [Emitter],
data() {
return {
contentWrapStyle: {
height: 'auto',
display: 'block',
},
contentHeight: 0,
focusing: false,
isClick: false,
id: generateId(),
};
},
inject: ['collapse'],
props: {
title: String,
name: {
type: [String, Number],
default() {
return this._uid;
},
},
disabled: Boolean,
},
computed: {
isActive() {
return this.collapse.activeNames.indexOf(this.name) > -1;
},
},
methods: {
handleFocus() {
setTimeout(() => {
if (!this.isClick) {
this.focusing = true;
} else {
this.isClick = false;
}
}, 50);
},
handleHeaderClick() {
if (this.disabled) return;
this.dispatch('MsApiCollapse', 'item-click', this);
this.focusing = false;
this.isClick = true;
},
handleCollapseClick() {
if (this.disabled) return;
this.dispatch('MsApiCollapse', 'collapse-click', this);
this.focusing = false;
this.isClick = true;
},
handleEnterClick() {
this.dispatch('MsApiCollapse', 'item-click', this);
},
},
};
</script>
<style scoped>
.el-collapse-item__header {
padding-left: 7px;
border-right: 2px solid #409eff;
}
.el-collapse-item__header.is-active {
background-color: #e9e9e9;
}
.el-collapse-item__content {
padding-bottom: 0;
}
</style>

View File

@ -23,7 +23,9 @@
</el-radio-group>
<div style="width: 98%" v-if="body.type == 'Form Data' || body.type == 'WWW_FORM'">
<el-row v-if="body.type == 'Form Data' || body.type == 'WWW_FORM'">
<el-link class="ms-el-link" @click="batchAdd"> {{ $t('commons.batch_add') }}</el-link>
<el-link class="ms-el-link" @click="batchAdd">
{{ $t('commons.batch_add') }}
</el-link>
</el-row>
<mock-combination-condition
:filter-type-object="body"

View File

@ -1,354 +0,0 @@
<template xmlns:v-slot="http://www.w3.org/1999/XSL/Transform">
<div>
<el-row>
<el-col :span="spanCount">
<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-tabs>
</div>
</el-col>
</el-row>
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter" />
</div>
</template>
<script>
import { testDataGenerator } from '@/api/xpack';
import MsApiKeyValue from '@/business/definition/components/ApiKeyValue';
import MsApiAuthConfig from '@/business/definition/components/auth/ApiAuthConfig';
import ApiRequestMethodSelect from '@/business/definition/components/collapse/ApiRequestMethodSelect';
import { REQUEST_HEADERS } from 'metersphere-frontend/src/utils/constants';
import MsApiVariable from '@/business/definition/components/ApiVariable';
import MsApiAssertions from '@/business/definition/components/assertion/ApiAssertions';
import MsApiExtract from '@/business/definition/components/extract/ApiExtract';
import { Body, KeyValue } from '@/business/definition/model/ApiTestModel';
import { getUUID } from 'metersphere-frontend/src/utils';
import { hasLicense, hasPermission } from 'metersphere-frontend/src/utils/permission';
import BatchAddParameter from '@/business/definition/components/basis/BatchAddParameter';
import MsApiAdvancedConfig from '@/business/definition/components/request/http/ApiAdvancedConfig';
import MsJsr233Processor from '@/business/automation/scenario/component/Jsr233Processor';
import ApiDefinitionStepButton from '@/business/definition/components/request/components/ApiDefinitionStepButton';
import Convert from '@/business/commons/json-schema/convert/convert';
import MockApiBody from '@/business/definition/components/mock/Components/MockApiBody';
export default {
name: 'MockRequestParam',
components: {
ApiDefinitionStepButton,
MsJsr233Processor,
MsApiAdvancedConfig,
BatchAddParameter,
MsApiVariable,
ApiRequestMethodSelect,
MsApiExtract,
MsApiAuthConfig,
MockApiBody,
MsApiKeyValue,
MsApiAssertions,
},
props: {
method: String,
request: {},
response: {},
definitionTest: {
type: Boolean,
default() {
return false;
},
},
showScript: {
type: Boolean,
default: true,
},
referenced: {
type: Boolean,
default: false,
},
isShowEnable: Boolean,
jsonPathList: Array,
isReadOnly: {
type: Boolean,
default: false,
},
type: String,
},
data() {
let validateURL = (rule, value, callback) => {
try {
new URL(this.addProtocol(this.request.url));
} catch (e) {
callback(this.$t('api_test.request.url_invalid'));
}
};
return {
activeName: this.request.method === 'POST' ? 'body' : 'parameters',
rules: {
name: [
{
max: 300,
message: this.$t('commons.input_limit', [1, 300]),
trigger: 'blur',
},
],
url: [
{
max: 500,
required: true,
message: this.$t('commons.input_limit', [1, 500]),
trigger: 'blur',
},
{ validator: validateURL, trigger: 'blur' },
],
path: [
{
max: 500,
message: this.$t('commons.input_limit', [0, 500]),
trigger: 'blur',
},
],
},
spanCount: 21,
headerSuggestions: REQUEST_HEADERS,
isReloadData: false,
isBodyShow: true,
dialogVisible: false,
hasOwnProperty: Object.prototype.hasOwnProperty,
propIsEnumerable: Object.prototype.propertyIsEnumerable,
};
},
created() {
if (!this.referenced && this.showScript) {
this.spanCount = 21;
} else {
this.spanCount = 24;
}
this.init();
},
methods: {
hasPermission,
hasLicense,
generate() {
if (this.request.body && (this.request.body.jsonSchema || this.request.body.raw)) {
if (!this.request.body.jsonSchema) {
const MsConvert = new Convert();
this.request.body.jsonSchema = MsConvert.format(JSON.parse(this.request.body.raw));
}
testDataGenerator(this.request.body.jsonSchema).then((response) => {
if (response.data) {
if (this.request.body.format !== 'JSON-SCHEMA') {
this.request.body.raw = response.data;
} else {
const MsConvert = new Convert();
let data = MsConvert.format(JSON.parse(response.data));
this.request.body.jsonSchema = this.deepAssign(this.request.body.jsonSchema, data);
}
this.reloadBody();
}
});
}
},
remove(row) {
let index = this.request.hashTree.indexOf(row);
this.request.hashTree.splice(index, 1);
this.reload();
},
copyRow(row) {
let obj = JSON.parse(JSON.stringify(row));
obj.id = getUUID();
this.request.hashTree.push(obj);
this.reload();
},
reload() {
this.isReloadData = true;
this.$nextTick(() => {
this.isReloadData = false;
});
},
init() {
if (
Object.prototype.toString
.call(this.request)
.match(/\[object (\w+)\]/)[1]
.toLowerCase() !== 'object'
) {
this.request = JSON.parse(this.request);
}
if (!this.request.body) {
this.request.body = new Body();
}
if (!this.request.headers) {
this.request.headers = [];
}
if (!this.request.body.kvs) {
this.request.body.kvs = [];
}
if (!this.request.rest) {
this.request.rest = [];
}
if (!this.request.arguments) {
this.request.arguments = [];
}
},
reloadBody() {
// body
this.isBodyShow = false;
this.$nextTick(() => {
this.isBodyShow = true;
});
},
batchAdd() {
this.$refs.batchAddParameter.open();
},
format(array, obj) {
if (array) {
let isAdd = true;
for (let i in array) {
let item = array[i];
if (item.name === obj.name) {
item.value = obj.value;
isAdd = false;
}
}
if (isAdd) {
switch (this.activeName) {
case 'parameters':
this.request.arguments.unshift(obj);
break;
case 'rest':
this.request.rest.unshift(obj);
break;
case 'headers':
this.request.headers.unshift(obj);
break;
default:
break;
}
}
}
},
batchSave(data) {
if (data) {
let params = data.split('\n');
let keyValues = [];
params.forEach((item) => {
let line = item.split(/|:/);
let required = false;
keyValues.unshift(
new KeyValue({
name: line[0],
required: required,
value: line[1],
description: line[2],
type: 'text',
valid: false,
file: false,
encode: true,
enable: true,
contentType: 'text/plain',
})
);
});
keyValues.forEach((item) => {
switch (this.activeName) {
case 'parameters':
this.format(this.request.arguments, item);
break;
case 'rest':
this.format(this.request.rest, item);
break;
case 'headers':
this.format(this.request.headers, item);
break;
default:
break;
}
});
}
},
isObj(x) {
let type = typeof x;
return x !== null && (type === 'object' || type === 'function');
},
toObject(val) {
if (val === null || val === undefined) {
return;
}
return Object(val);
},
assignKey(to, from, key) {
let val = from[key];
if (val === undefined || val === null) {
return;
}
if (!this.hasOwnProperty.call(to, key) || !this.isObj(val)) {
to[key] = val;
} else {
to[key] = this.assign(Object(to[key]), from[key]);
}
},
assign(to, from) {
if (to === from) {
return to;
}
from = Object(from);
for (let key in from) {
if (this.hasOwnProperty.call(from, key)) {
this.assignKey(to, from, key);
}
}
if (Object.getOwnPropertySymbols) {
let symbols = Object.getOwnPropertySymbols(from);
for (let i = 0; i < symbols.length; i++) {
if (this.propIsEnumerable.call(from, symbols[i])) {
this.assignKey(to, from, symbols[i]);
}
}
}
return to;
},
deepAssign(target) {
target = this.toObject(target);
for (let s = 1; s < arguments.length; s++) {
this.assign(target, arguments[s]);
}
return target;
},
},
};
</script>
<style scoped>
.ms-query {
background: #783887;
color: white;
height: 18px;
border-radius: 42%;
}
.ms-header {
background: #783887;
color: white;
height: 18px;
border-radius: 42%;
}
.request-tabs {
margin: 10px;
min-height: 200px;
}
.ms-el-link {
float: right;
margin-right: 45px;
}
</style>

View File

@ -1,370 +0,0 @@
<template>
<div class="card-container">
<el-card class="card-content" v-loading="mockConfigData.loading">
<p class="tip">期望列表</p>
<div class="card">
<el-input
:placeholder="$t('commons.search_by_name')"
class="search-input"
size="small"
:clearable="serchInputClearable"
v-model="tableSearch" />
<el-table
ref="table"
border
:data="
mockConfigData.mockExpectConfigList.filter(
(data) => !tableSearch || data.name.toLowerCase().includes(tableSearch.toLowerCase())
)
"
@row-click="clickRow"
row-key="id"
class="test-content"
:height="screenHeight">
<el-table-column :label="$t('api_test.mock.table.name')" min-width="160px" prop="name"></el-table-column>
<el-table-column :label="$t('api_test.mock.table.tag')" min-width="200px" prop="tags">
<template v-slot:default="scope">
<ms-tag
v-for="(itemName, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:show-tooltip="true"
:content="itemName"
style="margin-left: 0px; margin-right: 2px" />
</template>
</el-table-column>
<el-table-column
:label="$t('api_test.mock.table.creator')"
min-width="160px"
prop="createUserId"></el-table-column>
<el-table-column :label="$t('api_test.mock.table.status')" min-width="80px" prop="status">
<template v-slot:default="scope">
<div>
<el-switch v-model="scope.row.status" class="captcha-img" @change="changeStatus(scope.row)"></el-switch>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('api_test.mock.table.update_time')" min-width="160px" prop="updateTime">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" min-width="100" align="center" :label="$t('commons.operating')">
<template v-slot:default="scope">
<div>
<ms-table-operator-button
:tip="$t('commons.copy')"
icon="el-icon-copy-document"
@exec="copyExpect(scope.row)" />
<ms-table-operator-button
:tip="$t('commons.delete')"
icon="el-icon-delete"
@exec="removeExpect(scope.row)" />
</div>
</template>
</el-table-column>
</el-table>
</div>
<!-- 期望详情 -->
<p class="tip">{{ $t('api_test.mock.expect_detail') }}</p>
<el-form :model="mockExpectConfig" :rules="rule" ref="mockExpectForm" label-width="80px" label-position="right">
<div class="card">
<div class="base-info">
<el-row>
<el-col>{{ $t('api_test.mock.base_info') }}</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input class="ms-http-input" size="small" v-model="mockExpectConfig.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('commons.tag')" prop="tag">
<ms-input-tag :currentScenario="mockExpectConfig" v-if="showHeadTable" ref="tag" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>{{ $t('api_test.mock.req_param') }}</el-col>
</el-row>
<el-row>
<el-col style="margin: 10px">
<el-switch v-model="mockExpectConfig.request.jsonParam"> </el-switch>
JSON
</el-col>
</el-row>
<el-row>
<div v-if="mockExpectConfig.request.jsonParam">
<ms-code-edit
height="400px"
:mode="'json'"
ref="codeEdit"
:data.sync="mockExpectConfig.request.jsonData"
style="margin-top: 10px" />
</div>
<div v-else>
<mock-row-variables
:show-copy="false"
v-if="showHeadTable"
:header-suggestions="apiParams"
:items="mockExpectConfig.request.variables"
ref="reqHttpHead" />
</div>
</el-row>
<el-row style="margin-top: 10px">
<el-col>{{ $t('api_test.mock.rsp_param') }}</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="HTTP Code" label-width="100px" prop="response.httpCode">
<el-input class="ms-http-input" size="small" v-model="mockExpectConfig.response.httpCode" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="延时 (ms)" prop="response.delayed">
<el-input-number v-model="mockExpectConfig.response.delayed" :min="0">
<template slot="append">ms</template>
</el-input-number>
</el-form-item>
</el-col>
</el-row>
<el-row>
<span style="margin: 10px; font-size: 13px"> HTTP头: </span>
</el-row>
<el-row>
<mock-row-variables
v-if="showHeadTable"
:show-copy="false"
:header-suggestions="headerSuggestions"
:items="mockExpectConfig.response.httpHeads"
ref="rspHttpHead" />
</el-row>
<el-row style="margin-top: 10px">
<el-form-item label="Body:" label-width="50px">
<ms-code-edit
height="200px"
:mode="'txt'"
ref="codeEdit"
:data.sync="mockExpectConfig.response.body"
style="margin-top: 10px" />
</el-form-item>
</el-row>
<el-row>
<div style="float: right; margin-right: 20px">
<el-button type="primary" size="small" @click="saveMockExpectConfig" title="ctrl + s"
>{{ $t('commons.add') }}
</el-button>
<el-button type="primary" size="small" @click="cleanMockExpectConfig"
>{{ $t('commons.clear') }}
</el-button>
<el-button
type="primary"
size="small"
v-if="mockExpectConfig.id != '' && mockExpectConfig.id != null"
@click="updateMockExpectConfig"
>{{ $t('commons.update') }}
</el-button>
</div>
</el-row>
</div>
</div>
</el-form>
</el-card>
</div>
</template>
<script>
import { createMockConfig, delMock, getMockApiParams, updateMockExpectConfig } from '@/api/api-mock';
import MsTableOperatorButton from 'metersphere-frontend/src/components/MsTableOperatorButton';
import MockRowVariables from '@/business/definition/components/mock/MockRowVariables';
import MsInputTag from '@/business/automation/scenario/MsInputTag';
import MsCodeEdit from '@/business/definition/components/MsCodeEdit';
import MsApiVariableAdvance from 'metersphere-frontend/src/components/environment/commons/ApiVariableAdvance';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import { REQUEST_HEADERS } from 'metersphere-frontend/src/utils/constants';
import { operationConfirm } from 'metersphere-frontend/src/utils';
export default {
name: 'MockConfig',
components: {
MockRowVariables,
MsTableOperatorButton,
MsInputTag,
MsCodeEdit,
MsApiVariableAdvance,
MsTag,
},
data() {
return {
screenHeight: 300,
tableSearch: '',
showHeadTable: true,
serchInputClearable: true,
mockConfigData: {},
apiParams: [],
headerSuggestions: REQUEST_HEADERS,
mockExpectConfig: {
id: '',
name: '',
mockConfigId: '',
request: {
jsonParam: false,
variables: [],
jsonData: '{}',
},
response: {
httpCode: '',
delayed: '',
httpHeads: [],
body: '',
},
},
rule: {
name: [
{
required: true,
message: this.$t('test_track.case.input_name'),
trigger: 'blur',
},
{
max: 100,
message: this.$t('test_track.length_less_than') + '100',
trigger: 'blur',
},
],
response: {
httpCode: [
{
required: true,
message: this.$t('api_test.mock.rule.input_code'),
trigger: 'blur',
},
],
delayed: [
{
required: true,
message: this.$t('test_track.case.input_name'),
trigger: 'blur',
},
],
},
},
};
},
props: { baseMockConfigData: {} },
created() {
this.mockConfigData = this.baseMockConfigData;
this.searchApiParams(this.mockConfigData.mockConfig.apiId);
},
methods: {
searchApiParams(apiId) {
getMockApiParams(apiId).then((response) => {
this.apiParams = response.data;
});
},
removeExpect(row) {
operationConfirm(this, this.$t('api_test.mock.delete_mock_expect'), () => {
let mockInfoId = row.mockConfigId;
delMock(row.id).then((response) => {
this.cleanMockExpectConfig();
this.refreshMockInfo(mockInfoId);
this.$message({
type: 'success',
message: this.$t('commons.delete_success'),
});
});
});
},
saveMockExpectConfig() {
let mockConfigId = this.mockConfigData.mockConfig.id;
this.mockExpectConfig.mockConfigId = mockConfigId;
this.mockExpectConfig.id = '';
let formCheckResult = this.checkMockExpectForm('mockExpectForm', true);
},
cleanMockExpectConfig() {
this.showHeadTable = false;
this.mockExpectConfig = {
id: '',
name: '',
mockConfigId: '',
request: {
jsonParam: false,
variables: [],
jsonData: '{}',
},
response: {
httpCode: '',
delayed: '',
httpHeads: [],
body: '',
},
};
this.$nextTick(function () {
this.showHeadTable = true;
});
},
updateMockExpectConfig() {
this.checkMockExpectForm('mockExpectForm');
},
uploadMockExpectConfig(clearForm) {
let param = this.mockExpectConfig;
updateMockExpectConfig(param).then((response) => {
let returnData = response.data;
this.mockExpectConfig.id = returnData.id;
this.refreshMockInfo(param.mockConfigId);
if (clearForm) {
this.cleanMockExpectConfig();
}
this.$message({
type: 'success',
message: this.$t('commons.save_success'),
});
});
},
refreshMockInfo(mockConfigId) {
let mockParam = {};
mockParam.id = mockConfigId;
createMockConfig(mockParam).then((response) => {
this.mockConfigData = response.data;
});
},
checkMockExpectForm(formName, clearForm) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.uploadMockExpectConfig(clearForm);
return true;
} else {
return false;
}
});
},
},
};
</script>
<style scoped>
.search-input {
float: right;
width: 300px;
margin-right: 10px;
margin-bottom: 10px;
}
.base-info .el-form-item {
width: 100%;
}
.base-info .el-form-item :deep(.el-form-item__content) {
width: 80%;
}
.base-info .ms-http-select {
width: 100%;
}
</style>

View File

@ -135,7 +135,7 @@ export default {
tableSearch: '',
apiParams: {},
pageSize: 10,
screenHeight: document.documentElement.clientHeight - 250,
screenHeight: 'calc(100vh - 205px)',
operators: [
{
tip: this.$t('api_test.automation.execute'),

View File

@ -1,392 +0,0 @@
<template>
<div class="card-container">
<el-card class="card-content" v-loading="mockConfigData.loading">
<p class="tip">期望列表</p>
<div class="card">
<el-input
:placeholder="$t('commons.search_by_name')"
class="search-input"
size="small"
:clearable="serchInputClearable"
v-model="tableSearch" />
<el-table
ref="table"
border
:data="
mockConfigData.mockExpectConfigList.filter(
(data) => !tableSearch || data.name.toLowerCase().includes(tableSearch.toLowerCase())
)
"
@row-click="clickRow"
row-key="id"
class="test-content"
:height="screenHeight">
<el-table-column :label="$t('api_test.mock.table.name')" min-width="160px" prop="name"></el-table-column>
<el-table-column :label="$t('api_test.mock.table.tag')" min-width="200px" prop="tags">
<template v-slot:default="scope">
<ms-tag
v-for="(itemName, index) in scope.row.tags"
:key="index"
type="success"
effect="plain"
:show-tooltip="true"
:content="itemName"
style="margin-left: 0px; margin-right: 2px" />
</template>
</el-table-column>
<el-table-column
:label="$t('api_test.mock.table.creator')"
min-width="160px"
prop="createUserId"></el-table-column>
<el-table-column :label="$t('api_test.mock.table.status')" min-width="80px" prop="status">
<template v-slot:default="scope">
<div>
<el-switch v-model="scope.row.status" class="captcha-img" @change="changeStatus(scope.row)"></el-switch>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('api_test.mock.table.update_time')" min-width="160px" prop="updateTime">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | datetimeFormat }}</span>
</template>
</el-table-column>
<el-table-column fixed="right" min-width="100" align="center" :label="$t('commons.operating')">
<template v-slot:default="scope">
<div>
<ms-table-operator-button
:tip="$t('commons.copy')"
icon="el-icon-copy-document"
@exec="copyExpect(scope.row)" />
<ms-table-operator-button
:tip="$t('commons.delete')"
icon="el-icon-delete"
@exec="removeExpect(scope.row)" />
</div>
</template>
</el-table-column>
</el-table>
</div>
<!-- 期望详情 -->
<p class="tip">{{ $t('api_test.mock.expect_detail') }}</p>
<el-form :model="mockExpectConfig" :rules="rule" ref="mockExpectForm" label-width="80px" label-position="right">
<div class="card">
<div class="base-info">
<el-row>
<el-col>{{ $t('api_test.mock.base_info') }}</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('commons.name')" prop="name">
<el-input class="ms-http-input" size="small" v-model="mockExpectConfig.name" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('commons.tag')" prop="tag">
<ms-input-tag :currentScenario="mockExpectConfig" v-if="showHeadTable" ref="tag" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col>{{ $t('api_test.mock.req_param') }}</el-col>
</el-row>
<el-row>
<tcp-params :request="mockExpectConfig.request" style="margin: 10px 10px" ref="tcpParam"></tcp-params>
</el-row>
<el-row style="margin-top: 10px">
<el-col>{{ $t('api_test.mock.rsp_param') }}</el-col>
</el-row>
<el-row>
<el-form-item label="延时 (ms)" prop="response.delayed">
<el-input-number v-model="mockExpectConfig.response.delayed" :min="0">
<template slot="append">ms</template>
</el-input-number>
</el-form-item>
</el-row>
<el-row style="margin-top: 10px">
<el-form-item label="Body:" label-width="50px">
<ms-code-edit
height="200px"
:mode="'txt'"
ref="codeEdit"
:data.sync="mockExpectConfig.response.body"
style="margin-top: 10px" />
</el-form-item>
</el-row>
<el-row>
<div style="float: right; margin-right: 20px">
<el-button type="primary" size="small" @click="saveMockExpectConfig" title="ctrl + s"
>{{ $t('commons.add') }}
</el-button>
<el-button type="primary" size="small" @click="cleanMockExpectConfig"
>{{ $t('commons.clear') }}
</el-button>
<el-button
type="primary"
size="small"
v-if="mockExpectConfig.id != '' && mockExpectConfig.id != null"
@click="updateMockExpectConfig"
>{{ $t('commons.update') }}
</el-button>
</div>
</el-row>
</div>
</div>
</el-form>
</el-card>
</div>
</template>
<script>
import { createMockConfig, delMock, mockExpectConfig, updateMockExpectConfig } from '@/api/api-mock';
import MsTableOperatorButton from 'metersphere-frontend/src/components/MsTableOperatorButton';
import MockRowVariables from '@/business/definition/components/mock/MockRowVariables';
import MsInputTag from '@/business/automation/scenario/MsInputTag';
import MsCodeEdit from '@/business/definition/components/MsCodeEdit';
import MsApiVariableAdvance from 'metersphere-frontend/src/components/environment/commons/ApiVariableAdvance';
import MsTag from 'metersphere-frontend/src/components/MsTag';
import { REQUEST_HEADERS } from 'metersphere-frontend/src/utils/constants';
import TcpParams from '@/business/definition/components/request/tcp/TcpParams';
import { operationConfirm } from 'metersphere-frontend/src/utils';
export default {
name: 'TcpMockConfig',
components: {
MockRowVariables,
MsTableOperatorButton,
MsInputTag,
MsCodeEdit,
MsApiVariableAdvance,
TcpParams,
MsTag,
},
data() {
return {
screenHeight: 300,
tableSearch: '',
showHeadTable: true,
serchInputClearable: true,
mockConfigData: {},
apiParams: [],
headerSuggestions: REQUEST_HEADERS,
mockExpectConfig: {
id: '',
name: '',
mockConfigId: '',
request: {
reportType: 'raw',
xmlDataStruct: [],
jsonDataStruct: '',
rawDataStruct: '',
},
response: {
httpCode: '',
delayed: '',
httpHeads: [],
body: '',
},
},
rule: {
name: [
{
required: true,
message: this.$t('test_track.case.input_name'),
trigger: 'blur',
},
{
max: 100,
message: this.$t('test_track.length_less_than') + '100',
trigger: 'blur',
},
],
response: {
httpCode: [
{
required: true,
message: this.$t('api_test.mock.rule.input_code'),
trigger: 'blur',
},
],
delayed: [
{
required: true,
message: this.$t('test_track.case.input_name'),
trigger: 'blur',
},
],
},
},
};
},
props: {
baseMockConfigData: {},
type: String,
},
created() {
this.mockConfigData = this.baseMockConfigData;
// this.searchApiParams(this.mockConfigData.mockConfig.apiId);
},
methods: {
copyExpect(row) {
mockExpectConfig(row.id).then((response) => {
let data = response.data;
this.showHeadTable = false;
this.mockExpectConfig = data;
this.mockExpectConfig.id = '';
this.mockExpectConfig.name = this.mockExpectConfig.name + '_copy';
if (this.mockExpectConfig.request == null) {
this.mockExpectConfig.request = {
jsonParam: false,
variables: [],
jsonData: '{}',
};
}
if (this.mockExpectConfig.response == null) {
this.mockExpectConfig.response = {
httpCode: '',
delayed: '',
httpHeads: [],
body: '',
};
}
this.$nextTick(function () {
this.showHeadTable = true;
this.saveMockExpectConfig();
});
});
},
removeExpect(row) {
operationConfirm(this, this.$t('api_test.mock.delete_mock_expect'), () => {
let mockInfoId = row.mockConfigId;
delMock(row.id).then((response) => {
this.cleanMockExpectConfig();
this.refreshMockInfo(mockInfoId);
this.$message({
type: 'success',
message: this.$t('commons.delete_success'),
});
});
});
},
saveMockExpectConfig() {
let mockConfigId = this.mockConfigData.mockConfig.id;
this.mockExpectConfig.mockConfigId = mockConfigId;
this.mockExpectConfig.id = '';
let formCheckResult = this.checkMockExpectForm('mockExpectForm', true);
},
cleanMockExpectConfig() {
this.showHeadTable = false;
this.mockExpectConfig = {
id: '',
name: '',
mockConfigId: '',
request: {
reportType: 'raw',
xmlDataStruct: [],
jsonDataStruct: '',
rawDataStruct: '',
},
response: {
httpCode: '',
delayed: '',
httpHeads: [],
body: '',
},
};
this.$nextTick(function () {
this.$refs.tcpParam.reload();
this.showHeadTable = true;
});
},
updateMockExpectConfig() {
this.checkMockExpectForm('mockExpectForm');
},
uploadMockExpectConfig(clearForm) {
let param = this.mockExpectConfig;
updateMockExpectConfig(param).then((response) => {
let returnData = response.data;
this.mockExpectConfig.id = returnData.id;
this.refreshMockInfo(param.mockConfigId);
if (clearForm) {
this.cleanMockExpectConfig();
}
this.$message({
type: 'success',
message: this.$t('commons.save_success'),
});
});
},
refreshMockInfo(mockConfigId) {
let mockParam = {};
mockParam.id = mockConfigId;
createMockConfig(mockParam).then((response) => {
this.mockConfigData = response.data;
});
},
checkMockExpectForm(formName, clearForm) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.uploadMockExpectConfig(clearForm);
return true;
} else {
return false;
}
});
},
changeStatus(row) {
let mockParam = {};
mockParam.id = row.id;
mockParam.status = row.status;
updateMockExpectConfig(mockParam).then((response) => {});
},
clickRow(row, column, event) {
this.cleanMockExpectConfig();
mockExpectConfig(row.id).then((response) => {
let data = response.data;
this.showHeadTable = false;
this.mockExpectConfig = data;
if (this.mockExpectConfig.request == null) {
this.mockExpectConfig.request = {
reportType: 'raw',
xmlDataStruct: [],
};
}
if (this.mockExpectConfig.response == null) {
this.mockExpectConfig.response = {
httpCode: '',
delayed: '',
httpHeads: [],
body: '',
};
}
this.$nextTick(function () {
this.$refs.tcpParam.reload();
this.showHeadTable = true;
});
});
},
},
};
</script>
<style scoped>
.search-input {
float: right;
width: 300px;
margin-right: 10px;
margin-bottom: 10px;
}
.base-info .el-form-item {
width: 100%;
}
.base-info .el-form-item :deep(.el-form-item__content) {
width: 80%;
}
.base-info .ms-http-select {
width: 100%;
}
</style>