feat(UI自动化): 迁移UI报告分享

--story=1009465 --user=刘瑶 【UI测试】报告支持分享功能 https://www.tapd.cn/55049933/s/1238555
This commit is contained in:
nathan.liu 2022-09-01 10:16:35 +08:00 committed by zhangdahai112
parent e3bf964a3d
commit 1595dab562
21 changed files with 239 additions and 79 deletions

View File

@ -63,6 +63,7 @@ public class ShiroUtils {
filterChainDefinitionMap.put("/sharePlanReport", "anon");
filterChainDefinitionMap.put("/sharePerformanceReport", "anon");
filterChainDefinitionMap.put("/shareApiReport", "anon");
filterChainDefinitionMap.put("/shareUiReport", "anon");
filterChainDefinitionMap.put("/system/theme", "anon");
filterChainDefinitionMap.put("/system/save/baseurl/**", "anon");

View File

@ -51,4 +51,9 @@ public class IndexController {
public String shareApiRedirect() {
return "share-api-report.html";
}
@GetMapping(value = "/shareUiReport")
public String shareUiRedirect() {
return "share-ui-report.html";
}
}

View File

@ -12,6 +12,7 @@ public class ProjectConfig {
private String trackShareReportTime;
private String performanceShareReportTime;
private String apiShareReportTime;
private String uiShareReportTime;
private Boolean caseCustomNum = false;
private Boolean scenarioCustomNum = false;
private String apiQuickMenu;
@ -25,5 +26,7 @@ public class ProjectConfig {
private String cleanApiReportExpr;
private Boolean cleanLoadReport = false;
private String cleanLoadReportExpr;
private Boolean cleanUiReport = false;
private String cleanUiReportExpr;
private Boolean urlRepeatable = false;
}

View File

@ -53,8 +53,10 @@ public class CleanUpReportJob extends MsScheduleJob {
if (BooleanUtils.isTrue(config.getCleanLoadReport())) {
this.doCleanUp(projectService::cleanUpLoadReport, config.getCleanLoadReportExpr());
}
// 定时删除 UI 调试模式生成的截图
projectService.cleanUpUiReportImg();
if (BooleanUtils.isTrue(config.getCleanUiReport())) {
// 定时删除 UI 调试模式生成的截图
this.doCleanUp(projectService::cleanUpUiReportImg, config.getCleanUiReportExpr());
}
} catch (Exception e) {
LogUtil.error("clean up report error.");
LogUtil.error(e.getMessage(), e);

View File

@ -1035,14 +1035,16 @@ public class ProjectService {
}
// 删除 UI 报告产生的截图
public void cleanUpUiReportImg() {
public void cleanUpUiReportImg(long backupTime, String projectId) {
try {
// 属于定时任务删除调试报告情况
// 获取昨天的当前时间
Date backupTime = org.apache.commons.lang3.time.DateUtils.addDays(new Date(), -1);
// Date backupTime = org.apache.commons.lang3.time.DateUtils.addDays(new Date(), -1);
// 清理类型为 UI 报告类型且时间为昨天之前的 UI 调试类型报告截图
ApiScenarioReportExample example = new ApiScenarioReportExample();
example.createCriteria().andCreateTimeLessThan(backupTime.getTime()).andReportTypeEqualTo(ReportTypeConstants.UI_INDEPENDENT.name())
example.createCriteria()
.andProjectIdEqualTo(projectId)
.andCreateTimeLessThan(backupTime).andReportTypeEqualTo(ReportTypeConstants.UI_INDEPENDENT.name())
.andExecuteTypeEqualTo(ExecuteType.Debug.name());
List<ApiScenarioReport> apiScenarioReports = apiScenarioReportMapper.selectByExample(example);
// 删除调试报告的截图

@ -1 +1 @@
Subproject commit 2ca4ff0b6d771d280eba6958345426e56d1d15a4
Subproject commit a9db2120f547fd4cd88d6fb9ba115b182a9b80eb

View File

@ -3,52 +3,59 @@
<el-row>
<el-col>
<span v-if="!debug">
<el-input v-if="nameIsEdit" size="mini" @blur="handleSave(report.name)" @keyup.enter.native="handleSaveKeyUp" style="width: 200px" v-model="report.name" maxlength="60" show-word-limit/>
<el-input v-if="nameIsEdit" size="mini" @blur="handleSave(report.name)" @keyup.enter.native="handleSaveKeyUp"
style="width: 200px" v-model="report.name" maxlength="60" show-word-limit/>
<span v-else>
<router-link v-if="isSingleScenario" :to="{name: isUi ? 'uiAutomation' : 'ApiAutomation', params: { dataSelectRange: 'edit:' + scenarioId }}">
<router-link v-if="isSingleScenario"
:to="{name: isUi ? 'uiAutomation' : 'ApiAutomation', params: { dataSelectRange: 'edit:' + scenarioId }}">
{{ report.name }}
</router-link>
<span v-else>
{{ report.name }}
</span>
<i v-if="showCancelButton" class="el-icon-edit" style="cursor:pointer" @click="nameIsEdit = true" @click.stop/>
<i v-if="showCancelButton" class="el-icon-edit" style="cursor:pointer" @click="nameIsEdit = true"
@click.stop/>
</span>
</span>
<span v-if="report.endTime || report.createTime">
<span style="margin-left: 10px">{{$t('report.test_start_time')}}</span>
<span style="margin-left: 10px">{{ $t('report.test_start_time') }}</span>
<span class="time"> {{ report.createTime | timestampFormatDate }}</span>
<span style="margin-left: 10px">{{$t('report.test_end_time')}}</span>
<span style="margin-left: 10px">{{ $t('report.test_end_time') }}</span>
<span class="time"> {{ report.endTime | timestampFormatDate }}</span>
</span>
<el-button v-if="!isPlan && (!debug || exportFlag) && !isTemplate && !isUi" v-permission="['PROJECT_API_REPORT:READ+EXPORT']" :disabled="isReadOnly" class="export-button" plain type="primary" size="mini" @click="handleExport(report.name)" style="margin-right: 10px">
{{ $t('test_track.plan_view.export_report') }}
</el-button>
<el-popover
v-if="!isPlan && (!debug || exportFlag) && !isTemplate && !isUi"
v-permission="['PROJECT_PERFORMANCE_REPORT:READ+EXPORT']"
style="margin-right: 10px;float: right;"
placement="bottom"
width="300">
<p>{{ shareUrl }}</p>
<span style="color: red;float: left;margin-left: 10px;" v-if="application.typeValue">{{
$t('commons.validity_period')+application.typeValue
}}</span>
<div style="text-align: right; margin: 0">
<el-button type="primary" size="mini" :disabled="!shareUrl"
v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}
</el-button>
</div>
<el-button slot="reference" :disabled="isReadOnly" type="danger" plain size="mini"
@click="handleShare(report)">
{{ $t('test_track.plan_view.share_report') }}
<div style="float: right">
<el-button v-if="!isPlan && (!debug || exportFlag) && !isTemplate && !isUi"
v-permission="['PROJECT_API_REPORT:READ+EXPORT']" :disabled="isReadOnly" class="export-button"
plain type="primary" size="mini" @click="handleExport(report.name)" style="margin-right: 10px">
{{ $t('test_track.plan_view.export_report') }}
</el-button>
</el-popover>
<el-button v-if="showCancelButton" class="export-button" plain size="mini" @click="returnView()" >
{{$t('commons.cancel')}}
</el-button>
<el-popover
v-if="!isPlan && (!debug || exportFlag) && !isTemplate"
v-permission="['PROJECT_PERFORMANCE_REPORT:READ+EXPORT']"
style="margin-right: 10px;float: right;"
placement="bottom"
width="300">
<p>{{ shareUrl }}</p>
<span style="color: red;float: left;margin-left: 10px;" v-if="application.typeValue">{{
$t('commons.validity_period') + application.typeValue
}}</span>
<div style="text-align: right; margin: 0">
<el-button type="primary" size="mini" :disabled="!shareUrl"
v-clipboard:copy="shareUrl">{{ $t("commons.copy") }}
</el-button>
</div>
<el-button slot="reference" :disabled="isReadOnly" type="danger" plain size="mini"
@click="handleShare(report)">
{{ $t('test_track.plan_view.share_report') }}
</el-button>
</el-popover>
<el-button v-if="showCancelButton" class="export-button" plain size="mini" @click="returnView">
{{ $t('commons.cancel') }}
</el-button>
</div>
</el-col>
</el-row>
</header>
@ -58,9 +65,13 @@
import {generateShareInfoWithExpired} from "@/network/share";
import {getCurrentProjectID} from "@/common/js/utils";
import MsTag from "@/business/components/common/components/MsTag";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const UiDownloadScreenshot = requireComponent.keys().length > 0 ? requireComponent("./ui/automation/report/UiDownloadScreenshot.vue") : {};
export default {
name: "MsApiReportViewHeader",
components: {MsTag, 'UiDownloadScreenshot': UiDownloadScreenshot.default},
props: {
report: {},
debug: Boolean,
@ -103,7 +114,7 @@ export default {
isReadOnly: false,
nameIsEdit: false,
shareUrl: "",
application:{}
application: {}
}
},
created() {
@ -132,27 +143,33 @@ export default {
let pram = {};
pram.customData = report.id;
pram.shareType = 'API_REPORT';
let thisHost = window.location.host;
let shareUrl = thisHost + "/shareApiReport";
if(this.isUi){
pram.shareType = 'UI_REPORT';
shareUrl = thisHost + "/shareUiReport";
}
generateShareInfoWithExpired(pram, (data) => {
let thisHost = window.location.host;
this.shareUrl = thisHost + "/shareApiReport" + data.shareUrl;
this.shareUrl = shareUrl + data.shareUrl;
});
},
getProjectApplication(){
this.$get('/project_application/get/' + getCurrentProjectID()+"/API_SHARE_REPORT_TIME", res => {
if(res.data){
getProjectApplication() {
let path = "/API_SHARE_REPORT_TIME";
if(this.isUi){
path = "/UI_SHARE_REPORT_TIME";
}
this.$get('/project_application/get/' + getCurrentProjectID() + path, res => {
if (res.data && res.data.typeValue) {
let quantity = res.data.typeValue.substring(0, res.data.typeValue.length - 1);
let unit = res.data.typeValue.substring(res.data.typeValue.length - 1);
if(unit==='H'){
res.data.typeValue = quantity+this.$t('commons.date_unit.hour');
}else
if(unit==='D'){
res.data.typeValue = quantity+this.$t('commons.date_unit.day');
}else
if(unit==='M'){
res.data.typeValue = quantity+this.$t('commons.workspace_unit')+this.$t('commons.date_unit.month');
}else
if(unit==='Y'){
res.data.typeValue = quantity+this.$t('commons.date_unit.year');
if (unit === 'H') {
res.data.typeValue = quantity + this.$t('commons.date_unit.hour');
} else if (unit === 'D') {
res.data.typeValue = quantity + this.$t('commons.date_unit.day');
} else if (unit === 'M') {
res.data.typeValue = quantity + this.$t('commons.workspace_unit') + this.$t('commons.date_unit.month');
} else if (unit === 'Y') {
res.data.typeValue = quantity + this.$t('commons.date_unit.year');
}
this.application = res.data;
}

View File

@ -22,7 +22,7 @@ import MsAsideContainer from "../../../../common/components/MsAsideContainer";
import MsMainContainer from "../../../../common/components/MsMainContainer";
import MsAsideItem from "../../../../common/components/MsAsideItem";
import EnvironmentEdit from "./EnvironmentEdit";
import {hasPermission, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import {getUUID, hasPermission, listenGoBack, removeGoBackListener} from "@/common/js/utils";
import {Environment, parseEnvironment} from "../../model/EnvironmentModel";
export default {

View File

@ -24,7 +24,7 @@ import MsAsideContainer from "../../../common/components/MsAsideContainer";
import MsMainContainer from "../../../common/components/MsMainContainer";
import MsAsideItem from "../../../common/components/MsAsideItem";
import EnvironmentEdit from "./environment/EnvironmentEdit";
import {hasPermission, listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
import {getUUID, hasPermission, listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
import {Environment, parseEnvironment} from "../model/EnvironmentModel";
import MsDialogHeader from "@/business/components/common/components/MsDialogHeader";

View File

@ -132,7 +132,7 @@ import EnvironmentEdit from "@/business/components/api/test/components/environme
import MsAsideItem from "@/business/components/common/components/MsAsideItem";
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
import ProjectSwitch from "@/business/components/common/head/ProjectSwitch";
import {downloadFile, getCurrentProjectID} from "@/common/js/utils";
import {downloadFile, getCurrentProjectID, getUUID} from "@/common/js/utils";
import EnvironmentImport from "@/business/components/project/menu/EnvironmentImport";
import MsMainContainer from "@/business/components/common/components/MsMainContainer";
import MsContainer from "@/business/components/common/components/MsContainer";

View File

@ -105,24 +105,45 @@
<!-- UI 测试 -->
<el-tab-pane :label="$t('commons.ui_test')" name="ui_test">
<el-row style="margin-top: 10px">
<span style="font-weight:bold">{{ $t('commons.view_settings') }}</span>
</el-row>
<el-row style="margin-top: 15px">
<app-manage-item :title="$t('ui.ui_debug_mode')"
:append-span="12" :prepend-span="12" :middle-span="0">
<template #append>
<el-radio-group v-model="config.uiQuickMenu" @change="switchChange('UI_QUICK_MENU', $event)">
<el-radio label="local" value="local" :disabled="!isXpack">
{{ $t('ui.ui_local_debug') }}
</el-radio>
<el-radio label="server" value="server" :disabled="!isXpack">
{{ $t('ui.ui_server_debug') }}
</el-radio>
</el-radio-group>
</template>
</app-manage-item>
</el-row>
<!-- 启用设置 -->
<el-col :span="8" class="commons-api-enable">
<el-row style="margin-top: 10px">
<span style="font-weight:bold">{{ this.$t('commons.enable_settings') }}</span>
</el-row>
<el-row style="margin-top: 15px">
<timing-item ref="uiTimingItem" :choose.sync="config.cleanUiReport"
:expr.sync="config.cleanUiReportExpr"
@chooseChange="switchChange('CLEAN_UI_REPORT', config.cleanUiReport, ['CLEAN_UI_REPORT_EXPR', config.cleanUiReportExpr])"
:title="$t('project.timing_clean_ui_report')"/>
<timing-item ref="uiTimingItem" :choose.sync="config.shareReport"
:expr.sync="config.uiShareReportTime" :share-link="true"
:unit-options="applyUnitOptions"
@chooseChange="switchChange('UI_SHARE_REPORT_TIME', config.uiShareReportTime)"
:title="$t('report.report_sharing_link')"/>
</el-row>
</el-col>
<!-- 显示设置 -->
<el-col :span="8" class="commons-view-setting">
<el-row style="margin-top: 10px">
<span style="font-weight:bold">{{ $t('commons.view_settings') }}</span>
</el-row>
<el-row style="margin-top: 15px">
<app-manage-item :title="$t('ui.ui_debug_mode')"
:append-span="12" :prepend-span="12" :middle-span="0">
<template #append>
<el-radio-group v-model="config.uiQuickMenu" @change="switchChange('UI_QUICK_MENU', $event)">
<el-radio label="local" value="local" :disabled="!isXpack">
{{ $t('ui.ui_local_debug') }}
</el-radio>
<el-radio label="server" value="server" :disabled="!isXpack">
{{ $t('ui.ui_server_debug') }}
</el-radio>
</el-radio-group>
</template>
</app-manage-item>
</el-row>
</el-col>
</el-tab-pane>
<el-tab-pane :label="$t('commons.performance')" name="performance">
@ -176,7 +197,9 @@ export default {
cleanApiReport: false,
cleanApiReportExpr: "",
cleanLoadReport: false,
cleanLoadReportExpr: ""
cleanLoadReportExpr: "",
cleanUiReport: false,
cleanUiReportExpr: ""
},
count: 0,
isXpack: false,
@ -193,6 +216,7 @@ export default {
config: {
trackShareReportTime: "",
performanceShareReportTime: "",
uiShareReportTime: "",
apiShareReportTime: "",
caseCustomNum: false,
scenarioCustomNum: false,
@ -207,6 +231,8 @@ export default {
cleanApiReportExpr: "",
cleanLoadReport: false,
cleanLoadReportExpr: "",
cleanUiReport: false,
cleanUiReportExpr: "",
urlRepeatable: false,
shareReport: true
}

View File

@ -145,7 +145,7 @@ import EnvironmentEdit from "@/business/components/api/test/components/environme
import MsAsideItem from "@/business/components/common/components/MsAsideItem";
import MsAsideContainer from "@/business/components/common/components/MsAsideContainer";
import ProjectSwitch from "@/business/components/common/head/ProjectSwitch";
import {downloadFile, strMapToObj} from "@/common/js/utils";
import {downloadFile, getUUID, strMapToObj} from "@/common/js/utils";
import EnvironmentImport from "@/business/components/project/menu/EnvironmentImport";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import {_handleSelect, _handleSelectAll, getSelectDataCounts, setUnSelectIds} from "@/common/js/tableUtils";

@ -1 +1 @@
Subproject commit be7591f7f5077a77ee5abce16461af30f3ac8666
Subproject commit c698d5eac1182ad0c51f4ababdcc16c4b713416b

View File

@ -827,6 +827,7 @@ export default {
timing_clean_plan_report: "Regularly clean up test report",
timing_clean_api_report: "Regularly clean up api report",
timing_clean_load_report: "Regularly clean up performance report",
timing_clean_ui_report: "Regularly clean up ui report",
keep_recent: "Keep recent",
please_select_cleaning_time: "please select cleaning time"
},

View File

@ -831,6 +831,7 @@ export default {
timing_clean_plan_report: "定时清理测试计划报告",
timing_clean_api_report: "定时清理接口测试报告",
timing_clean_load_report: "定时清理性能测试报告",
timing_clean_ui_report: "定时清理UI测试报告",
keep_recent: "保留最近",
please_select_cleaning_time: "请选择清理时间!"
},

View File

@ -831,6 +831,7 @@ export default {
timing_clean_plan_report: "定時清理測試計劃報告",
timing_clean_api_report: "定時清理接口測試報告",
timing_clean_load_report: "定時清理性能測試報告",
timing_clean_ui_report: "定時清理UI測試報告",
keep_recent: "保留最近",
please_select_cleaning_time: "請選擇清理時間!"
},

View File

@ -0,0 +1,36 @@
<template>
<UiShareReportDetail :report-id="reportId" :share-id="shareId" :is-share="isShare" :is-plan="true" :show-cancel-button="false" ></UiShareReportDetail>
</template>
<script>
import {getShareId} from "@/common/js/utils";
import {getShareInfo} from "@/network/share";
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
const UiShareReportDetail = requireComponent.keys().length > 0 ? requireComponent("./ui/automation/report/UiShareReportDetail.vue") : {};
export default {
name: "ShareUiReportTemplate",
components: {'UiShareReportDetail' : UiShareReportDetail.default},
data() {
return {
reportId: '',
shareId: '',
isShare: true,
};
},
created() {
this.shareId = getShareId();
getShareInfo(this.shareId, (data) => {
if (!data) {
this.$error('报告已删除!');
return;
}
if (data.shareType === 'UI_REPORT') {
this.reportId = data.customData;
}
});
},
};
</script>
<style scoped>
</style>

View File

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="shortcut icon" href="<%= BASE_URL %>favicon.ico">
<title>Ui Report</title>
</head>
<body>
<div id="shareUiReport"></div>
</body>
</html>

View File

@ -0,0 +1,4 @@
import ShareUiReportTemplate from "@/template/report/ui/share/ShareUiReportTemplate";
import uiReportUse from "@/template/report/ui/uiReportUse";
uiReportUse('#shareUiReport', ShareUiReportTemplate);

View File

@ -0,0 +1,43 @@
import Vue from 'vue';
import ElementUI, {Button, Card, Col, Form, FormItem, Input, Main, Popover, Row, Table, TableColumn} from 'element-ui';
import '@/assets/theme/index.css';
import '@/common/css/menu-header.css';
import '@/common/css/main.css';
import i18n from "@/i18n/i18n";
import chart from "@/common/js/chart";
import filters from "@/common/js/filter";
import icon from "@/common/js/icon";
import message from "@/common/js/message";
import ajax from "@/common/js/ajax";
function uiReportUse(id, template) {
Vue.use(ElementUI, {
i18n: (key, value) => i18n.t(key, value)
});
Vue.use(Row);
Vue.use(Col);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
Vue.use(Button);
Vue.use(chart);
Vue.use(Main);
Vue.use(Card);
Vue.use(TableColumn);
Vue.use(Table);
Vue.use(filters);
Vue.use(icon);
Vue.use(message);
Vue.use(ajax);
Vue.use(Popover);
new Vue({
el: id,
i18n,
render: h => h(template)
});
}
export default uiReportUse;

View File

@ -49,6 +49,11 @@ module.exports = {
template: "src/template/report/api/share/share-api-report.html",
filename: "share-api-report.html",
},
shareUiReport: {
entry: "src/template/report/ui/share/share-ui-report.js",
template: "src/template/report/ui/share/share-ui-report.html",
filename: "share-ui-report.html",
},
enterpriseReport: {
entry: "src/template/enterprise/share/share-enterprise-report.js",
template: "src/template/enterprise/share/share-enterprise-report.html",