feat(接口测试):增加接口测试一键运行功能

This commit is contained in:
wenyann 2020-08-11 14:40:18 +08:00
parent 3fff716881
commit 66b6c179e8
16 changed files with 594 additions and 208 deletions

View File

@ -80,6 +80,7 @@ public class APITestController {
return apiTestService.get(testId); return apiTestService.get(testId);
} }
@PostMapping("/delete") @PostMapping("/delete")
public void delete(@RequestBody DeleteAPITestRequest request) { public void delete(@RequestBody DeleteAPITestRequest request) {
apiTestService.delete(request.getId()); apiTestService.delete(request.getId());

View File

@ -66,7 +66,7 @@
<style scoped> <style scoped>
.el-table { .el-table {
cursor:pointer; cursor: pointer;
} }
</style> </style>

View File

@ -9,18 +9,43 @@
<span class="time">{{ report.createTime | timestampFormatDate }}</span> <span class="time">{{ report.createTime | timestampFormatDate }}</span>
</header> </header>
<main v-if="this.isNotRunning"> <main v-if="this.isNotRunning">
<ms-metric-chart :content="content"/> <ms-metric-chart :content="content" :totalTime="totalTime"/>
<el-tabs v-model="activeName"> <!--<el-container>
<el-tab-pane :label="$t('api_report.total')" name="total"> <el-aside width="900px">
<ms-scenario-results :scenarios="content.scenarios"/> <el-tabs v-model="activeName">
</el-tab-pane> <el-tab-pane :label="$t('api_report.total')" name="total">
<el-tab-pane name="fail"> <ms-scenario-results :scenarios="content.scenarios"/>
<template slot="label"> </el-tab-pane>
<span class="fail">{{ $t('api_report.fail') }}</span> <el-tab-pane name="fail">
</template> <template slot="label">
<ms-scenario-results :scenarios="fails"/> <span class="fail">{{ $t('api_report.fail') }}</span>
</el-tab-pane> </template>
</el-tabs> <ms-scenario-results :scenarios="fails"/>
</el-tab-pane>
</el-tabs>
</el-aside>
<el-main style="margin-top: 20px">
</el-main>
</el-container>-->
<el-row :gutter="20">
<el-col :span="8">
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('api_report.total')" name="total">
<ms-scenario-results :scenarios="content.scenarios" v-on:requestResult="requestResult"/>
</el-tab-pane>
<el-tab-pane name="fail">
<template slot="label">
<span class="fail">{{ $t('api_report.fail') }}</span>
</template>
<ms-scenario-results v-on:requestResult="requestResult" :scenarios="fails"/>
</el-tab-pane>
</el-tabs>
</el-col>
<el-col :span="16" style="margin-top: 40px;">
<ms-request-result-tail v-if="isRequestResult" :request="request" :scenario-name="scenarioName"/>
</el-col>
</el-row>
</main> </main>
</section> </section>
</el-card> </el-card>
@ -30,131 +55,142 @@
<script> <script>
import MsRequestResult from "./components/RequestResult"; import MsRequestResult from "./components/RequestResult";
import MsScenarioResult from "./components/ScenarioResult"; import MsRequestResultTail from "./components/RequestResultTail";
import MsMetricChart from "./components/MetricChart"; import MsScenarioResult from "./components/ScenarioResult";
import MsScenarioResults from "./components/ScenarioResults"; import MsMetricChart from "./components/MetricChart";
import MsScenarioResults from "./components/ScenarioResults";
export default { export default {
name: "MsApiReportView", name: "MsApiReportView",
components: {MsScenarioResults, MsMetricChart, MsScenarioResult, MsRequestResult}, components: {MsScenarioResults, MsRequestResultTail, MsMetricChart, MsScenarioResult, MsRequestResult},
data() { data() {
return { return {
activeName: "total", activeName: "total",
content: {}, content: {},
report: {}, report: {},
loading: true, loading: true,
fails: [] fails: [],
} totalTime: "",
}, isRequestResult: false,
request: {},
methods: { scenarioName: null,
init() {
this.loading = true;
this.report = {};
this.content = {};
this.fails = [];
},
getReport() {
this.init();
if (this.reportId) {
let url = "/api/report/get/" + this.reportId;
this.$get(url, response => {
this.report = response.data || {};
if (response.data) {
if (this.isNotRunning) {
try {
this.content = JSON.parse(this.report.content);
} catch (e) {
console.log(this.report.content)
throw e;
}
this.getFails();
this.loading = false;
} else {
setTimeout(this.getReport, 2000)
}
} else {
this.loading = false;
this.$error(this.$t('api_report.not_exist'));
}
});
} }
}, },
getFails() {
if (this.isNotRunning) { methods: {
init() {
this.loading = true;
this.report = {};
this.content = {};
this.fails = []; this.fails = [];
this.content.scenarios.forEach((scenario) => { },
let failScenario = Object.assign({}, scenario); getReport() {
if (scenario.error > 0) { this.init();
this.fails.push(failScenario);
failScenario.requestResults = []; if (this.reportId) {
scenario.requestResults.forEach((request) => { let url = "/api/report/get/" + this.reportId;
if (!request.success) { this.$get(url, response => {
let failRequest = Object.assign({}, request); this.report = response.data || {};
failScenario.requestResults.push(failRequest); if (response.data) {
if (this.isNotRunning) {
try {
this.content = JSON.parse(this.report.content);
} catch (e) {
console.log(this.report.content)
throw e;
}
this.getFails();
this.loading = false;
} else {
setTimeout(this.getReport, 2000)
} }
}) } else {
this.loading = false;
this.$error(this.$t('api_report.not_exist'));
}
});
}
},
getFails() {
if (this.isNotRunning) {
this.fails = [];
this.content.scenarios.forEach((scenario) => {
this.totalTime = scenario.responseTime + scenario.responseTime
let failScenario = Object.assign({}, scenario);
if (scenario.error > 0) {
this.fails.push(failScenario);
failScenario.requestResults = [];
scenario.requestResults.forEach((request) => {
if (!request.success) {
let failRequest = Object.assign({}, request);
failScenario.requestResults.push(failRequest);
}
})
} }
}) })
}
},
requestResult(requestResult) {
this.isRequestResult = true;
this.request = requestResult.request;
this.scenarioName = requestResult.scenarioName;
} }
}
},
watch: {
'$route': 'getReport',
},
created() {
this.getReport();
},
computed: {
reportId: function () {
return this.$route.params.reportId;
}, },
path() {
return "/api/test/edit?id=" + this.report.testId; watch: {
'$route': 'getReport',
}, },
isNotRunning() {
return "Running" !== this.report.status; created() {
this.getReport();
},
computed: {
reportId: function () {
return this.$route.params.reportId;
},
path() {
return "/api/test/edit?id=" + this.report.testId;
},
isNotRunning() {
return "Running" !== this.report.status;
}
} }
} }
}
</script> </script>
<style> <style>
.report-container .el-tabs__header { .report-container .el-tabs__header {
margin-bottom: 1px; margin-bottom: 1px;
} }
</style> </style>
<style scoped> <style scoped>
.report-container { .report-container {
height: calc(100vh - 150px); height: calc(100vh - 150px);
min-height: 600px; min-height: 600px;
overflow-y: auto; overflow-y: auto;
} }
.report-header { .report-header {
font-size: 15px; font-size: 15px;
} }
.report-header a { .report-header a {
text-decoration: none; text-decoration: none;
} }
.report-header .time { .report-header .time {
color: #909399; color: #909399;
margin-left: 10px; margin-left: 10px;
} }
.report-container .fail { .report-container .fail {
color: #F56C6C; color: #F56C6C;
} }
.report-container .is-active .fail { .report-container .is-active .fail {
color: inherit; color: inherit;
} }
</style> </style>

View File

@ -3,6 +3,9 @@
<el-row type="flex" align="middle"> <el-row type="flex" align="middle">
<div style="width: 50%"> <div style="width: 50%">
<el-row type="flex" justify="center" align="middle"> <el-row type="flex" justify="center" align="middle">
<div class="metric-time">
<div class="value" style="margin-left: -150px">{{this.minutes}}min{{this.seconds}}s</div>
</div>
<chart id="chart" ref="chart" :options="options" :autoresize="true"></chart> <chart id="chart" ref="chart" :options="options" :autoresize="true"></chart>
<el-row type="flex" justify="center" align="middle"> <el-row type="flex" justify="center" align="middle">
<i class="circle success"/> <i class="circle success"/>
@ -48,9 +51,15 @@
name: "MsMetricChart", name: "MsMetricChart",
props: { props: {
content: Object content: Object,
totalTime: Number
},
data() {
return {
minutes: Math.floor(this.totalTime / 60),
seconds: this.totalTime % 60,
}
}, },
computed: { computed: {
options() { options() {
return { return {
@ -160,6 +169,12 @@
letter-spacing: -.5px; letter-spacing: -.5px;
} }
.metric-time .value {
font-size: 25px;
font-weight: 400;
letter-spacing: -.5px;
}
.metric-box .name { .metric-box .name {
font-size: 16px; font-size: 16px;
letter-spacing: -.2px; letter-spacing: -.2px;

View File

@ -7,55 +7,14 @@
{{request.method}} {{request.method}}
</div> </div>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="20">
<div class="name">{{request.name}}</div> <div class="name">{{request.name}}</div>
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800"> <el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
<div class="url">{{request.url}}</div> <div class="url">{{request.url}}</div>
</el-tooltip> </el-tooltip>
</el-col> </el-col>
<el-col :span="2">
<div class="time">
{{request.responseResult.responseTime}}
</div>
</el-col>
<el-col :span="2">
{{request.error}}
</el-col>
<el-col :span="2">
{{assertion}}
</el-col>
<el-col :span="2">
<el-tag size="mini" type="success" v-if="request.success">
{{$t('api_report.success')}}
</el-tag>
<el-tag size="mini" type="danger" v-else>
{{$t('api_report.fail')}}
</el-tag>
</el-col>
</el-row> </el-row>
</div> </div>
<el-collapse-transition>
<div v-show="isActive">
<el-tabs v-model="activeName" v-show="isActive" v-if="hasSub">
<el-tab-pane :label="$t('api_report.sub_result')" name="sub">
<ms-request-result class="sub-result" v-for="(sub, index) in request.subRequestResults"
:key="index" :request="sub"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_report.request_result')" name="result">
<ms-request-metric :request="request"/>
<ms-request-text :request="request"/>
<br>
<ms-response-text :response="request.responseResult"/>
</el-tab-pane>
</el-tabs>
<div v-else>
<ms-request-metric :request="request"/>
<ms-request-text :request="request"/>
<br>
<ms-response-text :response="request.responseResult"/>
</div>
</div>
</el-collapse-transition>
</div> </div>
</template> </template>
@ -69,19 +28,17 @@
name: "MsRequestResult", name: "MsRequestResult",
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric}, components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric},
props: { props: {
request: Object request: Object,
scenarioName: String
}, },
data() { data() {
return { return {}
isActive: false,
activeName: "sub",
}
}, },
methods: { methods: {
active() { active() {
this.isActive = !this.isActive; this.$emit("requestResult", {request: this.request, scenarioName: this.scenarioName});
} }
}, },

View File

@ -0,0 +1,177 @@
<template>
<div class="request-result">
<div @click="active">
<el-row :gutter="10" type="flex" align="middle" class="info">
<el-col :span="16">
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}"/>
{{scenarioName}}
</el-col>
<el-col :span="2">
{{$t('api_report.response_time')}}
</el-col>
<el-col :span="2">
{{$t('api_report.error')}}
</el-col>
<el-col :span="2">
{{$t('api_report.assertions')}}
</el-col>
<el-col :span="2">
{{$t('api_report.result')}}
</el-col>
</el-row>
<el-row :gutter="10" type="flex" align="middle" class="info">
<el-col :span="4">
<div class="method">
{{request.method}}
</div>
</el-col>
<el-col :span="12">
<div class="name">{{request.name}}</div>
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
<div class="url">{{request.url}}</div>
</el-tooltip>
</el-col>
<el-col :span="2">
<div class="time">
{{request.responseResult.responseTime}}
</div>
</el-col>
<el-col :span="2">
{{request.error}}
</el-col>
<el-col :span="2">
{{assertion}}
</el-col>
<el-col :span="2">
<el-tag size="mini" type="success" v-if="request.success">
{{$t('api_report.success')}}
</el-tag>
<el-tag size="mini" type="danger" v-else>
{{$t('api_report.fail')}}
</el-tag>
</el-col>
</el-row>
</div>
<el-collapse-transition>
<div v-show="isActive">
<el-tabs v-model="activeName" v-show="isActive" v-if="hasSub">
<el-tab-pane :label="$t('api_report.sub_result')" name="sub">
<ms-request-result class="sub-result" v-for="(sub, index) in request.subRequestResults"
:key="index" :request="sub"/>
</el-tab-pane>
<el-tab-pane :label="$t('api_report.request_result')" name="result">
<ms-request-metric :request="request"/>
<ms-request-text :request="request"/>
<br>
<ms-response-text :response="request.responseResult"/>
</el-tab-pane>
</el-tabs>
<div v-else>
<ms-request-metric :request="request"/>
<ms-request-text :request="request"/>
<br>
<ms-response-text :response="request.responseResult"/>
</div>
</div>
</el-collapse-transition>
</div>
</template>
<script>
import MsRequestMetric from "./RequestMetric";
import MsAssertionResults from "./AssertionResults";
import MsRequestText from "./RequestText";
import MsResponseText from "./ResponseText";
import MsRequestResult from "./RequestResult";
export default {
name: "MsRequestResultTail",
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult},
props: {
request: Object,
scenarioName: String
},
data() {
return {
isActive: false,
activeName: "sub",
}
},
methods: {
active() {
this.isActive = !this.isActive;
}
},
computed: {
assertion() {
return this.request.passAssertions + " / " + this.request.totalAssertions;
},
hasSub() {
return this.request.subRequestResults.length > 0;
}
}
}
</script>
<style scoped>
.request-result {
width: 100%;
min-height: 40px;
padding: 2px 0;
}
.request-result .info {
background-color: #F9F9F9;
margin-left: 20px;
cursor: pointer;
}
.request-result .method {
color: #1E90FF;
font-size: 14px;
font-weight: 500;
line-height: 40px;
padding-left: 5px;
}
.request-result .url {
color: #7f7f7f;
font-size: 12px;
font-weight: 400;
margin-top: 4px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
}
.request-result .tab .el-tabs__header {
margin: 0;
}
.request-result .text {
height: 300px;
overflow-y: auto;
}
.sub-result .info {
background-color: #FFF;
}
.sub-result .method {
border-left: 5px solid #1E90FF;
padding-left: 20px;
}
.sub-result:last-child {
border-bottom: 1px solid #EBEEF5;
}
.request-result .icon.is-active {
transform: rotate(90deg);
}
</style>

View File

@ -6,28 +6,13 @@
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}"/> <i class="icon el-icon-arrow-right" :class="{'is-active': isActive}"/>
{{scenario.name}} {{scenario.name}}
</el-col> </el-col>
<el-col :span="2">
{{scenario.responseTime}}
</el-col>
<el-col :span="2">
{{scenario.error}}
</el-col>
<el-col :span="2">
{{assertion}}
</el-col>
<el-col :span="2">
<el-tag size="mini" type="success" v-if="success">
{{$t('api_report.success')}}
</el-tag>
<el-tag size="mini" type="danger" v-else>
{{$t('api_report.fail')}}
</el-tag>
</el-col>
</el-row> </el-row>
</div> </div>
<el-collapse-transition> <el-collapse-transition>
<div v-show="isActive"> <div v-show="isActive">
<ms-request-result v-for="(request, index) in scenario.requestResults" :key="index" :request="request"/> <ms-request-result v-for="(request, index) in scenario.requestResults" :key="index" :request="request"
v-on:requestResult="requestResult"
:scenarioName="scenario.name"/>
</div> </div>
</el-collapse-transition> </el-collapse-transition>
</div> </div>
@ -54,6 +39,9 @@
methods: { methods: {
active() { active() {
this.isActive = !this.isActive; this.isActive = !this.isActive;
},
requestResult(requestResult) {
this.$emit("requestResult", requestResult);
} }
}, },

View File

@ -1,25 +1,15 @@
<template> <template>
<el-card> <el-card>
<div class="scenario-header"> <div class="scenario-header">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="16"> <el-col :span="16">
{{$t('api_report.scenario_name')}} {{$t('api_report.scenario_name')}}
</el-col> </el-col>
<el-col :span="2">
{{$t('api_report.response_time')}}
</el-col>
<el-col :span="2">
{{$t('api_report.error')}}
</el-col>
<el-col :span="2">
{{$t('api_report.assertions')}}
</el-col>
<el-col :span="2">
{{$t('api_report.result')}}
</el-col>
</el-row> </el-row>
</div> </div>
<ms-scenario-result v-for="(scenario, index) in scenarios" :key="index" :scenario="scenario"/> <ms-scenario-result v-for="(scenario, index) in scenarios" :key="index" :scenario="scenario"
v-on:requestResult="requestResult"/>
</el-card> </el-card>
</template> </template>
@ -33,8 +23,12 @@
props: { props: {
scenarios: Array scenarios: Array
},
methods: {
requestResult(requestResult) {
this.$emit("requestResult", requestResult);
}
} }
} }
</script> </script>

View File

@ -5,10 +5,20 @@
<template v-slot:header> <template v-slot:header>
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="search" <ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="search"
:title="$t('commons.test')" :title="$t('commons.test')"
@create="create" :createTip="$t('load_test.create')"/> @create="create" :createTip="$t('load_test.create')" :runTip="$t('load_test.run')"
:show-run="true"
@runTest="runTest"/>
</template> </template>
<el-table border :data="tableData" class="adjust-table table-content" @sort-change="sort" @row-click="handleView"
@filter-change="filter"> <one-click-operation ref="OneClickOperation" :select-ids="selectIds" :select-names="selectNames"
:select-project-names="selectProjectNames"></one-click-operation>
<el-table border :data="tableData" class="adjust-table table-content" @sort-change="sort"
@row-click="handleView"
@filter-change="filter" @select-all="handleSelectAll" @select="selectionChange">
<el-table-column
type="selection"></el-table-column>
<el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip> <el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="200" show-overflow-tooltip/> <el-table-column prop="projectName" :label="$t('load_test.project_name')" width="200" show-overflow-tooltip/>
@ -46,6 +56,7 @@
</template> </template>
<script> <script>
import OneClickOperation from './OneClickOperation';
import MsTablePagination from "../../common/pagination/TablePagination"; import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader"; import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator"; import MsTableOperator from "../../common/components/MsTableOperator";
@ -58,6 +69,7 @@
export default { export default {
components: { components: {
OneClickOperation,
MsTableOperators, MsTableOperators,
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator
}, },
@ -74,6 +86,9 @@
pageSize: 5, pageSize: 5,
total: 0, total: 0,
loading: false, loading: false,
selectIds: new Set(),
selectNames: new Set(),
selectProjectNames: new Set(),
buttons: [ buttons: [
{ {
tip: this.$t('commons.edit'), icon: "el-icon-edit", tip: this.$t('commons.edit'), icon: "el-icon-edit",
@ -105,16 +120,46 @@
create() { create() {
this.$router.push('/api/test/create'); this.$router.push('/api/test/create');
}, },
handleSelectAll(selection) {
if (selection.length > 0) {
this.tableData.forEach(item => {
this.selectIds.add(item.id);
this.selectProjectNames.add(item.projectName)
});
} else {
this.selectIds.clear()
this.selectProjectNames.clear()
}
},
selectionChange(selection, row) {
if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id);
this.selectProjectNames.delete(row.projectName)
} else {
this.selectIds.add(row.id);
this.selectProjectNames.add(row.projectName)
}
},
runTest() {
if (this.selectIds.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
} else {
this.$refs.OneClickOperation.openOneClickOperation();
}
},
search() { search() {
if (this.projectId !== 'all') { if (this.projectId !== 'all') {
this.condition.projectId = this.projectId; this.condition.projectId = this.projectId;
} }
let url = "/api/list/" + this.currentPage + "/" + this.pageSize; let url = "/api/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => { this.result = this.$post(url, this.condition, response => {
let data = response.data; let data = response.data;
this.total = data.itemCount; this.total = data.itemCount;
this.tableData = data.listObject; this.tableData = data.listObject;
this.tableData.forEach(item => {
this.selectNames.add(item.name)
})
}); });
}, },
handleSelectionChange(val) { handleSelectionChange(val) {

View File

@ -0,0 +1,149 @@
<template>
<el-dialog
:title="$t('load_test.run')"
:visible.sync="oneClickOperationVisible"
width="30%"
left
:destroy-on-close="true"
show-close
@closed="handleClose">
<el-form :model="ruleForm" label-position="right" label-width="120px" size="small" :rules="rule">
<el-form-item :label="$t('test_track.case.test_name')" prop="testName">
<el-input v-model="ruleForm.testName" autocomplete="off" clearable show-word-limit></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<ms-dialog-footer
@cancel="oneClickOperationVisible = false"
@confirm="checkedSaveAndRunTest"/>
</span>
</el-dialog>
</template>
<script>
import MsDialogFooter from '../../common/components/MsDialogFooter'
import {Test} from "./model/ScenarioModel";
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog";
import {removeGoBackListener} from "../../../../common/js/utils";
export default {
name: "OneClickOperation",
components: {
MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig, MsDialogFooter
},
data() {
return {
oneClickOperationVisible: false,
test: null,
tests: [],
ruleForm: {},
rule: {
testName: [
{required: true, message: this.$t('api_test.input_name'), trigger: 'blur'},
],
}
};
},
props: {
selectIds: {
type: Set
},
selectNames: {
type: Set
},
selectProjectNames: {
type: Set
}
},
methods: {
openOneClickOperation() {
this.oneClickOperationVisible = true;
},
checkedSaveAndRunTest() {
if (this.selectNames.has(this.testName)) {
this.$warning(this.$t('load_test.already_exists'));
} else {
if (this.selectProjectNames.size > 1) {
this.$warning(this.$t('load_test.same_project_test'));
} else {
for (let x of this.selectIds) {
this.getTest(x)
}
}
}
},
getTest(id) {
this.result = this.$get("/api/get/" + id, response => {
if (response.data) {
let item = response.data;
this.tests.push(item);
let test = new Test({
projectId: item.projectId,
name: this.ruleForm.testName,
scenarioDefinition: JSON.parse(item.scenarioDefinition),
schedule: {},
});
this.test = this.test || test;
this.test.scenarioDefinition = this.test.scenarioDefinition.concat(test.scenarioDefinition);
if (this.tests.length === this.selectIds.size) {
this.tests = [];
this.saveRunTest();
this.oneClickOperationVisible = false;
}
}
});
},
saveRunTest() {
this.save(() => {
this.$success(this.$t('commons.save_success'));
this.runTest();
})
},
save(callback) {
let url = "/api/create";
this.result = this.$request(this.getOptions(url), () => {
this.create = false;
if (callback) callback();
});
},
runTest() {
this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
this.$success(this.$t('api_test.running'));
this.$router.push({
path: '/api/report/view/' + response.data
})
});
},
getOptions(url) {
let formData = new FormData();
let requestJson = JSON.stringify(this.test);
formData.append('request', new Blob([requestJson], {
type: "application/json"
}));
let jmx = this.test.toJMX();
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
formData.append("files", new File([blob], jmx.name));
return {
method: 'POST',
url: url,
data: formData,
headers: {
'Content-Type': undefined
}
};
},
handleClose() {
this.ruleForm = {}
},
}
}
</script>
<style scoped>
</style>

View File

@ -5,9 +5,9 @@
</template> </template>
<script> <script>
export default { export default {
name: "MsMainContainer" name: "MsMainContainer"
} }
</script> </script>
<style scoped> <style scoped>

View File

@ -10,6 +10,10 @@
<span class="operate-button"> <span class="operate-button">
<ms-table-button :is-tester-permission="isTesterPermission" v-if="showCreate" icon="el-icon-circle-plus-outline" <ms-table-button :is-tester-permission="isTesterPermission" v-if="showCreate" icon="el-icon-circle-plus-outline"
:content="createTip" @click="create"/> :content="createTip" @click="create"/>
<ms-table-button :is-tester-permission="isTesterPermission" v-if="showRun" icon="el-icon-video-play"
type="primary"
:content="runTip" @click="runTest"/>
<slot name="button"></slot> <slot name="button"></slot>
</span> </span>
<span> <span>
@ -40,6 +44,10 @@
type: Boolean, type: Boolean,
default: true default: true
}, },
showRun: {
type: Boolean,
default: false
},
condition: { condition: {
type: Object type: Object
}, },
@ -49,6 +57,11 @@
return this.$t('commons.create'); return this.$t('commons.create');
} }
}, },
runTip: {
type: String,
},
isTesterPermission: { isTesterPermission: {
type: Boolean, type: Boolean,
default: false default: false
@ -61,6 +74,9 @@
}, },
create() { create() {
this.$emit('create'); this.$emit('create');
},
runTest() {
this.$emit('runTest')
} }
}, },
computed: { computed: {

View File

@ -15,6 +15,7 @@
changeRoute() { changeRoute() {
// //
this.$router.push(this.index + '/all'); this.$router.push(this.index + '/all');
window.location.reload();
} }
} }
} }

View File

@ -269,6 +269,8 @@ export default {
not_exist: "Test report does not exist", not_exist: "Test report does not exist",
}, },
load_test: { load_test: {
same_project_test: 'Only tests within the same project can be run',
run: 'One click operation',
operating: 'Operating', operating: 'Operating',
pressure_prediction_chart: 'Pressure Prediction Chart', pressure_prediction_chart: 'Pressure Prediction Chart',
recent: 'Recent Tests', recent: 'Recent Tests',

View File

@ -267,6 +267,8 @@ export default {
not_exist: "测试报告不存在", not_exist: "测试报告不存在",
}, },
load_test: { load_test: {
same_project_test: '只能运行同一项目内的测试',
already_exists: '测试名称不能重复',
operating: '操作', operating: '操作',
recent: '最近的测试', recent: '最近的测试',
search_by_name: '根据名称搜索', search_by_name: '根据名称搜索',
@ -319,6 +321,7 @@ export default {
custom_http_code: '自定义 HTTP 响应成功状态码', custom_http_code: '自定义 HTTP 响应成功状态码',
separated_by_commas: '按逗号分隔', separated_by_commas: '按逗号分隔',
create: '创建测试', create: '创建测试',
run: '一键运行',
select_resource_pool: '请选择资源池', select_resource_pool: '请选择资源池',
resource_pool_is_null: '资源池为空', resource_pool_is_null: '资源池为空',
download_log_file: '下载完整日志文件', download_log_file: '下载完整日志文件',

View File

@ -267,6 +267,8 @@ export default {
not_exist: "測試報告不存在", not_exist: "測試報告不存在",
}, },
load_test: { load_test: {
same_project_test: '只能運行同一項目內的測試',
run: '一鍵運行',
operating: '操作', operating: '操作',
recent: '最近的測試', recent: '最近的測試',
search_by_name: '根據名稱搜索', search_by_name: '根據名稱搜索',