feat(接口自动化): 单个报告执行和调试统一 实时接收结果
This commit is contained in:
parent
12243e7a83
commit
0ff5adf993
|
@ -126,7 +126,7 @@ public class JMeterService {
|
||||||
init();
|
init();
|
||||||
FixedTask.tasks.put(testId, System.currentTimeMillis());
|
FixedTask.tasks.put(testId, System.currentTimeMillis());
|
||||||
addBackendListener(testId, debugReportId, runMode, testPlan);
|
addBackendListener(testId, debugReportId, runMode, testPlan);
|
||||||
if (ExecuteType.Debug.name().equals(debugReportId)) {
|
if (ExecuteType.Debug.name().equals(debugReportId) || ApiRunMode.SCENARIO.name().equals(runMode)) {
|
||||||
addResultCollector(testId, testPlan);
|
addResultCollector(testId, testPlan);
|
||||||
}
|
}
|
||||||
LocalRunner runner = new LocalRunner(testPlan);
|
LocalRunner runner = new LocalRunner(testPlan);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package io.metersphere.api.service;
|
package io.metersphere.api.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
import io.metersphere.api.dto.scenario.request.RequestType;
|
import io.metersphere.api.dto.scenario.request.RequestType;
|
||||||
import io.metersphere.api.jmeter.*;
|
import io.metersphere.api.jmeter.*;
|
||||||
import io.metersphere.commons.utils.LogUtil;
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
@ -34,6 +35,9 @@ public class MsResultService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCache(String key, SampleResult result) {
|
public void setCache(String key, SampleResult result) {
|
||||||
|
if (key.startsWith("[") && key.endsWith("]")) {
|
||||||
|
key = JSON.parseArray(key).get(0).toString();
|
||||||
|
}
|
||||||
TestResult testResult = this.getResult(key);
|
TestResult testResult = this.getResult(key);
|
||||||
if (testResult == null) {
|
if (testResult == null) {
|
||||||
testResult = new TestResult();
|
testResult = new TestResult();
|
||||||
|
@ -98,9 +102,16 @@ public class MsResultService {
|
||||||
|
|
||||||
public String getJmeterLogger(String testId, boolean removed) {
|
public String getJmeterLogger(String testId, boolean removed) {
|
||||||
Long startTime = FixedTask.tasks.get(testId);
|
Long startTime = FixedTask.tasks.get(testId);
|
||||||
|
if (startTime == null) {
|
||||||
|
startTime = FixedTask.tasks.get("[" + testId + "]");
|
||||||
|
}
|
||||||
|
if (startTime == null) {
|
||||||
|
startTime = System.currentTimeMillis();
|
||||||
|
}
|
||||||
Long endTime = System.currentTimeMillis();
|
Long endTime = System.currentTimeMillis();
|
||||||
|
Long finalStartTime = startTime;
|
||||||
String logMessage = JmeterLoggerAppender.logger.entrySet().stream()
|
String logMessage = JmeterLoggerAppender.logger.entrySet().stream()
|
||||||
.filter(map -> map.getKey() > startTime && map.getKey() < endTime)
|
.filter(map -> map.getKey() > finalStartTime && map.getKey() < endTime)
|
||||||
.map(map -> map.getValue()).collect(Collectors.joining());
|
.map(map -> map.getValue()).collect(Collectors.joining());
|
||||||
if (removed) {
|
if (removed) {
|
||||||
FixedTask.tasks.remove(testId);
|
FixedTask.tasks.remove(testId);
|
||||||
|
|
|
@ -0,0 +1,480 @@
|
||||||
|
<template>
|
||||||
|
<ms-container v-loading="loading">
|
||||||
|
<ms-main-container>
|
||||||
|
<el-card>
|
||||||
|
<section class="report-container">
|
||||||
|
<ms-api-report-view-header :debug="debug" :report="report" @reportExport="handleExport"/>
|
||||||
|
<main>
|
||||||
|
<ms-metric-chart :content="content" :totalTime="totalTime" v-if="!loading"/>
|
||||||
|
<div>
|
||||||
|
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||||
|
<el-tab-pane :label="$t('api_report.total')" name="total">
|
||||||
|
<ms-scenario-results :treeData="fullTreeNodes" :default-expand="true" :console="content.console" 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" :console="content.console" :treeData="failsTreeNodes"/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
<ms-api-report-export v-if="reportExportVisible" id="apiTestReport" :title="report.testName"
|
||||||
|
:content="content" :total-time="totalTime"/>
|
||||||
|
</main>
|
||||||
|
</section>
|
||||||
|
</el-card>
|
||||||
|
</ms-main-container>
|
||||||
|
</ms-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import MsRequestResult from "./components/RequestResult";
|
||||||
|
import MsRequestResultTail from "./components/RequestResultTail";
|
||||||
|
import MsScenarioResult from "./components/ScenarioResult";
|
||||||
|
import MsMetricChart from "./components/MetricChart";
|
||||||
|
import MsScenarioResults from "./components/ScenarioResults";
|
||||||
|
import MsContainer from "@/business/components/common/components/MsContainer";
|
||||||
|
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
|
||||||
|
import MsApiReportExport from "./ApiReportExport";
|
||||||
|
import MsApiReportViewHeader from "./ApiReportViewHeader";
|
||||||
|
import {RequestFactory} from "../../definition/model/ApiTestModel";
|
||||||
|
import {windowPrint, getCurrentProjectID} from "@/common/js/utils";
|
||||||
|
import {ELEMENTS} from "../scenario/Setting";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "SysnApiReportDetail",
|
||||||
|
components: {
|
||||||
|
MsApiReportViewHeader,
|
||||||
|
MsApiReportExport,
|
||||||
|
MsMainContainer,
|
||||||
|
MsContainer, MsScenarioResults, MsRequestResultTail, MsMetricChart, MsScenarioResult, MsRequestResult
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeName: "total",
|
||||||
|
content: {total: 0, scenarioTotal: 1},
|
||||||
|
report: {},
|
||||||
|
loading: false,
|
||||||
|
fails: [],
|
||||||
|
failsTreeNodes: [],
|
||||||
|
totalTime: 0,
|
||||||
|
isRequestResult: false,
|
||||||
|
request: {},
|
||||||
|
isActive: false,
|
||||||
|
scenarioName: null,
|
||||||
|
reportExportVisible: false,
|
||||||
|
requestType: undefined,
|
||||||
|
fullTreeNodes: [],
|
||||||
|
debugResult: new Map,
|
||||||
|
scenarioMap: new Map,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
activated() {
|
||||||
|
this.isRequestResult = false;
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.scenarioId) {
|
||||||
|
this.getApiScenario();
|
||||||
|
} else {
|
||||||
|
if (this.scenario && this.scenario.scenarioDefinition) {
|
||||||
|
this.content.scenarioStepTotal = this.scenario.scenarioDefinition.hashTree.length;
|
||||||
|
this.initTree();
|
||||||
|
this.initWebSocket();
|
||||||
|
this.clearDebug();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
reportId: String,
|
||||||
|
currentProjectId: String,
|
||||||
|
infoDb: Boolean,
|
||||||
|
debug: Boolean,
|
||||||
|
scenario: {},
|
||||||
|
scenarioId: String
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getApiScenario() {
|
||||||
|
this.loading = true;
|
||||||
|
this.result = this.$get("/api/automation/getApiScenario/" + this.scenarioId, response => {
|
||||||
|
if (response.data) {
|
||||||
|
this.path = "/api/automation/update";
|
||||||
|
if (response.data.scenarioDefinition != null) {
|
||||||
|
let obj = JSON.parse(response.data.scenarioDefinition);
|
||||||
|
this.scenario.scenarioDefinition = obj;
|
||||||
|
this.scenario.name = response.data.name;
|
||||||
|
this.content.scenarioStepTotal = obj.hashTree.length;
|
||||||
|
this.initTree();
|
||||||
|
this.initWebSocket();
|
||||||
|
this.clearDebug();
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
initTree() {
|
||||||
|
this.fullTreeNodes = [];
|
||||||
|
let obj = {index: 1, label: this.scenario.name, value: {responseResult: {}, unexecute: true}, children: [], unsolicited: true};
|
||||||
|
this.formatContent(this.scenario.scenarioDefinition.hashTree, obj);
|
||||||
|
this.fullTreeNodes.push(obj);
|
||||||
|
},
|
||||||
|
setTreeValue(arr) {
|
||||||
|
arr.forEach(item => {
|
||||||
|
if (this.debugResult && this.debugResult.get(item.resId)) {
|
||||||
|
let arrValue = this.debugResult.get(item.resId);
|
||||||
|
if (arrValue.length > 1) {
|
||||||
|
for (let i = 0; i < arrValue.length; i++) {
|
||||||
|
let obj = {resId: item.resId, index: i, label: item.resId, value: arrValue[i], children: []};
|
||||||
|
let isAdd = true;
|
||||||
|
arr.forEach(obj => {
|
||||||
|
if (obj.value.name === arrValue[i].name) {
|
||||||
|
isAdd = false;
|
||||||
|
}
|
||||||
|
if (obj.value.name.indexOf("循环-") === -1) {
|
||||||
|
arr.splice(0, 1);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (isAdd) {
|
||||||
|
arr.push(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
item.value = arrValue[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (item.children && item.children.length > 0) {
|
||||||
|
this.setTreeValue(item.children);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
formatContent(hashTree, tree) {
|
||||||
|
if (hashTree) {
|
||||||
|
hashTree.forEach(item => {
|
||||||
|
if (item.enable) {
|
||||||
|
let key = item.id + item.name;
|
||||||
|
let name = item.name ? item.name : item.type;
|
||||||
|
let obj = {resId: key, index: Number(item.index), label: name, value: {name: name, responseResult: {}, unexecute: true}, children: [], unsolicited: true};
|
||||||
|
tree.children.push(obj);
|
||||||
|
if (ELEMENTS.get("AllSamplerProxy").indexOf(item.type) != -1) {
|
||||||
|
obj.unsolicited = false;
|
||||||
|
obj.type = item.type;
|
||||||
|
} else if (item.type === 'scenario') {
|
||||||
|
this.content.scenarioTotal += 1;
|
||||||
|
}
|
||||||
|
if (item.hashTree && item.hashTree.length > 0 && ELEMENTS.get("AllSamplerProxy").indexOf(item.type) === -1) {
|
||||||
|
this.formatContent(item.hashTree, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleExport() {
|
||||||
|
this.reportExportVisible = true;
|
||||||
|
let reset = this.exportReportReset;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
windowPrint('apiTestReport', 0.57);
|
||||||
|
reset();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handleClick(tab, event) {
|
||||||
|
this.isRequestResult = false
|
||||||
|
},
|
||||||
|
exportReportReset() {
|
||||||
|
this.$router.go(0);
|
||||||
|
},
|
||||||
|
requestResult(requestResult) {
|
||||||
|
this.active();
|
||||||
|
this.isRequestResult = false;
|
||||||
|
this.requestType = undefined;
|
||||||
|
if (requestResult.request.body.indexOf('[Callable Statement]') > -1) {
|
||||||
|
this.requestType = RequestFactory.TYPES.SQL;
|
||||||
|
}
|
||||||
|
this.$nextTick(function () {
|
||||||
|
this.isRequestResult = true;
|
||||||
|
this.request = requestResult.request;
|
||||||
|
this.scenarioName = requestResult.scenarioName;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
clearDebug() {
|
||||||
|
this.totalTime = 0;
|
||||||
|
this.content.total = 0;
|
||||||
|
this.content.error = 0;
|
||||||
|
this.content.success = 0;
|
||||||
|
this.content.passAssertions = 0;
|
||||||
|
this.content.totalAssertions = 0;
|
||||||
|
this.content.scenarioSuccess = 0;
|
||||||
|
this.content.scenarioError = 0;
|
||||||
|
},
|
||||||
|
initWebSocket() {
|
||||||
|
let protocol = "ws://";
|
||||||
|
if (window.location.protocol === 'https:') {
|
||||||
|
protocol = "wss://";
|
||||||
|
}
|
||||||
|
const uri = protocol + window.location.host + "/api/scenario/report/get/real/" + this.reportId;
|
||||||
|
this.websocket = new WebSocket(uri);
|
||||||
|
this.websocket.onmessage = this.onMessage;
|
||||||
|
this.websocket.onopen = this.onOpen;
|
||||||
|
this.websocket.onerror = this.onError;
|
||||||
|
this.websocket.onclose = this.onClose;
|
||||||
|
},
|
||||||
|
onOpen() {
|
||||||
|
},
|
||||||
|
onError(e) {
|
||||||
|
window.console.error(e)
|
||||||
|
},
|
||||||
|
onMessage(e) {
|
||||||
|
if (e.data) {
|
||||||
|
let data = JSON.parse(e.data);
|
||||||
|
this.formatResult(data);
|
||||||
|
if (data.end) {
|
||||||
|
this.removeReport();
|
||||||
|
this.getReport();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClose(e) {
|
||||||
|
if (e.code === 1005) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeReport() {
|
||||||
|
let url = "/api/scenario/report/remove/real/" + this.reportId;
|
||||||
|
this.$get(url, response => {
|
||||||
|
this.$success(this.$t('schedule.event_success'));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatResult(res) {
|
||||||
|
let resMap = new Map;
|
||||||
|
let startTime = 99991611737506593;
|
||||||
|
let endTime = 0;
|
||||||
|
this.clearDebug();
|
||||||
|
if (res && res.scenarios) {
|
||||||
|
res.scenarios.forEach(item => {
|
||||||
|
this.content.total += item.requestResults.length;
|
||||||
|
this.content.passAssertions += item.passAssertions
|
||||||
|
this.content.totalAssertions += item.totalAssertions;
|
||||||
|
if (item && item.requestResults) {
|
||||||
|
item.requestResults.forEach(req => {
|
||||||
|
req.responseResult.console = res.console;
|
||||||
|
let name = req.name.split('<->')[0];
|
||||||
|
let key = req.id + name;
|
||||||
|
if (resMap.get(key)) {
|
||||||
|
if (resMap.get(key).indexOf(req) === -1) {
|
||||||
|
resMap.get(key).push(req);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resMap.set(key, [req]);
|
||||||
|
}
|
||||||
|
if (req.success) {
|
||||||
|
this.content.success++;
|
||||||
|
} else {
|
||||||
|
this.content.error++;
|
||||||
|
}
|
||||||
|
if (req.startTime && Number(req.startTime) < startTime) {
|
||||||
|
startTime = req.startTime;
|
||||||
|
}
|
||||||
|
if (req.endTime && Number(req.endTime) > endTime) {
|
||||||
|
endTime = req.endTime;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (startTime < endTime) {
|
||||||
|
this.totalTime = endTime - startTime + 100;
|
||||||
|
}
|
||||||
|
this.debugResult = resMap;
|
||||||
|
this.setTreeValue(this.fullTreeNodes);
|
||||||
|
this.reload();
|
||||||
|
},
|
||||||
|
reload() {
|
||||||
|
this.loading = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.loading = false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getReport() {
|
||||||
|
let url = "/api/scenario/report/get/" + this.reportId;
|
||||||
|
this.$get(url, response => {
|
||||||
|
this.report = response.data || {};
|
||||||
|
if (response.data) {
|
||||||
|
try {
|
||||||
|
this.content = JSON.parse(this.report.content);
|
||||||
|
if (!this.content) {
|
||||||
|
this.content = {scenarios: []};
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
this.getFails();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
getFails() {
|
||||||
|
this.fails = [];
|
||||||
|
let array = [];
|
||||||
|
if (this.content.scenarios) {
|
||||||
|
this.content.scenarios.forEach((scenario) => {
|
||||||
|
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);
|
||||||
|
array.push(request);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.formatTree(array, this.failsTreeNodes);
|
||||||
|
this.recursiveSorting(this.failsTreeNodes);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatTree(array, tree) {
|
||||||
|
array.map((item) => {
|
||||||
|
let key = item.name;
|
||||||
|
let nodeArray = key.split('^@~@^');
|
||||||
|
let children = tree;
|
||||||
|
let scenarioId = "";
|
||||||
|
let scenarioName = "";
|
||||||
|
if (item.scenario) {
|
||||||
|
let scenarioArr = JSON.parse(item.scenario);
|
||||||
|
if (scenarioArr.length > 1) {
|
||||||
|
let scenarioIdArr = scenarioArr[0].split("_");
|
||||||
|
scenarioId = scenarioIdArr[0];
|
||||||
|
scenarioName = scenarioIdArr[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 循环构建子节点
|
||||||
|
for (let i = 0; i < nodeArray.length; i++) {
|
||||||
|
if (!nodeArray[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let node = {
|
||||||
|
label: nodeArray[i],
|
||||||
|
value: item,
|
||||||
|
};
|
||||||
|
if (i !== nodeArray.length) {
|
||||||
|
node.children = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (children.length === 0) {
|
||||||
|
children.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
let isExist = false;
|
||||||
|
for (let j in children) {
|
||||||
|
if (children[j].label === node.label) {
|
||||||
|
|
||||||
|
let idIsPath = true;
|
||||||
|
//判断ID是否匹配 目前发现问题的只有重复场景,而重复场景是在第二个节点开始合并的。所以这里暂时只判断第二个场景问题。
|
||||||
|
//如果出现了其他问题,则需要检查其他问题的数据结构。暂时采用具体问题具体分析的策略
|
||||||
|
if (i === nodeArray.length - 2) {
|
||||||
|
idIsPath = false;
|
||||||
|
let childId = "";
|
||||||
|
let childName = "";
|
||||||
|
if (children[j].value && children[j].value.scenario) {
|
||||||
|
let scenarioArr = JSON.parse(children[j].value.scenario);
|
||||||
|
if (scenarioArr.length > 1) {
|
||||||
|
let childArr = scenarioArr[0].split("_");
|
||||||
|
childId = childArr[0];
|
||||||
|
if (childArr.length > 1) {
|
||||||
|
childName = childArr[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scenarioId === "") {
|
||||||
|
idIsPath = true;
|
||||||
|
} else if (scenarioId === childId) {
|
||||||
|
idIsPath = true;
|
||||||
|
} else if (scenarioName !== childName) {
|
||||||
|
//如果两个名字不匹配则默认通过,不匹配ID
|
||||||
|
idIsPath = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idIsPath) {
|
||||||
|
if (i !== nodeArray.length - 1 && !children[j].children) {
|
||||||
|
children[j].children = [];
|
||||||
|
}
|
||||||
|
children = (i === nodeArray.length - 1 ? children : children[j].children);
|
||||||
|
isExist = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!isExist) {
|
||||||
|
children.push(node);
|
||||||
|
if (i !== nodeArray.length - 1 && !children[children.length - 1].children) {
|
||||||
|
children[children.length - 1].children = [];
|
||||||
|
}
|
||||||
|
children = (i === nodeArray.length - 1 ? children : children[children.length - 1].children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
recursiveSorting(arr) {
|
||||||
|
for (let i in arr) {
|
||||||
|
if (arr[i]) {
|
||||||
|
arr[i].index = Number(i) + 1;
|
||||||
|
if (arr[i].children && arr[i].children.length > 0) {
|
||||||
|
this.recursiveSorting(arr[i].children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
projectId() {
|
||||||
|
return getCurrentProjectID();
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style>
|
||||||
|
.report-container .el-tabs__header {
|
||||||
|
margin-bottom: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
.report-container {
|
||||||
|
height: calc(100vh - 155px);
|
||||||
|
min-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-header {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-header a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-header .time {
|
||||||
|
color: #909399;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-container .fail {
|
||||||
|
color: #F56C6C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-container .is-active .fail {
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-button {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenario-result .icon.is-active {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -34,19 +34,16 @@
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="2">
|
<el-col :span="2">
|
||||||
<div>
|
<div>
|
||||||
<el-tag size="mini" type="success" v-if="request.success">
|
<el-tag size="mini" v-if="request.unexecute">{{ $t('api_test.home_page.detail_card.unexecute') }}</el-tag>
|
||||||
{{ $t('api_report.success') }}
|
<el-tag size="mini" type="success" v-else-if="request.success"> {{ $t('api_report.success') }}</el-tag>
|
||||||
</el-tag>
|
<el-tag size="mini" type="danger" v-else> {{ $t('api_report.fail') }}</el-tag>
|
||||||
<el-tag size="mini" type="danger" v-else>
|
|
||||||
{{ $t('api_report.fail') }}
|
|
||||||
</el-tag>
|
|
||||||
</div>
|
</div>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-collapse-transition>
|
<el-collapse-transition>
|
||||||
<div v-show="isActive" style="width: 99%">
|
<div v-show="isActive && !request.unexecute" style="width: 99%">
|
||||||
<ms-request-result-tail :scenario-name="scenarioName"
|
<ms-request-result-tail :scenario-name="scenarioName"
|
||||||
:request-type="requestType"
|
:request-type="requestType"
|
||||||
:request="request"
|
:request="request"
|
||||||
|
@ -100,18 +97,29 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
active() {
|
active() {
|
||||||
this.isActive = !this.isActive;
|
if (this.request.unexecute) {
|
||||||
|
this.isActive = false;
|
||||||
|
} else {
|
||||||
|
this.isActive = !this.isActive;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
getName(name) {
|
getName(name) {
|
||||||
|
if (name && name.indexOf("<->") !== -1) {
|
||||||
|
return name.split("<->")[0];
|
||||||
|
}
|
||||||
if (name && name.indexOf("^@~@^") !== -1) {
|
if (name && name.indexOf("^@~@^") !== -1) {
|
||||||
let arr = name.split("^@~@^");
|
let arr = name.split("^@~@^");
|
||||||
if (arr[arr.length - 1].indexOf("UUID=")) {
|
let value = arr[arr.length - 1];
|
||||||
return arr[arr.length - 1].split("UUID=")[0];
|
if (value.indexOf("UUID=") !== -1) {
|
||||||
|
return value.split("UUID=")[0];
|
||||||
}
|
}
|
||||||
if (arr[arr.length - 1] && arr[arr.length - 1].startsWith("UUID=")) {
|
if (value && value.startsWith("UUID=")) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return arr[arr.length - 1];
|
if (value && value.indexOf("<->") !== -1) {
|
||||||
|
return value.split("<->")[0];
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
if (name && name.startsWith("UUID=")) {
|
if (name && name.startsWith("UUID=")) {
|
||||||
return "";
|
return "";
|
||||||
|
@ -209,6 +217,7 @@ export default {
|
||||||
.icon.is-active {
|
.icon.is-active {
|
||||||
transform: rotate(90deg);
|
transform: rotate(90deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.ms-req-name {
|
.ms-req-name {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0 5px;
|
margin: 0 5px;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="scenario-result">
|
<div class="scenario-result">
|
||||||
<div v-if="node.children && node.children.length >0 ">
|
<div v-if="(node.children && node.children.length >0) || node.unsolicited">
|
||||||
<el-card class="ms-card">
|
<el-card class="ms-card">
|
||||||
<div class="el-step__icon is-text ms-api-col">
|
<div class="el-step__icon is-text ms-api-col">
|
||||||
<div class="el-step__icon-inner"> {{ node.index }}</div>
|
<div class="el-step__icon-inner"> {{ node.index }}</div>
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
<el-card class="scenario-results">
|
<el-card class="scenario-results">
|
||||||
<el-tree :data="treeData"
|
<el-tree :data="treeData"
|
||||||
:expand-on-click-node="false"
|
:expand-on-click-node="false"
|
||||||
|
:default-expand-all="defaultExpand"
|
||||||
highlight-current
|
highlight-current
|
||||||
class="ms-tree ms-report-tree">
|
class="ms-tree ms-report-tree" ref="resultsTree">
|
||||||
<span slot-scope="{ node, data}" style="width: 99%" @click="nodeClick(node)">
|
<span slot-scope="{ node, data}" style="width: 99%" @click="nodeClick(node)">
|
||||||
<ms-scenario-result :node="data" :console="console" v-on:requestResult="requestResult"/>
|
<ms-scenario-result :node="data" :console="console" v-on:requestResult="requestResult"/>
|
||||||
</span>
|
</span>
|
||||||
|
@ -21,6 +22,15 @@ export default {
|
||||||
scenarios: Array,
|
scenarios: Array,
|
||||||
treeData: Array,
|
treeData: Array,
|
||||||
console: String,
|
console: String,
|
||||||
|
defaultExpand: {
|
||||||
|
default: false,
|
||||||
|
type: Boolean,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.$refs.resultsTree && this.$refs.resultsTree.root) {
|
||||||
|
this.$refs.resultsTree.root.expanded = true;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
requestResult(requestResult) {
|
requestResult(requestResult) {
|
||||||
|
|
|
@ -190,7 +190,7 @@
|
||||||
<!-- 执行结果 -->
|
<!-- 执行结果 -->
|
||||||
<el-drawer :visible.sync="runVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" :modal="false"
|
<el-drawer :visible.sync="runVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" :modal="false"
|
||||||
size="90%">
|
size="90%">
|
||||||
<ms-api-report-detail @refresh="search" :infoDb="infoDb" :report-id="reportId" :currentProjectId="projectId"/>
|
<ms-api-report-detail @refresh="search" :debug="true" :scenario="currentScenario" :scenarioId="scenarioId" :infoDb="infoDb" :report-id="reportId" :currentProjectId="projectId"/>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
<!--测试计划-->
|
<!--测试计划-->
|
||||||
<el-drawer :visible.sync="planVisible" :destroy-on-close="true" direction="ltr" :withHeader="false"
|
<el-drawer :visible.sync="planVisible" :destroy-on-close="true" direction="ltr" :withHeader="false"
|
||||||
|
@ -214,8 +214,8 @@ import MsTableHeader from "@/business/components/common/components/MsTableHeader
|
||||||
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
|
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
|
||||||
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
|
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
|
||||||
import MsTag from "../../../common/components/MsTag";
|
import MsTag from "../../../common/components/MsTag";
|
||||||
import {downloadFile, getCurrentProjectID, getUUID, strMapToObj} from "@/common/js/utils";
|
import {downloadFile, getCurrentProjectID, getUUID, objToStrMap, strMapToObj} from "@/common/js/utils";
|
||||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
import MsApiReportDetail from "../report/SysnApiReportDetail";
|
||||||
import MsTableMoreBtn from "./TableMoreBtn";
|
import MsTableMoreBtn from "./TableMoreBtn";
|
||||||
import MsScenarioExtendButtons from "@/business/components/api/automation/scenario/ScenarioExtendBtns";
|
import MsScenarioExtendButtons from "@/business/components/api/automation/scenario/ScenarioExtendBtns";
|
||||||
import MsTestPlanList from "./testplan/TestPlanList";
|
import MsTestPlanList from "./testplan/TestPlanList";
|
||||||
|
@ -305,7 +305,7 @@ export default {
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
initApiTableOpretion: String,
|
initApiTableOpretion: String,
|
||||||
isRelate: Boolean
|
isRelate: Boolean,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -319,6 +319,7 @@ export default {
|
||||||
condition: {
|
condition: {
|
||||||
components: API_SCENARIO_CONFIGS
|
components: API_SCENARIO_CONFIGS
|
||||||
},
|
},
|
||||||
|
scenarioId: "",
|
||||||
currentScenario: {},
|
currentScenario: {},
|
||||||
schedule: {},
|
schedule: {},
|
||||||
tableData: [],
|
tableData: [],
|
||||||
|
@ -466,7 +467,7 @@ export default {
|
||||||
if (!this.projectName || this.projectName === "") {
|
if (!this.projectName || this.projectName === "") {
|
||||||
this.getProjectName();
|
this.getProjectName();
|
||||||
}
|
}
|
||||||
if(!this.isReferenceTable){
|
if (!this.isReferenceTable) {
|
||||||
this.operators = this.unTrashOperators;
|
this.operators = this.unTrashOperators;
|
||||||
this.buttons = this.unTrashButtons;
|
this.buttons = this.unTrashButtons;
|
||||||
}
|
}
|
||||||
|
@ -775,6 +776,7 @@ export default {
|
||||||
|
|
||||||
execute(row) {
|
execute(row) {
|
||||||
this.infoDb = false;
|
this.infoDb = false;
|
||||||
|
this.scenarioId = row.id;
|
||||||
let url = "/api/automation/run";
|
let url = "/api/automation/run";
|
||||||
let run = {};
|
let run = {};
|
||||||
let scenarioIds = [];
|
let scenarioIds = [];
|
||||||
|
@ -783,7 +785,6 @@ export default {
|
||||||
run.projectId = this.projectId;
|
run.projectId = this.projectId;
|
||||||
run.ids = scenarioIds;
|
run.ids = scenarioIds;
|
||||||
this.$post(url, run, response => {
|
this.$post(url, run, response => {
|
||||||
let data = response.data;
|
|
||||||
this.runVisible = true;
|
this.runVisible = true;
|
||||||
this.reportId = run.id;
|
this.reportId = run.id;
|
||||||
});
|
});
|
||||||
|
|
|
@ -245,7 +245,7 @@
|
||||||
<!-- 调试结果 -->
|
<!-- 调试结果 -->
|
||||||
<el-drawer v-if="type!=='detail'" :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr"
|
<el-drawer v-if="type!=='detail'" :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr"
|
||||||
:withHeader="true" :modal="false" size="90%">
|
:withHeader="true" :modal="false" size="90%">
|
||||||
<ms-api-report-detail :report-id="reportId" :debug="true" :currentProjectId="projectId" @refresh="detailRefresh"/>
|
<ms-api-report-detail :scenario="currentScenario" :report-id="reportId" :debug="true" :currentProjectId="projectId" @refresh="detailRefresh"/>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|
||||||
<!--场景公共参数-->
|
<!--场景公共参数-->
|
||||||
|
@ -296,7 +296,7 @@ import {
|
||||||
import ApiEnvironmentConfig from "@/business/components/api/test/components/ApiEnvironmentConfig";
|
import ApiEnvironmentConfig from "@/business/components/api/test/components/ApiEnvironmentConfig";
|
||||||
import MsInputTag from "./MsInputTag";
|
import MsInputTag from "./MsInputTag";
|
||||||
import MsRun from "./DebugRun";
|
import MsRun from "./DebugRun";
|
||||||
import MsApiReportDetail from "../report/ApiReportDetail";
|
import MsApiReportDetail from "../report/SysnApiReportDetail";
|
||||||
import MsVariableList from "./variable/VariableList";
|
import MsVariableList from "./variable/VariableList";
|
||||||
import ApiImport from "../../definition/components/import/ApiImport";
|
import ApiImport from "../../definition/components/import/ApiImport";
|
||||||
import "@/common/css/material-icons.css"
|
import "@/common/css/material-icons.css"
|
||||||
|
@ -571,6 +571,7 @@ export default {
|
||||||
promise.then(() => {
|
promise.then(() => {
|
||||||
let sign = this.$refs.envPopover.checkEnv(this.isFullUrl);
|
let sign = this.$refs.envPopover.checkEnv(this.isFullUrl);
|
||||||
if (!sign) {
|
if (!sign) {
|
||||||
|
this.debugLoading = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.editScenario().then(() => {
|
this.editScenario().then(() => {
|
||||||
|
|
Loading…
Reference in New Issue