This commit is contained in:
chenjianxing 2020-08-28 19:24:39 +08:00
commit fb93109cf6
13 changed files with 447 additions and 412 deletions

View File

@ -94,6 +94,10 @@ public class APITestController {
public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) { public String runDebug(@RequestPart("request") SaveAPITestRequest request, @RequestPart(value = "file") MultipartFile file, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
return apiTestService.runDebug(request, file, bodyFiles); return apiTestService.runDebug(request, file, bodyFiles);
} }
@PostMapping(value = "/checkName")
public void checkName(@RequestBody SaveAPITestRequest request) {
apiTestService.checkName(request);
}
@PostMapping(value = "/import", consumes = {"multipart/form-data"}) @PostMapping(value = "/import", consumes = {"multipart/form-data"})
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR) @RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)

View File

@ -245,6 +245,13 @@ public class APITestService {
MSException.throwException(Translator.get("load_test_already_exists")); MSException.throwException(Translator.get("load_test_already_exists"));
} }
} }
public void checkName(SaveAPITestRequest request) {
ApiTestExample example = new ApiTestExample();
example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId());
if (apiTestMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("load_test_already_exists"));
}
}
private ApiTest updateTest(SaveAPITestRequest request) { private ApiTest updateTest(SaveAPITestRequest request) {
checkNameExist(request); checkNameExist(request);

View File

@ -1,8 +1,11 @@
package io.metersphere.excel.utils; package io.metersphere.excel.utils;
import com.alibaba.excel.EasyExcel; import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
import io.metersphere.exception.ExcelException; import io.metersphere.exception.ExcelException;
import org.apache.poi.ss.usermodel.IndexedColors;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
@ -29,9 +32,12 @@ public class EasyExcelExporter {
public void export(HttpServletResponse response, List data, String fileName, String sheetName) { public void export(HttpServletResponse response, List data, String fileName, String sheetName) {
response.setContentType("application/vnd.ms-excel"); response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8"); response.setCharacterEncoding("utf-8");
WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
contentWriteCellStyle.setWrapped(true);
try { try {
HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(null, contentWriteCellStyle);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx"); response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
EasyExcel.write(response.getOutputStream(), this.clazz).sheet(sheetName).doWrite(data); EasyExcel.write(response.getOutputStream(), this.clazz).registerWriteHandler(horizontalCellStyleStrategy).sheet(sheetName).doWrite(data);
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
LogUtil.error(e.getMessage(), e); LogUtil.error(e.getMessage(), e);
throw new ExcelException("Utf-8 encoding is not supported"); throw new ExcelException("Utf-8 encoding is not supported");

View File

@ -224,6 +224,10 @@ public class IssuesService {
String account = object.getString("account"); String account = object.getString("account");
String password = object.getString("password"); String password = object.getString("password");
String url = object.getString("url"); String url = object.getString("url");
String issuetype = object.getString("issuetype");
if (StringUtils.isBlank(issuetype)) {
MSException.throwException("Jira 问题类型为空");
}
String auth = EncryptUtils.base64Encoding(account + ":" + password); String auth = EncryptUtils.base64Encoding(account + ":" + password);
String testCaseId = issuesRequest.getTestCaseId(); String testCaseId = issuesRequest.getTestCaseId();
@ -252,8 +256,7 @@ public class IssuesService {
" \"summary\":\"" + issuesRequest.getTitle() + "\",\n" + " \"summary\":\"" + issuesRequest.getTitle() + "\",\n" +
" \"description\": " + JSON.toJSONString(desc) + ",\n" + " \"description\": " + JSON.toJSONString(desc) + ",\n" +
" \"issuetype\":{\n" + " \"issuetype\":{\n" +
" \"id\":\"10009\",\n" + " \"name\":\"" + issuetype + "\"\n" +
" \"name\":\"Defect\"\n" +
" }\n" + " }\n" +
" }\n" + " }\n" +
"}"; "}";

View File

@ -11,12 +11,13 @@
</template> </template>
<one-click-operation ref="OneClickOperation" :select-ids="selectIds" :select-names="selectNames" <one-click-operation ref="OneClickOperation" :select-ids="selectIds"
:select-project-names="selectProjectNames" @refresh="init()"></one-click-operation> :select-project-names="selectProjectNames" :select-project-id="selectProjectId"
@refresh="init()"></one-click-operation>
<el-table border :data="tableData" class="adjust-table table-content" @sort-change="sort" <el-table border :data="tableData" class="adjust-table table-content" @sort-change="sort"
@row-click="handleView" @row-click="handleView"
@filter-change="filter" @select-all="handleSelectAll" @select="selectionChange"> @filter-change="filter" @select-all="select" @select="select">
<el-table-column <el-table-column
type="selection"></el-table-column> type="selection"></el-table-column>
<el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip> <el-table-column prop="name" :label="$t('commons.name')" width="250" show-overflow-tooltip>
@ -59,175 +60,164 @@
</template> </template>
<script> <script>
import OneClickOperation from './OneClickOperation'; import OneClickOperation from './OneClickOperation';
import MsTablePagination from "../../common/pagination/TablePagination"; import MsTablePagination from "../../common/pagination/TablePagination";
import MsTableHeader from "../../common/components/MsTableHeader"; import MsTableHeader from "../../common/components/MsTableHeader";
import MsTableOperator from "../../common/components/MsTableOperator"; import MsTableOperator from "../../common/components/MsTableOperator";
import MsContainer from "../../common/components/MsContainer"; import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer"; import MsMainContainer from "../../common/components/MsMainContainer";
import MsApiTestStatus from "./ApiTestStatus"; import MsApiTestStatus from "./ApiTestStatus";
import MsTableOperators from "../../common/components/MsTableOperators"; import MsTableOperators from "../../common/components/MsTableOperators";
import {_filter, _sort} from "@/common/js/utils"; import {_filter, _sort} from "@/common/js/utils";
import {TEST_CONFIGS} from "../../common/components/search/search-components"; import {TEST_CONFIGS} from "../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent"; import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import ApiCopyDialog from "./components/ApiCopyDialog"; import ApiCopyDialog from "./components/ApiCopyDialog";
export default { export default {
components: { components: {
ApiCopyDialog, ApiCopyDialog,
OneClickOperation, OneClickOperation,
MsTableOperators, MsTableOperators,
MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator
}, },
data() { data() {
return { return {
result: {}, result: {},
condition: { condition: {
components: TEST_CONFIGS components: TEST_CONFIGS
}, },
projectId: null, projectId: null,
tableData: [], tableData: [],
multipleSelection: [], multipleSelection: [],
currentPage: 1, currentPage: 1,
pageSize: 5, pageSize: 5,
total: 0, total: 0,
loading: false, loading: false,
selectIds: new Set(), selectIds: new Set(),
selectNames: new Set(), selectProjectNames: new Set(),
selectProjectNames: new Set(), selectProjectId: new Set(),
buttons: [ buttons: [
{ {
tip: this.$t('commons.edit'), icon: "el-icon-edit", tip: this.$t('commons.edit'), icon: "el-icon-edit",
exec: this.handleEdit exec: this.handleEdit
}, { }, {
tip: this.$t('commons.copy'), icon: "el-icon-copy-document", type: "success", tip: this.$t('commons.copy'), icon: "el-icon-copy-document", type: "success",
exec: this.handleCopy exec: this.handleCopy
}, { }, {
tip: this.$t('commons.delete'), icon: "el-icon-delete", type: "danger", tip: this.$t('commons.delete'), icon: "el-icon-delete", type: "danger",
exec: this.handleDelete exec: this.handleDelete
} }
], ],
statusFilters: [ statusFilters: [
{text: 'Saved', value: 'Saved'}, {text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'}, {text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'}, {text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'}, {text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'}, {text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'} {text: 'Error', value: 'Error'}
] ]
} }
},
watch: {
'$route': 'init'
},
methods: {
create() {
this.$router.push('/api/test/create');
}, },
handleSelectAll(selection) { watch: {
if (selection.length > 0) { '$route': 'init'
this.tableData.forEach(item => { },
this.selectIds.add(item.id);
this.selectProjectNames.add(item.projectName) methods: {
}); create() {
} else { this.$router.push('/api/test/create');
},
select(selection) {
this.selectIds.clear() this.selectIds.clear()
this.selectProjectNames.clear() this.selectProjectNames.clear()
} this.selectProjectId.clear()
}, selection.forEach(s => {
selectionChange(selection, row) { this.selectIds.add(s.id)
if (this.selectIds.has(row.id)) { this.selectProjectNames.add(s.projectName)
this.selectIds.delete(row.id); this.selectProjectId.add(s.projectId)
this.selectProjectNames.delete(row.projectName)
} else {
this.selectIds.add(row.id);
this.selectProjectNames.add(row.projectName)
}
},
runTest() {
if (this.selectIds.size < 1) {
this.$warning(this.$t('test_track.plan_view.select_manipulate'));
} else {
this.$refs.OneClickOperation.openOneClickOperation();
}
},
search() {
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
}
let url = "/api/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.tableData.forEach(item => {
this.selectNames.add(item.name)
}) })
}); },
}, runTest() {
handleSelectionChange(val) { if (this.selectIds.size < 1) {
this.multipleSelection = val; this.$warning(this.$t('test_track.plan_view.select_manipulate'));
}, } else {
handleEdit(test) { this.$refs.OneClickOperation.openOneClickOperation();
this.$router.push({
path: '/api/test/edit?id=' + test.id,
})
},
handleView(test) {
this.$router.push({
path: '/api/test/view?id=' + test.id,
})
},
handleDelete(test) {
this.$alert(this.$t('load_test.delete_confirm') + test.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.result = this.$post("/api/delete", {id: test.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
} }
}); },
search() {
if (this.projectId !== 'all') {
this.condition.projectId = this.projectId;
}
let url = "/api/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleEdit(test) {
this.$router.push({
path: '/api/test/edit?id=' + test.id,
})
},
handleView(test) {
this.$router.push({
path: '/api/test/view?id=' + test.id,
})
},
handleDelete(test) {
this.$alert(this.$t('load_test.delete_confirm') + test.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.result = this.$post("/api/delete", {id: test.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
}
});
},
handleCopy(test) {
this.$refs.apiCopy.open(test);
},
init() {
this.selectIds.clear()
this.selectProjectNames.clear()
this.selectIds.clear()
this.projectId = this.$route.params.projectId;
if (this.projectId && this.projectId !== "all") {
this.$store.commit('setProjectId', this.projectId);
}
this.search();
},
sort(column) {
_sort(column, this.condition);
this.init();
},
filter(filters) {
_filter(filters, this.condition);
this.init();
},
}, },
handleCopy(test) { created() {
this.$refs.apiCopy.open(test);
},
init() {
this.projectId = this.$route.params.projectId;
if (this.projectId && this.projectId !== "all") {
this.$store.commit('setProjectId', this.projectId);
}
this.search();
},
sort(column) {
_sort(column, this.condition);
this.init(); this.init();
}, }
filter(filters) {
_filter(filters, this.condition);
this.init();
},
},
created() {
this.init();
}
} }
</script> </script>
<style scoped> <style scoped>
.table-content { .table-content {
width: 100%; width: 100%;
} }
.el-table { .el-table {
cursor: pointer; cursor: pointer;
} }
</style> </style>

View File

@ -26,7 +26,7 @@
import MsApiScenarioConfig from "./components/ApiScenarioConfig"; import MsApiScenarioConfig from "./components/ApiScenarioConfig";
import MsApiReportStatus from "../report/ApiReportStatus"; import MsApiReportStatus from "../report/ApiReportStatus";
import MsApiReportDialog from "./ApiReportDialog"; import MsApiReportDialog from "./ApiReportDialog";
import {getUUID} from "../../../../common/js/utils"; import {getUUID} from "@/common/js/utils";
export default { export default {
@ -41,6 +41,7 @@
tests: [], tests: [],
ruleForm: {}, ruleForm: {},
change: false, change: false,
projectId: "",
rule: { rule: {
testName: [ testName: [
{required: true, message: this.$t('api_test.input_name'), trigger: 'blur'}, {required: true, message: this.$t('api_test.input_name'), trigger: 'blur'},
@ -61,10 +62,10 @@
selectIds: { selectIds: {
type: Set type: Set
}, },
selectNames: { selectProjectNames: {
type: Set type: Set
}, },
selectProjectNames: { selectProjectId: {
type: Set type: Set
} }
}, },
@ -73,26 +74,33 @@
this.oneClickOperationVisible = true; this.oneClickOperationVisible = true;
}, },
checkedSaveAndRunTest() { checkedSaveAndRunTest() {
if (this.selectNames.has(this.ruleForm.testName)) { if (this.ruleForm.testName) {
this.selectIds.clear()
this.$warning(this.$t('load_test.already_exists'));
this.oneClickOperationVisible = false;
this.$emit('refresh')
} else {
if (this.selectProjectNames.size > 1) { if (this.selectProjectNames.size > 1) {
this.selectIds.clear()
this.$warning(this.$t('load_test.same_project_test')); this.$warning(this.$t('load_test.same_project_test'));
this.oneClickOperationVisible = false; this.oneClickOperationVisible = false;
this.$emit('refresh') this.$emit('refresh')
} else { } else {
for (let x of this.selectIds) { this.checkNameResult(this.ruleForm.testName)
this.getTest(x)
}
} }
} else {
this.$warning(this.$t('api_test.input_name'))
}
},
checkNameResult() {
this.checkName(() => {
for (let x of this.selectIds) {
this.getTest(x)
}
})
},
checkName(callback) {
for (let i of this.selectProjectId) {
this.result = this.$post('/api/checkName', {name: this.ruleForm.testName, projectId: i}, () => {
if (callback) callback();
})
} }
}, },
_getEnvironmentAndRunTest: function (item) { _getEnvironmentAndRunTest: function (item) {
let count = 0;
this.result = this.$get('/api/environment/list/' + item.projectId, response => { this.result = this.$get('/api/environment/list/' + item.projectId, response => {
let environments = response.data; let environments = response.data;
let environmentMap = new Map(); let environmentMap = new Map();

View File

@ -22,9 +22,9 @@
@click="rerun(testId)"> @click="rerun(testId)">
{{ $t('report.test_execute_again') }} {{ $t('report.test_execute_again') }}
</el-button> </el-button>
<!-- <el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)"> <!-- <el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)">
{{$t('report.export')}} {{$t('report.export')}}
</el-button>--> </el-button>-->
<!-- <!--
<el-button :disabled="isReadOnly" type="warning" plain size="mini"> <el-button :disabled="isReadOnly" type="warning" plain size="mini">
{{$t('report.compare')}} {{$t('report.compare')}}
@ -82,252 +82,244 @@
</template> </template>
<script> <script>
import MsReportErrorLog from './components/ErrorLog'; import MsReportErrorLog from './components/ErrorLog';
import MsReportLogDetails from './components/LogDetails'; import MsReportLogDetails from './components/LogDetails';
import MsReportRequestStatistics from './components/RequestStatistics'; import MsReportRequestStatistics from './components/RequestStatistics';
import MsReportTestOverview from './components/TestOverview'; import MsReportTestOverview from './components/TestOverview';
import MsPerformancePressureConfig from "./components/PerformancePressureConfig"; import MsPerformancePressureConfig from "./components/PerformancePressureConfig";
import MsContainer from "../../common/components/MsContainer"; import MsContainer from "../../common/components/MsContainer";
import MsMainContainer from "../../common/components/MsMainContainer"; import MsMainContainer from "../../common/components/MsMainContainer";
import {checkoutTestManagerOrTestUser} from "@/common/js/utils"; import {checkoutTestManagerOrTestUser} from "@/common/js/utils";
import writer from "file-writer";
import ResumeCss from "../../../../common/css/main.css";
export default { export default {
name: "PerformanceReportView", name: "PerformanceReportView",
components: { components: {
MsReportErrorLog, MsReportErrorLog,
MsReportLogDetails, MsReportLogDetails,
MsReportRequestStatistics, MsReportRequestStatistics,
MsReportTestOverview, MsReportTestOverview,
MsContainer, MsContainer,
MsMainContainer, MsMainContainer,
MsPerformancePressureConfig MsPerformancePressureConfig
}, },
data() { data() {
return { return {
result: {}, result: {},
active: '1', active: '1',
reportId: '', reportId: '',
status: '', status: '',
reportName: '', reportName: '',
testId: '', testId: '',
testName: '', testName: '',
projectId: '', projectId: '',
projectName: '', projectName: '',
startTime: '0', startTime: '0',
endTime: '0', endTime: '0',
minutes: '0', minutes: '0',
seconds: '0', seconds: '0',
title: 'Logging', title: 'Logging',
report: {}, report: {},
isReadOnly: false, isReadOnly: false,
websocket: null, websocket: null,
dialogFormVisible: false, dialogFormVisible: false,
testPlan: {testResourcePoolId: null} testPlan: {testResourcePoolId: null}
}
},
methods: {
initBreadcrumb() {
if (this.reportId) {
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => {
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;
}
})
} }
}, },
methods: { initReportTimeInfo() {
initBreadcrumb() { if (this.status === 'Starting') {
if (this.reportId) { this.clearData();
this.result = this.$get("/performance/report/test/pro/info/" + this.reportId, res => { return;
let data = res.data; }
if (this.reportId) {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
.then(res => {
let data = res.data.data;
if (data) { if (data) {
this.reportName = data.name; this.startTime = data.startTime;
this.testId = data.testId; this.endTime = data.endTime;
this.testName = data.testName; let duration = data.duration;
this.projectId = data.projectId; this.minutes = Math.floor(duration / 60);
this.projectName = data.projectName; this.seconds = duration % 60;
} }
}) }).catch(() => {
} this.clearData();
}, });
initReportTimeInfo() {
if (this.reportId) {
this.result = this.$get("/performance/report/content/report_time/" + this.reportId)
.then(res => {
let data = res.data.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;
}
}).catch(() => {
this.clearData();
})
}
},
initWebSocket() {
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'));
break;
case 'Starting':
this.$alert(this.$t('report.start_status'));
break;
case 'Reporting':
case 'Running':
case 'Completed':
default:
break;
}
},
clearData() {
this.startTime = '0';
this.endTime = '0';
this.minutes = '0';
this.seconds = '0';
},
stopTest(forceStop) {
this.result = 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;
},
rerun(testId) {
this.$confirm(this.$t('report.test_rerun_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => {
this.reportId = response.data;
this.$router.push({path: '/performance/report/view/' + this.reportId});
// socket
this.initWebSocket();
})
}).catch(() => {
});
},
onOpen() {
window.console.log("socket opening.");
},
onError(e) {
window.console.error(e)
},
onMessage(e) {
this.$set(this.report, "refresh", e.data); //
this.$set(this.report, "status", 'Running');
this.initReportTimeInfo();
window.console.log('receive a message:', e.data);
},
onClose(e) {
this.$set(this.report, "refresh", Math.random()); //
this.$set(this.report, "status", 'Completed');
this.initReportTimeInfo();
window.console.log("socket closed.");
} }
}, },
created() { initWebSocket() {
this.isReadOnly = false; let protocol = "ws://";
if (!checkoutTestManagerOrTestUser()) { if (window.location.protocol === 'https:') {
this.isReadOnly = true; protocol = "wss://";
} }
this.reportId = this.$route.path.split('/')[4]; const uri = protocol + window.location.host + "/performance/report/" + this.reportId;
this.result = this.$get("/performance/report/" + this.reportId, res => { this.websocket = new WebSocket(uri);
let data = res.data; this.websocket.onmessage = this.onMessage;
if (data) { this.websocket.onopen = this.onOpen;
this.status = data.status; this.websocket.onerror = this.onError;
this.$set(this.report, "id", this.reportId); this.websocket.onclose = this.onClose;
this.$set(this.report, "status", data.status); },
this.$set(this.report, "testId", data.testId); checkReportStatus(status) {
this.$set(this.report, "loadConfiguration", data.loadConfiguration); switch (status) {
this.checkReportStatus(data.status); case 'Error':
if (this.status === "Completed" || this.status === "Running") { this.$warning(this.$t('report.generation_error'));
this.initReportTimeInfo(); break;
} case 'Starting':
this.initBreadcrumb(); this.$alert(this.$t('report.start_status'));
this.initWebSocket(); break;
case 'Reporting':
case 'Running':
case 'Completed':
default:
break;
}
},
clearData() {
this.startTime = '0';
this.endTime = '0';
this.minutes = '0';
this.seconds = '0';
},
stopTest(forceStop) {
this.result = this.$get('/performance/stop/' + this.reportId + '/' + forceStop, () => {
this.$success(this.$t('report.test_stop_success'));
if (forceStop) {
this.$router.push('/performance/report/all');
} else { } else {
this.$error(this.$t('report.not_exist')) this.report.status = 'Completed';
} }
}); });
this.dialogFormVisible = false;
}, },
beforeDestroy() { rerun(testId) {
this.websocket.close() //websocket this.$confirm(this.$t('report.test_rerun_confirm'), '', {
confirmButtonText: this.$t('commons.confirm'),
cancelButtonText: this.$t('commons.cancel'),
type: 'warning'
}).then(() => {
this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => {
this.reportId = response.data;
this.$router.push({path: '/performance/report/view/' + this.reportId});
// socket
this.initWebSocket();
})
}).catch(() => {
});
}, },
watch: { onOpen() {
'$route'(to) { window.console.log("socket opening.");
if (to.name === "perReportView") { },
this.isReadOnly = false; onError(e) {
if (!checkoutTestManagerOrTestUser()) { window.console.error(e)
this.isReadOnly = true; },
} onMessage(e) {
let reportId = to.path.split('/')[4]; this.$set(this.report, "refresh", e.data); //
this.reportId = reportId; this.$set(this.report, "status", 'Running');
if (reportId) { this.status = 'Running';
this.$get("/performance/report/test/pro/info/" + reportId, response => { this.initReportTimeInfo();
let data = response.data; window.console.log('receive a message:', e.data);
if (data) { },
this.status = data.status; onClose(e) {
this.reportName = data.name; if (e.code === 1005) {
this.testName = data.testName; // socketreport
this.testId = data.testId; return;
this.projectName = data.projectName; }
this.$set(this.report, "refresh", Math.random()); //
this.$set(this.report, "id", reportId); this.$set(this.report, "status", 'Completed');
this.$set(this.report, "status", data.status); this.initReportTimeInfo();
window.console.log("socket closed.");
this.checkReportStatus(data.status); }
if (this.status === "Completed") { },
this.result = this.$get("/performance/report/content/report_time/" + this.reportId).then(res => { created() {
let data = res.data.data; this.isReadOnly = false;
if (data) { if (!checkoutTestManagerOrTestUser()) {
this.startTime = data.startTime; this.isReadOnly = true;
this.endTime = data.endTime; }
let duration = data.duration; this.reportId = this.$route.path.split('/')[4];
this.minutes = Math.floor(duration / 60); this.result = this.$get("/performance/report/" + this.reportId, res => {
this.seconds = duration % 60; let data = res.data;
} if (data) {
}).catch(() => { this.status = data.status;
this.clearData(); this.$set(this.report, "id", this.reportId);
}) this.$set(this.report, "status", data.status);
} else { this.$set(this.report, "testId", data.testId);
this.clearData(); this.$set(this.report, "loadConfiguration", data.loadConfiguration);
} this.checkReportStatus(data.status);
} else { if (this.status === "Completed" || this.status === "Running") {
this.$error(this.$t('report.not_exist')); this.initReportTimeInfo();
}
});
}
} }
this.initBreadcrumb();
this.initWebSocket();
} else {
this.$error(this.$t('report.not_exist'))
}
});
},
watch: {
'$route'(to) {
if (to.name === "perReportView") {
this.isReadOnly = false;
if (!checkoutTestManagerOrTestUser()) {
this.isReadOnly = true;
}
let reportId = to.path.split('/')[4];
this.reportId = reportId;
if (reportId) {
this.$get("/performance/report/test/pro/info/" + reportId, response => {
let data = response.data;
if (data) {
this.status = data.status;
this.reportName = data.name;
this.testName = data.testName;
this.testId = data.testId;
this.projectName = data.projectName;
this.$set(this.report, "id", reportId);
this.$set(this.report, "status", data.status);
this.checkReportStatus(data.status);
this.initReportTimeInfo();
} else {
this.$error(this.$t('report.not_exist'));
}
});
}
} else {
console.log("close socket.");
this.websocket.close() //websocket
} }
} }
} }
}
</script> </script>
<style scoped> <style scoped>
.ms-report-view-btns { .ms-report-view-btns {
margin-top: 15px; margin-top: 15px;
} }
.ms-report-time-desc { .ms-report-time-desc {
text-align: left; text-align: left;
display: block; display: block;
color: #5C7878; color: #5C7878;
} }
</style> </style>

View File

@ -164,6 +164,7 @@ export default {
} else { } else {
this.calculateChart(); this.calculateChart();
} }
this.getResourcePools();
}, },
}, },
methods: { methods: {

View File

@ -25,6 +25,9 @@
<el-form-item :label="$t('organization.integration.jira_url')" prop="url" v-if="platform === 'Jira'"> <el-form-item :label="$t('organization.integration.jira_url')" prop="url" v-if="platform === 'Jira'">
<el-input v-model="form.url" :placeholder="$t('organization.integration.input_jira_url')"/> <el-input v-model="form.url" :placeholder="$t('organization.integration.input_jira_url')"/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('organization.integration.jira_issuetype')" prop="issuetype" v-if="platform === 'Jira'">
<el-input v-model="form.issuetype" :placeholder="$t('organization.integration.input_jira_issuetype')"/>
</el-form-item>
</el-form> </el-form>
</div> </div>
@ -43,10 +46,13 @@
<div class="defect-tip"> <div class="defect-tip">
<div>{{$t('organization.integration.use_tip')}}</div> <div>{{$t('organization.integration.use_tip')}}</div>
<div> <div>
1. {{$t('organization.integration.use_tip_one')}} 1. {{$t('organization.integration.use_tip_tapd')}}
</div> </div>
<div> <div>
2. {{$t('organization.integration.use_tip_two')}} 2. {{$t('organization.integration.use_tip_jira')}}
</div>
<div>
3. {{$t('organization.integration.use_tip_two')}}
<router-link to="/track/project/all" style="margin-left: 5px"> <router-link to="/track/project/all" style="margin-left: 5px">
{{$t('organization.integration.link_the_project_now')}} {{$t('organization.integration.link_the_project_now')}}
</router-link> </router-link>
@ -85,6 +91,11 @@
required: true, required: true,
message: this.$t('organization.integration.input_jira_url'), message: this.$t('organization.integration.input_jira_url'),
trigger: ['change', 'blur'] trigger: ['change', 'blur']
},
issuetype: {
required: true,
message: this.$t('organization.integration.input_jira_issuetype'),
trigger: ['change', 'blur']
} }
}, },
} }
@ -105,6 +116,7 @@
this.$set(this.form, 'account', config.account); this.$set(this.form, 'account', config.account);
this.$set(this.form, 'password', config.password); this.$set(this.form, 'password', config.password);
this.$set(this.form, 'url', config.url); this.$set(this.form, 'url', config.url);
this.$set(this.form, 'issuetype', config.issuetype);
} else { } else {
this.clear(); this.clear();
} }
@ -153,7 +165,8 @@
let auth = { let auth = {
account: this.form.account, account: this.form.account,
password: this.form.password, password: this.form.password,
url: this.form.url url: this.form.url,
issuetype: this.form.issuetype
}; };
param.organizationId = getCurrentUser().lastOrganizationId; param.organizationId = getCurrentUser().lastOrganizationId;
param.platform = this.platform; param.platform = this.platform;
@ -188,6 +201,7 @@
this.$set(this.form, 'account', config.account); this.$set(this.form, 'account', config.account);
this.$set(this.form, 'password', config.password); this.$set(this.form, 'password', config.password);
this.$set(this.form, 'url', config.url); this.$set(this.form, 'url', config.url);
this.$set(this.form, 'issuetype', config.issuetype);
} else { } else {
this.clear(); this.clear();
} }
@ -197,6 +211,7 @@
this.$set(this.form, 'account', ''); this.$set(this.form, 'account', '');
this.$set(this.form, 'password', ''); this.$set(this.form, 'password', '');
this.$set(this.form, 'url', ''); this.$set(this.form, 'url', '');
this.$set(this.form, 'issuetype', '');
this.$nextTick(() => { this.$nextTick(() => {
this.$refs.form.clearValidate(); this.$refs.form.clearValidate();
}); });

View File

@ -352,8 +352,8 @@ export default {
handleAddStep(index, data) { handleAddStep(index, data) {
let step = {}; let step = {};
step.num = data.num + 1; step.num = data.num + 1;
step.desc = null; step.desc = "";
step.result = null; step.result = "";
this.form.steps.forEach(step => { this.form.steps.forEach(step => {
if (step.num > data.num) { if (step.num > data.num) {
step.num++; step.num++;

View File

@ -186,11 +186,14 @@ export default {
api_account: 'API account', api_account: 'API account',
api_password: 'API password', api_password: 'API password',
jira_url: 'JIRA url', jira_url: 'JIRA url',
jira_issuetype: 'JIRA issuetype',
input_api_account: 'please enter account', input_api_account: 'please enter account',
input_api_password: 'Please enter password', input_api_password: 'Please enter password',
input_jira_url: 'Please enter Jira address, for example: https://metersphere.atlassian.net/', input_jira_url: 'Please enter Jira address, for example: https://metersphere.atlassian.net/',
input_jira_issuetype: 'Please enter the question type',
use_tip: 'Usage guidelines:', use_tip: 'Usage guidelines:',
use_tip_one: 'Basic Auth account information is queried in "Company Management-Security and Integration-Open Platform"', use_tip_tapd: 'Basic Auth account information is queried in "Company Management-Security and Integration-Open Platform"',
use_tip_jira: 'Jira software server authentication information is account password, Jira software cloud authentication information is account + token (account settings-security-create API token)',
use_tip_two: 'After saving the Basic Auth account information, you need to manually associate the ID/key in the Metersphere project', use_tip_two: 'After saving the Basic Auth account information, you need to manually associate the ID/key in the Metersphere project',
link_the_project_now: 'Link the project now', link_the_project_now: 'Link the project now',
cancel_edit: 'Cancel edit', cancel_edit: 'Cancel edit',

View File

@ -187,11 +187,14 @@ export default {
api_account: 'API 账号', api_account: 'API 账号',
api_password: 'API 口令', api_password: 'API 口令',
jira_url: 'JIRA 地址', jira_url: 'JIRA 地址',
jira_issuetype: '问题类型',
input_api_account: '请输入账号', input_api_account: '请输入账号',
input_api_password: '请输入口令', input_api_password: '请输入口令',
input_jira_url: '请输入Jira地址https://metersphere.atlassian.net/', input_jira_url: '请输入Jira地址https://metersphere.atlassian.net/',
input_jira_issuetype: '请输入问题类型',
use_tip: '使用指引:', use_tip: '使用指引:',
use_tip_one: 'Basic Auth 账号信息在"公司管理-安全与集成-开放平台"中查询', use_tip_tapd: 'Tapd Basic Auth 账号信息在"公司管理-安全与集成-开放平台"中查询',
use_tip_jira: 'Jira software server 认证信息为 账号密码Jira software cloud 认证信息为 账号+令牌(账户设置-安全-创建API令牌)',
use_tip_two: '保存 Basic Auth 账号信息后,需要在 Metersphere 项目中手动关联 ID/key', use_tip_two: '保存 Basic Auth 账号信息后,需要在 Metersphere 项目中手动关联 ID/key',
link_the_project_now: '马上关联项目', link_the_project_now: '马上关联项目',
cancel_edit: '取消编辑', cancel_edit: '取消编辑',

View File

@ -185,11 +185,14 @@ export default {
api_account: 'API 賬號', api_account: 'API 賬號',
api_password: 'API 口令', api_password: 'API 口令',
jira_url: 'JIRA 地址', jira_url: 'JIRA 地址',
jira_issuetype: '問題類型',
input_api_account: '請輸入賬號', input_api_account: '請輸入賬號',
input_api_password: '請輸入口令', input_api_password: '請輸入口令',
input_jira_url: '請輸入Jira地址https://metersphere.atlassian.net/', input_jira_url: '請輸入Jira地址https://metersphere.atlassian.net/',
input_jira_issuetype: '請輸入問題類型',
use_tip: '使用指引:', use_tip: '使用指引:',
use_tip_one: 'Basic Auth 賬號信息在"公司管理-安全與集成-開放平台"中查詢', use_tip_tapd: 'Basic Auth 賬號信息在"公司管理-安全與集成-開放平台"中查詢',
use_tip_jira: 'Jira software server 認證信息為 賬號密碼Jira software cloud 認證信息為 賬號+令牌(賬戶設置-安全-創建API令牌)',
use_tip_two: '保存 Basic Auth 賬號信息後,需要在 Metersphere 項目中手動關聯 ID/key', use_tip_two: '保存 Basic Auth 賬號信息後,需要在 Metersphere 項目中手動關聯 ID/key',
link_the_project_now: '馬上關聯項目', link_the_project_now: '馬上關聯項目',
cancel_edit: '取消編輯', cancel_edit: '取消編輯',