fix(测试跟踪): 测试计划报告导出后场景等报告无法展示

This commit is contained in:
chenjianxing 2022-10-12 18:25:49 +08:00 committed by jianxing
parent 44d6b1638b
commit 912f95074b
29 changed files with 142 additions and 873 deletions

View File

@ -0,0 +1,27 @@
package io.metersphere.controller.plan;
import io.metersphere.api.dto.ApiReportResult;
import io.metersphere.service.BaseShareInfoService;
import io.metersphere.service.definition.ApiDefinitionService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/share/test/plan/api/case")
public class ShareTestPlanApiReportController {
@Resource
ApiDefinitionService apiDefinitionService;
@Resource
BaseShareInfoService baseShareInfoService;
@GetMapping("/api/definition/report/getReport/{shareId}/{testId}")
public ApiReportResult getApiReport(@PathVariable String shareId, @PathVariable String testId) {
baseShareInfoService.validateExpired(shareId);
return apiDefinitionService.getDbResult(testId);
}
}

View File

@ -175,7 +175,7 @@ public class TestPlanScenarioCaseController {
return testPlanScenarioCaseService.getApiScenarioEnv(planId);
}
@GetMapping("/plan/report")
@PostMapping("/plan/report")
public ApiPlanReportDTO buildApiReport(@RequestBody ApiPlanReportRequest request) {
return testPlanScenarioCaseService.buildApiReport(request);
}

View File

@ -863,7 +863,7 @@ public class TestPlanScenarioCaseService {
if (StringUtils.isNotEmpty(contentStr)) {
content.put("envName", apiDefinitionService.getEnvNameByEnvConfig(result.getProjectId(), result.getEnvConfig()));
}
contentStr = content.toString();
contentStr = JSON.toJSONString(content);
apiCase.setResponse(contentStr);
} catch (Exception e) {
LogUtil.error("解析content失败!", e);

View File

@ -121,8 +121,8 @@ import {RequestFactory} from "../../definition/model/ApiTestModel";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {getUUID, windowPrint} from "metersphere-frontend/src/utils";
import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {getScenarioReport, getScenarioReportDetail, getShareScenarioReport, reportReName} from "@/api/scenario-report";
import {STEP} from "@/business/automation/scenario/Setting";
import {getScenarioReport, getScenarioReportDetail, getShareScenarioReport, reportReName} from "../../../api/scenario-report";
import {STEP} from "../../automation/scenario/Setting";
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
export default {

View File

@ -87,7 +87,7 @@ import ApiReportReqestHeaderItem from "./ApiReportReqestHeaderItem";
import MsMetricChart from "./components/MetricChart";
import MsReportTitle from "metersphere-frontend/src/components/report/MsReportTitle";
import MsReportExportTemplate from "metersphere-frontend/src/components/report/MsReportExportTemplate";
import MsAssertionResults from "@/business/automation/report/components/AssertionResults"
import MsAssertionResults from "../../../business/automation/report/components/AssertionResults"
export default {
name: "MsApiReportExport",

View File

@ -79,11 +79,11 @@
<script>
import {generateShareInfoWithExpired,getShareRedirectUrl} from "@/api/share";
import {generateShareInfoWithExpired,getShareRedirectUrl} from "../../../api/share";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {getProjectApplicationConfig} from "@/api/project";
import {apiTestReRun} from "@/api/xpack";
import {getProjectApplicationConfig} from "../../../api/project";
import {apiTestReRun} from "../../../api/xpack";
import {getUUID} from "metersphere-frontend/src/utils";
export default {

View File

@ -74,13 +74,13 @@ import MsAssertionResults from "./AssertionResults";
import MsRequestText from "./RequestText";
import MsResponseText from "./ResponseText";
import MsRequestResultTail from "./RequestResultTail";
import {getCurrentByResourceId} from "@/api/user";
import {getShareContent} from "@/api/share";
import {getScenarioReportStepDetail} from "@/api/scenario-report";
import {getCurrentByResourceId} from "../../../../api/user";
import {getShareContent} from "../../../../api/share";
import {getScenarioReportStepDetail} from "../../../../api/scenario-report";
import MsApiReportStatus from "../ApiReportStatus";
const {getReportStatusColor} = require("@/business/commons/js/commons");
const {getReportStatusColor} = require("../../../../business/commons/js/commons");
export default {
name: "MsRequestResult",
components: {

View File

@ -53,8 +53,8 @@
<script>
import MsAssertionResults from "./AssertionResults";
import MsCodeEdit from "metersphere-frontend/src/components/MsCodeEdit";
import MsDropdown from "@/business/commons/MsDropdown";
import {BODY_FORMAT, RequestFactory, Request, SqlRequest} from "@/business/definition/model/ApiTestModel";
import MsDropdown from "../../../../business/commons/MsDropdown";
import {BODY_FORMAT, RequestFactory, Request, SqlRequest} from "../../../../business/definition/model/ApiTestModel";
import MsSqlResultTable from "./SqlResultTable";
export default {

View File

@ -38,8 +38,8 @@
<script>
import MsRequestResult from "./RequestResult";
import {STEP} from "@/business/automation/scenario/Setting";
import {getCurrentByResourceId} from "@/api/user";
import {STEP} from "../../../../business/automation/scenario/Setting";
import {getCurrentByResourceId} from "../../../../api/user";
export default {
name: "MsScenarioResult",

View File

@ -9,7 +9,7 @@
<script>
import MsResponseResult from "../response/ResponseResult";
import MsRequestMetric from "../response/RequestMetric";
import {getApiReportDetail} from "@/api/definition-report";
import {getApiReportDetail} from "../../../../api/definition-report";
export default {
name: "MsRequestResultTail",

View File

@ -47,7 +47,7 @@
<script>
import MsAssertionResults from "./AssertionResults";
import MsCodeEdit from "../MsCodeEdit";
import MsDropdown from "@/business/commons/MsDropdown";
import MsDropdown from "../../../../business/commons/MsDropdown";
import {BODY_FORMAT} from "../../model/ApiTestModel";
import MsSqlResultTable from "./SqlResultTable";

View File

@ -154,7 +154,7 @@ public class TestPlanLoadCaseController {
testPlanLoadCaseService.copyPlan(sourcePlanId, targetPlanId);
}
@GetMapping("/plan/report")
@PostMapping("/plan/report")
public LoadPlanReportDTO buildApiReport(@RequestBody PlanSubReportRequest request) {
return testPlanLoadCaseService.buildLoadReport(request);
}

View File

@ -37,8 +37,8 @@ import {
getPerformanceReportLogResourceDetail,
getSharePerformanceReportLogResource,
getSharePerformanceReportLogResourceDetail,
} from "@/api/load-test";
import {downloadLogFile} from "@/api/report";
} from "../../../api/load-test";
import {downloadLogFile} from "../../../api/report";
export default {
name: "LogDetails",

View File

@ -130,7 +130,7 @@
</template>
<script>
import {getPerformanceReportContent, getSharePerformanceReportContent} from "@/api/load-test";
import {getPerformanceReportContent, getSharePerformanceReportContent} from "../../../api/load-test";
export default {
name: "RequestStatistics",

View File

@ -12,9 +12,9 @@
</template>
<script>
import PerformancePressureConfig from "@/business/test/components/PerformancePressureConfig";
import PerformanceAdvancedConfig from "@/business/test/components/PerformanceAdvancedConfig";
import PerformanceBasicConfig from "@/business/test/components/PerformanceBasicConfig";
import PerformancePressureConfig from "../../../business/test/components/PerformancePressureConfig";
import PerformanceAdvancedConfig from "../../../business/test/components/PerformanceAdvancedConfig";
import PerformanceBasicConfig from "../../../business/test/components/PerformanceBasicConfig";
export default {
name: "TestConfiguration",

View File

@ -103,11 +103,11 @@
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import {findThreadGroup} from "@/business/test/model/ThreadGroup";
import {findThreadGroup} from "../../../business/test/model/ThreadGroup";
import MsTableButton from "metersphere-frontend/src/components/MsTableButton";
import MsTableHeader from "metersphere-frontend/src/components/MsTableHeader";
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
import {checkFileIsRelated, deleteFile, getJmxContents, getProjectFileByName, getProjectFiles, updateFile, uploadFiles} from "@/api/performance";
import {checkFileIsRelated, deleteFile, getJmxContents, getProjectFileByName, getProjectFiles, updateFile, uploadFiles} from "../../../api/performance";
export default {
name: "ExistFiles",

View File

@ -52,10 +52,10 @@ import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {findThreadGroup} from "@/business/test/model/ThreadGroup";
import {findThreadGroup} from "../../../business/test/model/ThreadGroup";
import MsTableSearchBar from "metersphere-frontend/src/components/MsTableSearchBar";
import MsSearch from "metersphere-frontend/src/components/search/MsSearch";
import {exportScenarioJmx, searchScenarioList} from "@/api/performance";
import {exportScenarioJmx, searchScenarioList} from "../../../api/performance";
export default {
name: "ExistScenarios",

View File

@ -461,9 +461,9 @@
<script>
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton.vue";
import EditMonitor from "@/business/test/components/EditMonitor";
import BatchAddMonitor from "@/business/test/components/BatchAddMonitor";
import {getAdvancedConfig} from "@/api/performance";
import EditMonitor from "../../../business/test/components/EditMonitor";
import BatchAddMonitor from "../../../business/test/components/BatchAddMonitor";
import {getAdvancedConfig} from "../../../api/performance";
export default {
name: "PerformanceAdvancedConfig",

View File

@ -132,12 +132,12 @@ import MsTableButton from "metersphere-frontend/src/components/MsTableButton";
import MsTablePagination from "metersphere-frontend/src/components/pagination/TablePagination";
import MsTableOperatorButton from "metersphere-frontend/src/components/MsTableOperatorButton";
import MsDialogFooter from "metersphere-frontend/src/components/MsDialogFooter";
import ExistFiles from "@/business/test/components/ExistFiles";
import ExistScenarios from "@/business/test/components/ExistScenarios";
import {findThreadGroup} from "@/business/test/model/ThreadGroup";
import ExistFiles from "../../../business/test/components/ExistFiles";
import ExistScenarios from "../../../business/test/components/ExistScenarios";
import {findThreadGroup} from "../../../business/test/model/ThreadGroup";
import {hasPermission} from "metersphere-frontend/src/utils/permission";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {downloadFile, getFiles, getMetadataById} from "@/api/performance";
import {downloadFile, getFiles, getMetadataById} from "../../../api/performance";
export default {
name: "PerformanceBasicConfig",

View File

@ -246,8 +246,8 @@
<script>
import MsChart from "metersphere-frontend/src/components/chart/MsChart";
import {findThreadGroup} from "@/business/test/model/ThreadGroup";
import {getJmxContent, getLoadConfig, getResourcePools} from "@/api/performance";
import {findThreadGroup} from "../../../business/test/model/ThreadGroup";
import {getJmxContent, getLoadConfig, getResourcePools} from "../../../api/performance";
const HANDLER = "handler";
const THREAD_GROUP_TYPE = "tgType";

View File

@ -167,20 +167,20 @@
import {exportPdf} from "metersphere-frontend/src/utils";
import html2canvas from 'html2canvas';
import {Message} from "element-ui";
import MsPerformanceReportExport from "@/business/report/PerformanceReportExport";
import MsReportErrorLog from "@/business/report/components/ErrorLog";
import MsReportLogDetails from "@/business/report/components/LogDetails";
import MsReportRequestStatistics from "@/business/report/components/RequestStatistics";
import MsReportTestOverview from "@/business/report/components/TestOverview";
import MsPerformanceReportExport from "../../../../business/report/PerformanceReportExport";
import MsReportErrorLog from "../../../../business/report/components/ErrorLog";
import MsReportLogDetails from "../../../../business/report/components/LogDetails";
import MsReportRequestStatistics from "../../../../business/report/components/RequestStatistics";
import MsReportTestOverview from "../../../../business/report/components/TestOverview";
import MsContainer from "metersphere-frontend/src/components/MsContainer";
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MonitorCard from "@/business/report/components/MonitorCard";
import MsReportTestDetails from '@/business/report/components/TestDetails';
import ProjectEnvironmentDialog from "@/business/report/components/ProjectEnvironmentDialog";
import MonitorCard from "../../../../business/report/components/MonitorCard";
import MsReportTestDetails from '../../../../business/report/components/TestDetails';
import ProjectEnvironmentDialog from "../../../../business/report/components/ProjectEnvironmentDialog";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {getPerformanceReport, getPerformanceReportTime, getSharePerformanceReport, getSharePerformanceReportTime} from "@/api/load-test";
import MsTestConfiguration from "@/business/report/components/TestConfiguration";
import {getTestProInfo, stopTest} from "@/api/report";
import {getPerformanceReport, getPerformanceReportTime, getSharePerformanceReport, getSharePerformanceReportTime} from "../../../../api/load-test";
import MsTestConfiguration from "../../../../business/report/components/TestConfiguration";
import {getTestProInfo, stopTest} from "../../../../api/report";
export default {

View File

@ -1,210 +0,0 @@
<template>
<span></span>
</template>
<script>
// import {baseSocket} from "@/api/base-network";
// import {strMapToObj} from "metersphere-frontend/src/utils";
// import {getCurrentProjectID} from "metersphere-frontend/src/utils/token";
// import ThreadGroup from "./jmeter/components/thread-group";
// import TestPlan from "./jmeter/components/test-plan";
// import {TYPE_TO_C} from "@/business/automation/scenario/Setting";
// import {apiDefinitionRunDebug} from "@/api/remote/api/api-definition";
export default {
name: 'MsRun',
components: {},
props: {
editCaseRequest: {
type: Boolean,
default: false
},
debug: Boolean,
reportId: String,
runData: Array,
type: String,
envMap: Map,
isStop: Boolean,
},
data() {
return {
result: false,
loading: false,
requestResult: {responseResult: {}},
reqNumber: 0,
websocket: {}
}
},
watch: {
reportId() {
//
this.socketSync();
},
isStop() {
if (!this.isStop && this.websocket && this.websocket.close instanceof Function) {
this.websocket.close();
}
}
},
methods: {
socketSync() {
this.websocket = baseSocket(this.reportId);
this.websocket.onmessage = this.onMessages;
this.websocket.onerror = this.onError;
},
onError() {
this.$emit('runRefresh', "");
this.$error(this.$t('api_test.automation.rerun_warning'));
},
onMessages(e) {
//
if (e && e.data === "CONN_SUCCEEDED") {
this.run();
}
if (e.data && e.data.startsWith("result_")) {
try {
let data = e.data.substring(7);
this.websocket.close();
this.$emit('runRefresh', JSON.parse(data));
} catch (e) {
this.websocket.close();
this.$emit('runRefresh', "");
}
} else if (e.data === "MS_TEST_END") {
this.$emit('runRefresh', "");
}
},
sort(stepArray) {
if (stepArray) {
for (let i in stepArray) {
if (!stepArray[i].clazzName) {
stepArray[i].clazzName = TYPE_TO_C.get(stepArray[i].type);
}
if (stepArray[i].type === "Assertions" && !stepArray[i].document) {
stepArray[i].document = {
type: "JSON",
data: {xmlFollowAPI: false, jsonFollowAPI: false, json: [], xml: []}
};
}
if (stepArray[i] && stepArray[i].authManager && !stepArray[i].authManager.clazzName) {
stepArray[i].authManager.clazzName = TYPE_TO_C.get(stepArray[i].authManager.type);
}
if (stepArray[i].hashTree && stepArray[i].hashTree.length > 0) {
this.sort(stepArray[i].hashTree);
}
}
}
},
run() {
let projectId = getCurrentProjectID();
// envMap
if (!this.envMap || this.envMap.size === 0) {
projectId = getCurrentProjectID();
} else {
//
if (this.runData.projectId) {
projectId = this.runData.projectId;
}
}
let testPlan = new TestPlan();
testPlan.clazzName = TYPE_TO_C.get(testPlan.type);
let threadGroup = new ThreadGroup();
threadGroup.clazzName = TYPE_TO_C.get(threadGroup.type);
threadGroup.hashTree = [];
testPlan.hashTree = [threadGroup];
this.runData.forEach(item => {
item.projectId = projectId;
if (!item.clazzName) {
item.clazzName = TYPE_TO_C.get(item.type);
}
threadGroup.hashTree.push(item);
})
this.sort(testPlan.hashTree);
this.requestResult.reportId = this.reportId;
let reqObj = {
id: this.reportId,
testElement: testPlan,
type: this.type,
clazzName: this.clazzName ? this.clazzName : TYPE_TO_C.get(this.type),
projectId: projectId,
environmentMap: strMapToObj(this.envMap)
};
let bodyFiles = this.getBodyUploadFiles(reqObj, this.runData);
reqObj.editCaseRequest = this.editCaseRequest;
reqObj.debug = this.debug;
if (this.runData[0].url) {
reqObj.name = this.runData[0].url;
} else {
reqObj.name = this.runData[0].path;
}
if (this.runData[0].useEnvironment) {
reqObj.environmentId = this.runData[0].useEnvironment;
}
reqObj.reportId = this.reportId;
if (!this.debug) {
reqObj.syncResult = true;
}
apiDefinitionRunDebug(null, bodyFiles, reqObj).then(response => {
this.requestResult = response.data;
this.$emit('autoCheckStatus'); //
}, error => {
this.$emit('errorRefresh', {});
});
},
getBodyUploadFiles(obj, runData) {
let bodyUploadFiles = [];
obj.bodyUploadIds = [];
if (runData) {
if (runData instanceof Array) {
runData.forEach(request => {
obj.requestId = request.id;
this._getBodyUploadFiles(request, bodyUploadFiles, obj);
});
} else {
obj.requestId = runData.id;
this._getBodyUploadFiles(runData, bodyUploadFiles, obj);
}
}
return bodyUploadFiles;
},
_getBodyUploadFiles(request, bodyUploadFiles, obj) {
let body = null;
if (request.hashTree && request.hashTree.length > 0 && request.hashTree[0] && request.hashTree[0].body) {
obj.requestId = request.hashTree[0].id;
body = request.hashTree[0].body;
} else if (request.body) {
obj.requestId = request.id;
body = request.body;
}
if (body) {
if (body.kvs) {
body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
item.name = item.file.name ? item.file.name : item.name;
bodyUploadFiles.push(item.file);
}
});
}
});
}
if (body.binary) {
body.binary.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
item.name = item.file.name ? item.file.name : item.name;
bodyUploadFiles.push(item.file);
}
});
}
});
}
}
}
}
}
</script>

View File

@ -158,10 +158,6 @@
<test-plan-api-case-result :response="response" ref="apiCaseResult"/>
<!-- 执行组件 -->
<ms-run :debug="false" :type="'API_PLAN'" :reportId="reportId" :run-data="runData"
@runRefresh="runRefresh" @errorRefresh="errorRefresh" ref="runTest" @autoCheckStatus="autoCheckStatus"/>
<!-- 批量编辑 -->
<batch-edit :dialog-title="$t('test_track.case.batch_edit_case')" :type-arr="typeArr" :value-arr="valueArr"
:select-row="$refs.table ? $refs.table.selectRows : new Set()" ref="batchEdit"
@ -192,7 +188,6 @@ import {hasLicense} from "metersphere-frontend/src/utils/permission";
import {strMapToObj, getUUID} from "metersphere-frontend/src/utils";
import PriorityTableItem from "../../../../common/tableItems/planview/PriorityTableItem";
import TestPlanCaseListHeader from "./TestPlanCaseListHeader";
import MsRun from "./Run";
import TestPlanApiCaseResult from "./TestPlanApiCaseResult";
import {TEST_PLAN_API_CASE} from "metersphere-frontend/src/utils/constants";
import {
@ -235,7 +230,6 @@ export default {
HeaderLabelOperate,
HeaderCustom,
TestPlanApiCaseResult,
MsRun,
TestPlanCaseListHeader,
PriorityTableItem,
MsTablePagination,
@ -496,9 +490,10 @@ export default {
return RESULT_MAP.get("default");
}
},
runRefresh(data) {
runRefresh() {
this.rowLoading = '';
this.$success(this.$t('schedule.event_success'));
this.autoCheckStatus();
this.initTable();
},
singleRun(row) {

View File

@ -1,39 +0,0 @@
<template>
<el-drawer
class="load-case-report-drawer"
:visible.sync="drawer"
direction="ltr"
@close="handleClose"
size="80%">
<load-case-report-view :report-id="reportId" ref="loadCaseReportView"/>
</el-drawer>
</template>
<script>
import LoadCaseReportView from "@/business/plan/view/comonents/load/LoadCaseReportView";
export default {
name: "LoadCaseReport",
components: {LoadCaseReportView},
data() {
return {
drawer: false,
}
},
props: {
reportId: String
},
methods: {
handleClose() {
this.drawer = false;
this.$emit('refresh');
}
},
}
</script>
<style scoped>
.load-case-report-drawer :deep(.el-drawer__header) {
margin-bottom: 0;
}
</style>

View File

@ -1,546 +0,0 @@
<template>
<ms-container>
<el-main>
<el-card v-loading="loading" v-if="show">
<el-row v-if="isShare">
<el-col :span="24">
<div style="float:left;">
<div>
<span class="ms-report-time-desc-share">{{ $t('commons.name') }}{{ report.name }}</span>
<span class="ms-report-time-desc-share">{{ $t('commons.executor') }}{{ report.userName }}</span>
</div>
<div>
<span class="ms-report-time-desc-share">{{ $t('report.test_duration', [minutes, seconds]) }}</span>
<span class="ms-report-time-desc-share" v-if="startTime !== '0'">
{{ $t('report.test_start_time') }}{{ startTime | datetimeFormat }}
</span>
<span class="ms-report-time-desc-share" v-else>{{ $t('report.test_start_time') }}-</span>
<span class="ms-report-time-desc-share" v-if="report.status === 'Completed' && endTime !== '0'">
{{ $t('report.test_end_time') }}{{ endTime | datetimeFormat }}
</span>
<span class="ms-report-time-desc-share" v-else>{{ $t('report.test_end_time') }}- </span>
</div>
</div>
<div style="float: right;margin-right: 10px;">
<div v-if="showProjectEnv" type="flex">
<span> {{ $t('commons.environment') + ':' }} </span>
<div v-for="(values,key) in projectEnvMap" :key="key" style="margin-right: 10px">
{{ key + ":" }}
<ms-tag v-for="(item,index) in values" :key="index" type="success" :content="item"
style="margin-left: 2px"/>
</div>
<div v-show="showMoreProjectEnvMap">
<el-link icon="el-icon-more" @click="showAllProjectInfo"></el-link>
</div>
</div>
</div>
</el-col>
</el-row>
<el-row v-else>
<el-col :span="16">
<el-row v-if="!isPlanReport">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item :to="{ path: '/performance/test/' + this.projectId }">{{ projectName }}
</el-breadcrumb-item>
<el-breadcrumb-item :to="{ path: '/performance/test/edit/' + this.testId }">{{ testName }}
</el-breadcrumb-item>
<el-breadcrumb-item>{{ reportName }}</el-breadcrumb-item>
</el-breadcrumb>
</el-row>
<el-row class="ms-report-view-btns" v-if="!isPlanReport">
<el-button :disabled="isReadOnly || report.status !== 'Running'" type="primary" plain size="mini"
@click="dialogFormVisible=true">
{{ $t('report.test_stop_now') }}
</el-button>
<el-button :disabled="isReadOnly" type="info" plain size="mini" @click="handleExport(reportName)">
{{ $t('test_track.plan_view.export_report') }}
</el-button>
<el-button :disabled="isReadOnly" type="warning" plain size="mini" @click="downloadJtl()">
{{ $t('report.downloadJtl') }}
</el-button>
</el-row>
</el-col>
<el-col :span="8">
<div v-if="isPlanReport" style="float: right;margin-right: 10px;">
<div v-if="showProjectEnv" type="flex">
<span> {{ $t('commons.environment') + ':' }} </span>
<div v-for="(values,key) in projectEnvMap" :key="key" style="margin-right: 10px">
{{ key + ":" }}
<ms-tag v-for="(item,index) in values" :key="index" type="success" :content="item"
style="margin-left: 2px"/>
</div>
<div v-show="showMoreProjectEnvMap">
<el-link icon="el-icon-more" @click="showAllProjectInfo"></el-link>
</div>
</div>
</div>
<div style="float: left">
<span class="ms-report-time-desc">
{{
$t('report.test_duration', [templateMinutes ? templateMinutes : minutes,
templateSeconds ? templateSeconds : seconds])
}}
</span>
<span class="ms-report-time-desc" v-if="startTime !== '0'">
{{ $t('report.test_start_time') }}{{ startTime | datetimeFormat }}
</span>
<span class="ms-report-time-desc" v-else-if="planReportTemplate && planReportTemplate.startTime">
{{ $t('report.test_start_time') }}{{ planReportTemplate.startTime | datetimeFormat }}
</span>
<span class="ms-report-time-desc" v-else>
{{ $t('report.test_start_time') }}-
</span>
<span class="ms-report-time-desc" v-if="report.status === 'Completed' && endTime !== '0'">
{{ $t('report.test_end_time') }}{{ endTime | datetimeFormat }}
</span>
<span class="ms-report-time-desc" v-else-if="planReportTemplate && planReportTemplate.endTime">
{{ $t('report.test_end_time') }}{{ planReportTemplate.endTime | datetimeFormat }}
</span>
<span class="ms-report-time-desc" v-else>
{{ $t('report.test_end_time') }}-
</span>
</div>
</el-col>
</el-row>
<el-divider/>
<div ref="resume">
<el-tabs v-model="active">
<el-tab-pane :label="$t('report.test_overview')">
<ms-report-test-overview :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
:share-id="shareId" ref="testOverview"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_details')">
<ms-report-test-details :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
:share-id="shareId" ref="testDetails"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_request_statistics')">
<ms-report-request-statistics :report="report" :is-share="isShare"
:plan-report-template="planReportTemplate"
:share-id="shareId" ref="requestStatistics"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_error_log')">
<ms-report-error-log :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
:share-id="shareId" ref="errorLog"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_log_details')">
<ms-report-log-details :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
:share-id="shareId"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_monitor_details')">
<monitor-card :report="report" :is-share="isShare" :plan-report-template="planReportTemplate"
:share-id="shareId"/>
</el-tab-pane>
<el-tab-pane :label="$t('report.test_config')">
<ms-test-configuration :report-id="reportId" :report="report"
:plan-report-template="planReportTemplate"
:is-share="isShare" :share-id="shareId"/>
</el-tab-pane>
</el-tabs>
</div>
<ms-performance-report-export v-if="!isShare && !planReportTemplate" :title="reportName"
id="performanceReportExport" v-show="reportExportVisible"
:report="report"/>
</el-card>
<el-dialog :title="$t('report.test_stop_now_confirm')" :visible.sync="dialogFormVisible" width="30%"
:append-to-body="true">
<p v-html="$t('report.force_stop_tips')"/>
<p v-html="$t('report.stop_tips')"/>
<div slot="footer" class="dialog-footer">
<el-button type="danger" size="small" @click="stopTest(true)">{{ $t('report.force_stop_btn') }}
</el-button>
<el-button type="primary" size="small" @click="stopTest(false)">{{ $t('report.stop_btn') }}
</el-button>
</div>
</el-dialog>
<project-environment-dialog ref="projectEnvDialog"></project-environment-dialog>
</el-main>
</ms-container>
</template>
<script>
// import {exportPdf} from "metersphere-frontend/src/utils";
// import html2canvas from 'html2canvas';
// import {Message} from "element-ui";
// import MsPerformanceReportExport from "metersphere-frontend/src/components/report/PerformanceReportExport";
// import MsReportErrorLog from "metersphere-frontend/src/components/report/ErrorLog";
// import MsReportLogDetails from "metersphere-frontend/src/components/report/LogDetails";
// import MsReportRequestStatistics from "metersphere-frontend/src/components/report/RequestStatistics";
// import MsReportTestOverview from "metersphere-frontend/src/components/report/TestOverview";
// import MsContainer from "metersphere-frontend/src/components/MsContainer";
// import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
// import MonitorCard from "metersphere-frontend/src/components/report/MonitorCard";
// import MsReportTestDetails from 'metersphere-frontend/src/components/report/TestDetails';
// import ProjectEnvironmentDialog from "metersphere-frontend/src/components/report/ProjectEnvironmentDialog";
import MsTag from "metersphere-frontend/src/components/MsTag";
import {
getPerformanceReport,
getPerformanceReportTime,
getSharePerformanceReport,
getSharePerformanceReportTime
} from "@/api/load-test";
// import {
// getPerformanceReport,
// getPerformanceReportTime,
// getSharePerformanceReport,
// getSharePerformanceReportTime
// } from "@/api/load-test";
// import MsTestConfiguration from "@/business/performance/TestConfiguration";
export default {
name: "LoadCaseReportView",
components: {
// MsTestConfiguration,
// MonitorCard,
// MsPerformanceReportExport,
// MsReportErrorLog,
// MsReportLogDetails,
// MsReportRequestStatistics,
// MsReportTestOverview,
// MsReportTestDetails,
// MsContainer,
// MsMainContainer,
// ProjectEnvironmentDialog,
MsTag,
},
data() {
return {
loading: false,
active: '0',
status: '',
reportName: '',
testId: '',
testName: '',
projectId: '',
projectName: '',
startTime: '0',
endTime: '0',
minutes: '0',
seconds: '0',
title: 'Logging',
projectEnvMap: null,
showMoreProjectEnvMap: false,
allProjectEnvMap: null,
report: {},
websocket: null,
dialogFormVisible: false,
reportExportVisible: false,
testPlan: {testResourcePoolId: null},
show: true,
test: {testResourcePoolId: null},
};
},
props: {
reportId: String,
isReadOnly: {
type: Boolean,
default: false
},
isPlanReport: Boolean,
isShare: Boolean,
shareId: String,
planReportTemplate: Object
},
watch: {
reportId() {
this.init();
}
},
computed: {
showProjectEnv() {
return this.projectEnvMap && JSON.stringify(this.projectEnvMap) !== '{}';
},
templateMinutes() {
if (this.planReportTemplate && this.planReportTemplate.duration) {
let duration = this.planReportTemplate.duration;
return Math.floor(duration / 60);
}
return null;
},
templateSeconds() {
if (this.planReportTemplate && this.planReportTemplate.duration) {
let duration = this.planReportTemplate.duration;
return duration % 60;
}
return null;
}
},
methods: {
showAllProjectInfo() {
this.$refs.projectEnvDialog.open(this.allProjectEnvMap);
},
isProjectEnvShowMore(projectEnvMap) {
this.showMoreProjectEnvMap = false;
this.projectEnvMap = {};
if (projectEnvMap) {
let keySize = 0;
for (let key in projectEnvMap) {
keySize++;
if (keySize > 1) {
this.showMoreProjectEnvMap = true;
return;
} else {
this.projectEnvMap = {};
this.$set(this.projectEnvMap, key, projectEnvMap[key]);
}
}
}
},
initBreadcrumb(callback) {
if (this.isPlanReport) {
return;
}
if (this.reportId) {
this.loading = true;
this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
this.loading = false;
let data = res.data;
if (data) {
this.reportName = data.name;
this.testId = data.testId;
this.testName = data.testName;
this.projectId = data.projectId;
this.projectName = data.projectName;
if (callback) callback(res);
} else {
this.$error(this.$t('report.not_exist'));
}
});
}
},
initReportTimeInfo() {
if (this.status === 'Starting') {
this.clearData();
return;
}
if (this.planReportTemplate) {
this.handleInitReportTimeInfo(this.planReportTemplate);
} else if (this.isShare) {
if (this.reportId) {
this.loading = true;
getSharePerformanceReportTime(this.shareId, this.reportId)
.then((res) => {
this.loading = false;
this.handleInitReportTimeInfo(res.data.data);
}).catch(() => {
this.clearData();
});
}
} else {
if (this.reportId) {
this.loading = true; getPerformanceReportTime(this.reportId).then(res => {
this.loading = false;
this.handleInitReportTimeInfo(res.data.data);
}).catch(() => {
this.clearData();
});
}
}
},
handleInitReportTimeInfo(data) {
if (data) {
this.startTime = data.startTime;
this.endTime = data.endTime;
let duration = data.duration;
this.minutes = Math.floor(duration / 60);
this.seconds = duration % 60;
}
},
initWebSocket() {
if (this.isPlanReport) {
return;
}
let protocol = "ws://";
if (window.location.protocol === 'https:') {
protocol = "wss://";
}
const uri = protocol + window.location.host + "/performance/report/" + 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;
},
checkReportStatus(status) {
switch (status) {
case 'Error':
// this.$warning(this.$t('report.generation_error'));
this.active = '4';
break;
case 'Starting':
this.$alert(this.$t('report.start_status'));
break;
case 'Reporting':
case 'Running':
case 'Completed':
default:
break;
}
},
clearData() {
this.show = false;
this.startTime = '0';
this.endTime = '0';
this.minutes = '0';
this.seconds = '0';
this.$nextTick(() => {
this.show = true;
});
},
stopTest(forceStop) {
this.loading = true; this.$get('/performance/stop/' + this.reportId + '/' + forceStop, () => {
this.$success(this.$t('report.test_stop_success'));
if (forceStop) {
this.$router.push('/performance/report/all');
} else {
this.report.status = 'Completed';
}
});
this.dialogFormVisible = false;
},
onOpen() {
},
onError(e) {
},
onMessage(e) {
this.$set(this.report, "refresh", e.data); //
if (e.data.startsWith('Error')) {
this.$set(this.report, "status", 'Error');
this.$warning(e.data);
return;
}
this.$set(this.report, "status", 'Running');
this.status = 'Running';
this.initReportTimeInfo();
},
onClose(e) {
if (e.code === 1005) {
// socketreport
return;
}
this.$set(this.report, "refresh", Math.random()); //
this.$set(this.report, "status", 'Completed');
this.initReportTimeInfo();
},
handleExport(name) {
this.loading = true;
this.reportExportVisible = true;
let reset = this.exportReportReset;
this.$nextTick(function () {
setTimeout(() => {
let ids = ['testOverview', 'testDetails', 'requestStatistics', 'errorLog'];
let promises = [];
ids.forEach(id => {
let promise = html2canvas(document.getElementById(id), {scale: 2});
promises.push(promise);
});
Promise.all(promises).then(function (canvas) {
exportPdf(name, canvas);
reset();
});
}, 1000);
});
},
exportReportReset() {
this.reportExportVisible = false;
this.loading = false;
},
downloadJtl() {
let config = {
url: "/performance/report/jtl/download/" + this.reportId,
method: 'get',
responseType: 'blob'
};
this.loading = true; this.$request(config).then(response => {
const content = response.data;
const blob = new Blob([content]);
if ("download" in document.createElement("a")) {
// IE
// chrome/firefox
let aTag = document.createElement('a');
aTag.download = this.reportName + ".jtl";
aTag.href = URL.createObjectURL(blob);
aTag.click();
URL.revokeObjectURL(aTag.href);
} else {
// IE10+
navigator.msSaveBlob(blob, this.filename);
}
}).catch(e => {
let text = e.response.data.text();
text.then((data) => {
Message.error({message: JSON.parse(data).message || e.message, showClose: true});
});
});
},
init() {
this.clearData();
if (this.planReportTemplate) {
this.handleInit(this.planReportTemplate);
} else if (this.isShare) {
this.loading = true;
getSharePerformanceReport(this.shareId, this.reportId, data => {
this.handleInit(data);
});
} else {
this.loading = true;
getPerformanceReport(this.reportId, data => {
this.handleInit(data);
});
}
},
handleInit(data) {
if (data) {
this.allProjectEnvMap = data.projectEnvMap;
this.isProjectEnvShowMore(data.projectEnvMap);
this.status = data.status;
this.$set(this, "report", data);
this.$set(this.test, "testResourcePoolId", data.testResourcePoolId);
this.checkReportStatus(data.status);
if (this.status === "Completed" || this.status === "Running") {
this.initReportTimeInfo();
}
this.initBreadcrumb();
this.initWebSocket();
} else {
this.$error(this.$t('report.not_exist'));
}
}
},
created() {
this.init();
}
};
</script>
<style scoped>
.ms-report-view-btns {
margin-top: 15px;
}
.ms-report-time-desc {
text-align: left;
display: block;
color: #5C7878;
}
.ms-report-time-desc-share {
text-align: left;
color: #5C7878;
padding-right: 20px;
}
.report-export .el-card {
margin-bottom: 15px;
}
</style>

View File

@ -49,21 +49,31 @@
</el-card>
</ms-aside-container>
<ms-main-container v-loading="responseLoading">
<el-card v-if="showResponse">
<micro-app route-name="ApiReportView"
<div v-if="showResponse">
<micro-app v-if="!isTemplate"
route-name="ApiReportView"
service="api"
:route-params="{
isTestPlan: showResponse,
response
}"/>
<el-card v-else>
<ms-request-result-tail :response="response"
:is-test-plan="showResponse"
ref="debugResult"/>
</el-card>
</div>
</el-card>
<div class="empty" v-else>{{ $t('test_track.plan.load_case.content_empty') }}</div>
</ms-main-container>
</el-container>
</template>
<script>
//
import MsRequestResultTail from "../../../../../../../../../../api-test/frontend/src/business/definition/components/response/RequestResultTail";
import PriorityTableItem from "../../../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
@ -94,7 +104,8 @@ export default {
MsMainContainer,
MsAsideContainer,
MicroApp,
MsTableColumn, MsTable, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem
MsTableColumn, MsTable, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem,
MsRequestResultTail
},
props: {
planId: String,

View File

@ -50,17 +50,28 @@
</ms-aside-container>
<el-main>
<micro-app v-if="showResponse"
route-name="ApiScenarioReportView"
service="api"
:route-params="{
reportId,
isShare,
shareId,
isPlanReport: true,
isTemplate,
response
}"/>
<div v-if="showResponse">
<micro-app
v-if="!isTemplate"
route-name="ApiScenarioReportView"
service="api"
:route-params="{
reportId,
isShare,
shareId,
isPlanReport: true,
isTemplate,
response
}"/>
<ms-api-report v-else
:report-id="reportId"
:is-share="isShare"
:share-id="shareId"
:is-plan="true"
:is-template="isTemplate"
:template-report="response"/>
</div>
<div class="empty" v-else>{{ $t('test_track.plan.load_case.content_empty') }}</div>
</el-main>
@ -68,6 +79,10 @@
</template>
<script>
//
import MsApiReport from "../../../../../../../../../../api-test/frontend/src/business/automation/report/ApiReportDetail";
import PriorityTableItem from "../../../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
@ -96,7 +111,8 @@ export default {
MsMainContainer,
MsAsideContainer,
MicroApp,
MsTableColumn, MsTable, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem
MsTableColumn, MsTable, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem,
MsApiReport
},
props: {
planId: String,

View File

@ -13,18 +13,33 @@
@setSize="setAllSize"/>
</ms-aside-container>
<ms-main-container>
<micro-app v-if="showResponse"
route-name="perReportView"
service="performance"
:route-params="{
<div v-if="showResponse">
<micro-app v-if="!isTemplate"
route-name="perReportView"
service="performance"
:route-params="{
reportId,
}"/>
<load-case-report-view v-else
:is-plan-report="true"
:share-id="shareId"
:is-share="isShare"
:plan-report-template="response"
:report-id="reportId"
ref="loadCaseReportView"/>
</div>
<div class="empty" v-show="!showResponse">{{ $t('test_track.plan.load_case.content_empty') }}</div>
</ms-main-container>
</el-container>
</template>
<script>
//
import LoadCaseReportView from "../../../../../../../../../../performance-test/frontend/src/template/report/performance/share/LoadCaseReportView";
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
@ -35,13 +50,13 @@ import MsAsideContainer from "metersphere-frontend/src/components/MsAsideContain
import MsMainContainer from "metersphere-frontend/src/components/MsMainContainer";
import MicroApp from "metersphere-frontend/src/components/MicroApp";
export default {
name: "LoadAllResult",
components: {
MsMainContainer,
MsAsideContainer,
LoadFailureResult, StatusTableItem, MethodTableItem, TypeTableItem,
LoadCaseReportView,
MicroApp
},
props: {

View File

@ -97,7 +97,7 @@ module.exports = {
// 报告模板打包成一个html
config.plugin('inline-source-html-planReport')
.after('html-planReport')
.use(HtmlWebpackInlineSourcePlugin, [HtmlWebpackPlugin, [/.(js|css)$/]]);
.use(HtmlWebpackInlineSourcePlugin, [HtmlWebpackPlugin]);
config.plugins.delete('prefetch');
}