feat: 测试计划-统计报告
This commit is contained in:
parent
1fee4b682c
commit
9c199766b0
|
@ -0,0 +1,60 @@
|
|||
package io.metersphere.track.domain;
|
||||
|
||||
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
|
||||
import io.metersphere.track.dto.TestCaseReportMetricDTO;
|
||||
import io.metersphere.track.dto.TestCaseReportStatusResultDTO;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ReportResultChartComponent extends ReportComponent {
|
||||
Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap = new HashMap<>();
|
||||
|
||||
public ReportResultChartComponent(TestPlanDTO testPlan) {
|
||||
super(testPlan);
|
||||
componentId = "3";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void readRecord(TestPlanCaseDTO testCase) {
|
||||
getStatusResultMap(reportStatusResultMap, testCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterBuild(TestCaseReportMetricDTO testCaseReportMetric) {
|
||||
testCaseReportMetric.setExecuteResult(getReportStatusResult());
|
||||
}
|
||||
|
||||
private List<TestCaseReportStatusResultDTO> getReportStatusResult() {
|
||||
List<TestCaseReportStatusResultDTO> reportStatusResult = new ArrayList<>();
|
||||
addToReportStatusResultList(reportStatusResult, TestPlanTestCaseStatus.Pass.name());
|
||||
addToReportStatusResultList(reportStatusResult, TestPlanTestCaseStatus.Failure.name());
|
||||
addToReportStatusResultList(reportStatusResult, TestPlanTestCaseStatus.Blocking.name());
|
||||
addToReportStatusResultList(reportStatusResult, TestPlanTestCaseStatus.Skip.name());
|
||||
addToReportStatusResultList(reportStatusResult, TestPlanTestCaseStatus.Underway.name());
|
||||
addToReportStatusResultList(reportStatusResult, TestPlanTestCaseStatus.Prepare.name());
|
||||
return reportStatusResult;
|
||||
}
|
||||
|
||||
private void addToReportStatusResultList(List<TestCaseReportStatusResultDTO> reportStatusResultList, String status) {
|
||||
if (reportStatusResultMap.get(status) != null) {
|
||||
reportStatusResultList.add(reportStatusResultMap.get(status));
|
||||
}
|
||||
}
|
||||
|
||||
private void getStatusResultMap(Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap, TestPlanCaseDTO testCase) {
|
||||
TestCaseReportStatusResultDTO statusResult = reportStatusResultMap.get(testCase.getStatus());
|
||||
if (statusResult == null) {
|
||||
statusResult = new TestCaseReportStatusResultDTO();
|
||||
statusResult.setStatus(testCase.getStatus());
|
||||
statusResult.setCount(0);
|
||||
}
|
||||
statusResult.setCount(statusResult.getCount() + 1);
|
||||
reportStatusResultMap.put(testCase.getStatus(), statusResult);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.track.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class TestCaseReportStatusResultDTO {
|
||||
private String status;
|
||||
private Integer count;
|
||||
}
|
||||
|
|
@ -14,11 +14,13 @@
|
|||
class="el-menu-demo header-menu" mode="horizontal" @select="handleSelect">
|
||||
<el-menu-item index="functional">功能测试用例</el-menu-item>
|
||||
<el-menu-item index="api">接口测试用例</el-menu-item>
|
||||
<el-menu-item index="report">报告统计</el-menu-item>
|
||||
</el-menu>
|
||||
</template>
|
||||
</ms-test-plan-header-bar>
|
||||
<test-plan-functional v-if="activeIndex === 'functional'" :plan-id="planId"/>
|
||||
<test-plan-api v-if="activeIndex === 'api'" :plan-id="planId"/>
|
||||
<test-case-statistics-report-view :test-plan="currentPlan" v-if="activeIndex === 'report'"/>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
@ -35,10 +37,12 @@
|
|||
import MsTestPlanHeaderBar from "./comonents/head/TestPlanHeaderBar";
|
||||
import TestPlanFunctional from "./comonents/functional/TestPlanFunctional";
|
||||
import TestPlanApi from "./comonents/api/TestPlanApi";
|
||||
import TestCaseStatisticsReportView from "./comonents/report/statistics/TestCaseStatisticsReportView";
|
||||
|
||||
export default {
|
||||
name: "TestPlanView",
|
||||
components: {
|
||||
TestCaseStatisticsReportView,
|
||||
TestPlanApi,
|
||||
TestPlanFunctional,
|
||||
MsTestPlanHeaderBar,
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
<div v-if="!metric">
|
||||
<base-info-component :is-report="false" v-if="preview.id == 1"/>
|
||||
<test-result-component v-if="preview.id == 2"/>
|
||||
<test-result-chart-component v-if="preview.id == 3"/>
|
||||
<!--<test-result-chart-component v-if="preview.id == 3"/>-->
|
||||
<test-result-advance-chart-component v-if="preview.id == 3"/>
|
||||
<failure-result-component v-if="preview.id == 4"/>
|
||||
<defect-list-component v-if="preview.id == 5"/>
|
||||
<rich-text-component :preview="preview" v-if="preview.type != 'system'"/>
|
||||
|
@ -15,7 +16,8 @@
|
|||
<div v-if="metric">
|
||||
<base-info-component id="baseInfoComponent" :report-info="metric" v-if="preview.id == 1"/>
|
||||
<test-result-component id="testResultComponent" :test-results="metric.moduleExecuteResult" v-if="preview.id == 2"/>
|
||||
<test-result-chart-component id="resultChartComponent" :execute-result="metric.executeResult" v-if="preview.id == 3"/>
|
||||
<!--<test-result-chart-component id="resultChartComponent" :execute-result="metric.executeResult" v-if="preview.id == 3"/>-->
|
||||
<test-result-advance-chart-component id="resultChartComponent" :execute-result="metric.executeResult" v-if="preview.id == 3"/>
|
||||
<failure-result-component id="failureResultComponent" :failure-test-cases="metric.failureTestCases" v-if="preview.id == 4"/>
|
||||
<defect-list-component id="defectListComponent" :defect-list="metric.issues" v-if="preview.id == 5"/>
|
||||
<rich-text-component id="richTextComponent" :is-report-view="isReportView" :preview="preview" v-if="preview.type != 'system'"/>
|
||||
|
@ -32,10 +34,12 @@
|
|||
import FailureResultComponent from "./FailureResultComponent";
|
||||
import DefectListComponent from "./DefectListComponent";
|
||||
import html2canvas from 'html2canvas';
|
||||
import TestResultAdvanceChartComponent from "./TestResultAdvanceChartComponent";
|
||||
|
||||
export default {
|
||||
name: "TemplateComponent",
|
||||
components: {
|
||||
TestResultAdvanceChartComponent,
|
||||
FailureResultComponent,DefectListComponent,
|
||||
RichTextComponent, TestResultChartComponent, TestResultComponent, BaseInfoComponent},
|
||||
props: {
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
<template>
|
||||
|
||||
<common-component :title="$t('test_track.plan_view.result_statistics')">
|
||||
|
||||
<template>
|
||||
|
||||
<div class="char-item">
|
||||
<ms-pie-chart v-if="isShow" :text="'功能测试用例'"
|
||||
:name="$t('test_track.plan_view.test_result')" :data="functionalCharData"/>
|
||||
</div>
|
||||
|
||||
<div class="char-item">
|
||||
<ms-pie-chart v-if="isShow" :text="'接口测试用例'"
|
||||
:name="$t('test_track.plan_view.test_result')" :data="apiCharData"/>
|
||||
</div>
|
||||
|
||||
<div class="char-item">
|
||||
<ms-pie-chart v-if="isShow" :text="'场景测试用例'"
|
||||
:name="$t('test_track.plan_view.test_result')" :data="scenarioCharData"/>
|
||||
</div>
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
</common-component>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CommonComponent from "./CommonComponent";
|
||||
import MsPieChart from "../../../../../../common/components/MsPieChart";
|
||||
|
||||
export default {
|
||||
name: "TestResultAdvanceChartComponent",
|
||||
components: {MsPieChart, CommonComponent},
|
||||
data() {
|
||||
return {
|
||||
dataMap: new Map([
|
||||
["Pass", {name: this.$t('test_track.plan_view.pass'), itemStyle: {color: '#67C23A'}}],
|
||||
["Failure", {name: this.$t('test_track.plan_view.failure'), itemStyle: {color: '#F56C6C'}}],
|
||||
["Blocking", {name: this.$t('test_track.plan_view.blocking'), itemStyle: {color: '#E6A23C'}}],
|
||||
["Skip", {name: this.$t('test_track.plan_view.skip'), itemStyle: {color: '#909399'}}],
|
||||
["Underway", {name: this.$t('test_track.plan.plan_status_running'), itemStyle: {color: 'lightskyblue'}}],
|
||||
["Prepare", {name: this.$t('test_track.plan.plan_status_prepare'), itemStyle: {color: '#DEDE10'}}]
|
||||
]),
|
||||
functionalCharData: [],
|
||||
apiCharData: [],
|
||||
scenarioCharData: [],
|
||||
isShow: true
|
||||
}
|
||||
},
|
||||
props: {
|
||||
executeResult: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
functionalResult: [
|
||||
{status: 'Pass', count: '235'},
|
||||
{status: 'Failure', count: '310'},
|
||||
{status: 'Blocking', count: '274'},
|
||||
{status: 'Skip', count: '335'},
|
||||
{status: 'Underway', count: '245'},
|
||||
{status: 'Prepare', count: '265'},
|
||||
],
|
||||
apiResult: [
|
||||
{status: 'Pass', count: '235'},
|
||||
{status: 'Failure', count: '310'},
|
||||
{status: 'Underway', count: '245'},
|
||||
],
|
||||
scenarioResult: [
|
||||
{status: 'Pass', count: '205'},
|
||||
{status: 'Failure', count: '350'},
|
||||
{status: 'Underway', count: '110'},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
executeResult() {
|
||||
this.getCharData();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getCharData();
|
||||
},
|
||||
methods: {
|
||||
getCharData() {
|
||||
this.getFunctionalCharData();
|
||||
this.getApiCharData();
|
||||
this.getScenarioCharData();
|
||||
this.reload();
|
||||
},
|
||||
getFunctionalCharData() {
|
||||
this.functionalCharData = [];
|
||||
if (this.executeResult.functionalResult) {
|
||||
this.executeResult.functionalResult.forEach(item => {
|
||||
let data = this.dataMap.get(item.status);
|
||||
data.value = item.count;
|
||||
this.functionalCharData.push(data);
|
||||
});
|
||||
}
|
||||
},
|
||||
getApiCharData() {
|
||||
this.apiCharData = [];
|
||||
if (this.executeResult.apiResult) {
|
||||
this.executeResult.apiResult.forEach(item => {
|
||||
let data = this.dataMap.get(item.status);
|
||||
data.value = item.count;
|
||||
this.apiCharData.push(data);
|
||||
});
|
||||
}
|
||||
},
|
||||
getScenarioCharData() {
|
||||
this.scenarioCharData = [];
|
||||
if (this.executeResult.apiResult) {
|
||||
this.executeResult.scenarioResult.forEach(item => {
|
||||
let data = this.dataMap.get(item.status);
|
||||
data.value = item.count;
|
||||
this.scenarioCharData.push(data);
|
||||
});
|
||||
}
|
||||
},
|
||||
reload() {
|
||||
this.isShow = false;
|
||||
this.$nextTick(function () {
|
||||
this.isShow = true;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.echarts {
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.char-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,252 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
<el-row type="flex" class="head-bar">
|
||||
<el-col :span="12">
|
||||
</el-col>
|
||||
|
||||
<el-col v-if="!reportId" :span="12" class="head-right">
|
||||
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="openTemplateReport">
|
||||
{{$t('test_track.plan_view.create_report')}}
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col v-else :span="12" class="head-right">
|
||||
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleSave">
|
||||
{{$t('commons.save')}}
|
||||
</el-button>
|
||||
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleEdit">
|
||||
{{$t('test_track.plan_view.edit_component')}}
|
||||
</el-button>
|
||||
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExport(report.name)">
|
||||
{{$t('test_track.plan_view.export_report')}}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="container" ref="resume" id="app">
|
||||
<el-main>
|
||||
<div v-for="(item, index) in previews" :key="item.id">
|
||||
<template-component :isReportView="true" :metric="metric" :preview="item" :index="index" ref="templateComponent"/>
|
||||
</div>
|
||||
</el-main>
|
||||
</div>
|
||||
|
||||
<test-report-template-list @openReport="openReport" ref="testReportTemplateList"/>
|
||||
|
||||
<ms-test-case-report-export v-if="reportExportVisible" id="testCaseReportExport" :title="report.name" :metric="metric" :previews="previews"/>
|
||||
<test-case-report-template-edit :metric="metric" ref="templateEdit" @refresh="getReport"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {checkoutTestManagerOrTestUser, exportPdf, jsonToMap, mapToJson} from "../../../../../../../../common/js/utils";
|
||||
import BaseInfoComponent from "../TemplateComponent/BaseInfoComponent";
|
||||
import TestResultChartComponent from "../TemplateComponent/TestResultChartComponent";
|
||||
import TestResultComponent from "../TemplateComponent/TestResultComponent";
|
||||
import RichTextComponent from "../TemplateComponent/RichTextComponent";
|
||||
import TestCaseReportTemplateEdit from "../TestCaseReportTemplateEdit";
|
||||
import TemplateComponent from "../TemplateComponent/TemplateComponent";
|
||||
import html2canvas from "html2canvas";
|
||||
import MsTestCaseReportExport from "../../TestCaseReportExport";
|
||||
import TestReportTemplateList from "../../TestReportTemplateList";
|
||||
|
||||
export default {
|
||||
name: "TestCaseStatisticsReportView",
|
||||
components: {
|
||||
TestReportTemplateList,
|
||||
MsTestCaseReportExport,
|
||||
TemplateComponent,
|
||||
TestCaseReportTemplateEdit,
|
||||
RichTextComponent, TestResultComponent, TestResultChartComponent, BaseInfoComponent
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
imgUrl: "",
|
||||
previews: [],
|
||||
report: {},
|
||||
metric: {},
|
||||
reportExportVisible: false,
|
||||
componentMap: new Map(
|
||||
[
|
||||
[1, {name: this.$t('test_track.plan_view.base_info'), id: 1, type: 'system'}],
|
||||
[2, {name: this.$t('test_track.plan_view.test_result'), id: 2, type: 'system'}],
|
||||
[3, {name: this.$t('test_track.plan_view.result_distribution'), id: 3, type: 'system'}],
|
||||
[4, {name: this.$t('test_track.plan_view.failure_case'), id: 4, type: 'system'}],
|
||||
[5, {name: this.$t('test_track.plan_view.defect_list'), id: 5, type: 'system'}],
|
||||
[6, {name: this.$t('test_track.plan_view.custom_component'), id: 6, type: 'custom'}]
|
||||
]
|
||||
),
|
||||
isTestManagerOrTestUser: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.isTestManagerOrTestUser = checkoutTestManagerOrTestUser();
|
||||
this.getReport();
|
||||
},
|
||||
watch: {
|
||||
reportId() {
|
||||
this.getReport();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
planId() {
|
||||
return this.testPlan.id;
|
||||
},
|
||||
reportId() {
|
||||
return this.testPlan.reportId;
|
||||
}
|
||||
},
|
||||
props: ['testPlan'],
|
||||
methods: {
|
||||
openTemplateReport() {
|
||||
this.$refs.testReportTemplateList.open(this.planId);
|
||||
},
|
||||
openReport(planId, id) {
|
||||
this.testPlan.reportId = id;
|
||||
},
|
||||
getReport() {
|
||||
if (this.reportId) {
|
||||
this.result = this.$get('/case/report/get/' + this.reportId, response => {
|
||||
this.report = response.data;
|
||||
this.report.content = JSON.parse(response.data.content);
|
||||
if (this.report.content.customComponent) {
|
||||
this.report.content.customComponent = jsonToMap(this.report.content.customComponent);
|
||||
}
|
||||
this.getMetric();
|
||||
this.initPreviews();
|
||||
});
|
||||
}
|
||||
},
|
||||
initPreviews() {
|
||||
this.previews = [];
|
||||
this.report.content.components.forEach(item => {
|
||||
let preview = this.componentMap.get(item);
|
||||
if (preview && preview.type != 'custom') {
|
||||
this.previews.push(preview);
|
||||
} else {
|
||||
if (this.report.content.customComponent) {
|
||||
let customComponent = this.report.content.customComponent.get(item.toString());
|
||||
if (customComponent) {
|
||||
this.previews.push({id: item, title: customComponent.title, content: customComponent.content});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
handleEdit() {
|
||||
this.$refs.templateEdit.open(this.reportId, true);
|
||||
},
|
||||
handleSave() {
|
||||
let param = {};
|
||||
this.buildParam(param);
|
||||
this.result = this.$post('/case/report/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
});
|
||||
},
|
||||
buildParam(param) {
|
||||
let content = {};
|
||||
content.components = [];
|
||||
this.previews.forEach(item => {
|
||||
content.components.push(item.id);
|
||||
if (!this.componentMap.get(item.id)) {
|
||||
content.customComponent = new Map();
|
||||
content.customComponent.set(item.id, {title: item.title, content: item.content})
|
||||
}
|
||||
});
|
||||
param.name = this.report.name;
|
||||
if (content.customComponent) {
|
||||
content.customComponent = mapToJson(content.customComponent);
|
||||
}
|
||||
param.content = JSON.stringify(content);
|
||||
param.id = this.report.id;
|
||||
if (this.metric.startTime) {
|
||||
param.startTime = this.metric.startTime.getTime();
|
||||
}
|
||||
if (this.metric.endTime) {
|
||||
param.endTime = this.metric.endTime.getTime();
|
||||
}
|
||||
},
|
||||
getMetric() {
|
||||
this.result = this.$get('/test/plan/get/statistics/metric/' + this.planId, response => {
|
||||
this.metric = response.data;
|
||||
|
||||
if (!this.metric.failureTestCases) {
|
||||
this.metric.failureTestCases = [];
|
||||
}
|
||||
if (!this.metric.executeResult) {
|
||||
this.metric.executeResult = [];
|
||||
}
|
||||
if (!this.metric.moduleExecuteResult) {
|
||||
this.metric.moduleExecuteResult = [];
|
||||
}
|
||||
/*缺陷列表*/
|
||||
if (!this.metric.issues) {
|
||||
this.metric.issues = [];
|
||||
}
|
||||
|
||||
|
||||
if (this.report.startTime) {
|
||||
this.metric.startTime = new Date(this.report.startTime);
|
||||
}
|
||||
if (this.report.endTime) {
|
||||
this.metric.endTime = new Date(this.report.endTime);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleExport(name) {
|
||||
this.result.loading = true;
|
||||
this.reportExportVisible = true;
|
||||
let reset = this.exportReportReset;
|
||||
|
||||
this.$nextTick(function () {
|
||||
setTimeout(() => {
|
||||
html2canvas(document.getElementById('testCaseReportExport'), {
|
||||
scale: 2
|
||||
}).then(function(canvas) {
|
||||
exportPdf(name, [canvas]);
|
||||
reset();
|
||||
});
|
||||
}, 1000);
|
||||
});
|
||||
},
|
||||
exportReportReset() {
|
||||
this.reportExportVisible = false;
|
||||
this.result.loading = false;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>cd
|
||||
|
||||
<style scoped>
|
||||
|
||||
.el-main {
|
||||
height: calc(100vh - 70px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.head-bar {
|
||||
background: white;
|
||||
height: 45px;
|
||||
line-height: 45px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #EBEEF5;
|
||||
box-shadow: 0 0 2px 0 rgba(31, 31, 31, 0.15), 0 1px 2px 0 rgba(31, 31, 31, 0.15);
|
||||
}
|
||||
|
||||
.container {
|
||||
height: 100vh;
|
||||
/*background: #F5F5F5;*/
|
||||
}
|
||||
|
||||
.el-card {
|
||||
width: 70%;
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
.head-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1 +1 @@
|
|||
Subproject commit a22a3005d9bd254793fcf634d72539cbdf31be3a
|
||||
Subproject commit 010ad7a5f072a5e9d368c756a2473bbd20781433
|
Loading…
Reference in New Issue