This commit is contained in:
wenyann 2020-08-04 16:10:43 +08:00
commit fdfee3a6b8
24 changed files with 183 additions and 53 deletions

View File

@ -47,10 +47,6 @@ public class APITestController {
return apiTestService.getApiTestByProjectId(projectId); return apiTestService.getApiTestByProjectId(projectId);
} }
@GetMapping("/state/get/{testId}")
public ApiTest apiState(@PathVariable String testId) {
return apiTestService.getApiTestByTestId(testId);
}
@PostMapping(value = "/schedule/update") @PostMapping(value = "/schedule/update")
public void updateSchedule(@RequestBody Schedule request) { public void updateSchedule(@RequestBody Schedule request) {

View File

@ -126,9 +126,6 @@ public class APITestService {
return null; return null;
} }
public ApiTest getApiTestByTestId(String testId) {
return apiTestMapper.selectByPrimaryKey(testId);
}
public List<ApiTest> getApiTestByProjectId(String projectId) { public List<ApiTest> getApiTestByProjectId(String projectId) {
return extApiTestMapper.getApiTestByProjectId(projectId); return extApiTestMapper.getApiTestByProjectId(projectId);

View File

@ -225,7 +225,12 @@
</foreach> </foreach>
</if> </if>
</where> </where>
order by update_time desc <if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
${order.name} ${order.type}
</foreach>
</if>
</select> </select>
<select id="getMaxNumByProjectId" resultType="io.metersphere.base.domain.TestCase"> <select id="getMaxNumByProjectId" resultType="io.metersphere.base.domain.TestCase">

View File

@ -128,7 +128,6 @@ public class UserController {
} }
@GetMapping("/list") @GetMapping("/list")
@RequiresRoles(value = {RoleConstants.ADMIN, RoleConstants.ORG_ADMIN, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public List<User> getUserList() { public List<User> getUserList() {
return userService.getUserList(); return userService.getUserList();
} }

View File

@ -1,6 +1,7 @@
package io.metersphere.track.request.testcase; package io.metersphere.track.request.testcase;
import io.metersphere.base.domain.TestCaseWithBLOBs; import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.controller.request.OrderRequest;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -10,4 +11,5 @@ import java.util.List;
@Setter @Setter
public class TestCaseBatchRequest extends TestCaseWithBLOBs { public class TestCaseBatchRequest extends TestCaseWithBLOBs {
private List<String> ids; private List<String> ids;
private List<OrderRequest> orders;
} }

View File

@ -356,6 +356,12 @@ public class TestCaseService {
} }
private List<TestCaseExcelData> generateTestCaseExcel(TestCaseBatchRequest request) { private List<TestCaseExcelData> generateTestCaseExcel(TestCaseBatchRequest request) {
List<OrderRequest> orderList = ServiceUtils.getDefaultOrder(request.getOrders());
OrderRequest order = new OrderRequest();
order.setName("sort");
order.setType("desc");
orderList.add(order);
request.setOrders(orderList);
List<TestCaseDTO> TestCaseList = extTestCaseMapper.listByTestCaseIds(request); List<TestCaseDTO> TestCaseList = extTestCaseMapper.listByTestCaseIds(request);
List<TestCaseExcelData> list = new ArrayList<>(); List<TestCaseExcelData> list = new ArrayList<>();
StringBuilder step = new StringBuilder(""); StringBuilder step = new StringBuilder("");
@ -466,6 +472,7 @@ public class TestCaseService {
/** /**
* 导入用例前检查数据库是否存在此用例 * 导入用例前检查数据库是否存在此用例
*
* @param testCaseWithBLOBs * @param testCaseWithBLOBs
* @return * @return
*/ */

View File

@ -0,0 +1,2 @@
ALTER TABLE load_test_report_result
MODIFY report_value LONGTEXT NULL;

View File

@ -62,17 +62,22 @@ export default {
let url = "/api/report/get/" + this.reportId; let url = "/api/report/get/" + this.reportId;
this.$get(url, response => { this.$get(url, response => {
this.report = response.data || {}; this.report = response.data || {};
if (this.isNotRunning) { if (response.data) {
try { if (this.isNotRunning) {
this.content = JSON.parse(this.report.content); try {
} catch (e) { this.content = JSON.parse(this.report.content);
console.log(this.report.content) } catch (e) {
throw e; console.log(this.report.content)
throw e;
}
this.getFails();
this.loading = false;
} else {
setTimeout(this.getReport, 2000)
} }
this.getFails();
this.loading = false;
} else { } else {
setTimeout(this.getReport, 2000) this.loading = false;
this.$error(this.$t('api_report.not_exist'));
} }
}); });
} }

View File

@ -52,7 +52,7 @@
<ms-api-report-dialog :test-id="id" ref="reportDialog"/> <ms-api-report-dialog :test-id="id" ref="reportDialog"/>
<ms-schedule-config :schedule="test.schedule" :save="saveCronExpression" @scheduleChange="saveSchedule" :check-open="checkScheduleEdit"/> <ms-schedule-config :schedule="test.schedule" :is-read-only="isReadOnly" :save="saveCronExpression" @scheduleChange="saveSchedule" :check-open="checkScheduleEdit"/>
</el-row> </el-row>
</el-header> </el-header>
<ms-api-scenario-config :is-read-only="isReadOnly" :scenarios="test.scenarioDefinition" :project-id="test.projectId" ref="config"/> <ms-api-scenario-config :is-read-only="isReadOnly" :scenarios="test.scenarioDefinition" :project-id="test.projectId" ref="config"/>

View File

@ -9,13 +9,13 @@
<el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200" <el-input v-if="!suggestions" :disabled="isReadOnly" v-model="item.name" size="small" maxlength="200"
@change="change" @change="change"
:placeholder="keyText" show-word-limit/> :placeholder="keyText" show-word-limit/>
<el-autocomplete :maxlength="200" v-if="suggestions" v-model="item.name" size="small" <el-autocomplete :disabled="isReadOnly" :maxlength="200" v-if="suggestions" v-model="item.name" size="small"
:fetch-suggestions="querySearch" @change="change" :placeholder="keyText" :fetch-suggestions="querySearch" @change="change" :placeholder="keyText"
show-word-limit/> show-word-limit/>
</el-col> </el-col>
<el-col> <el-col>
<el-input :disabled="isReadOnly" v-model="item.value" size="small" maxlength="2000" @change="change" <el-input :disabled="isReadOnly" v-model="item.value" size="small" @change="change"
:placeholder="valueText" show-word-limit/> :placeholder="valueText" show-word-limit/>
</el-col> </el-col>
<el-col class="kv-delete"> <el-col class="kv-delete">

View File

@ -6,7 +6,7 @@
:placeholder="$t('api_test.request.extract.json_path_expression')"/> :placeholder="$t('api_test.request.extract.json_path_expression')"/>
</el-col> </el-col>
<el-col> <el-col>
<el-input :disabled="isReadOnly" v-model="jsonPath.expect" maxlength="2000" size="small" show-word-limit <el-input :disabled="isReadOnly" v-model="jsonPath.expect" size="small" show-word-limit
:placeholder="$t('api_test.request.assertions.expect')"/> :placeholder="$t('api_test.request.assertions.expect')"/>
</el-col> </el-col>
<el-col class="assertion-btn"> <el-col class="assertion-btn">

View File

@ -10,7 +10,7 @@
</el-select> </el-select>
</el-col> </el-col>
<el-col> <el-col>
<el-input :disabled="isReadOnly" v-model="regex.expression" maxlength="2000" size="small" show-word-limit <el-input :disabled="isReadOnly" v-model="regex.expression" size="small" show-word-limit
:placeholder="$t('api_test.request.assertions.expression')"/> :placeholder="$t('api_test.request.assertions.expression')"/>
</el-col> </el-col>
<el-col class="assertion-btn"> <el-col class="assertion-btn">

View File

@ -11,7 +11,7 @@
@change="change" show-word-limit :placeholder="$t('api_test.variable_name')"/> @change="change" show-word-limit :placeholder="$t('api_test.variable_name')"/>
</el-col> </el-col>
<el-col> <el-col>
<el-input :disabled="isReadOnly" v-model="common.expression" maxlength="2000" size="small" show-word-limit <el-input :disabled="isReadOnly" v-model="common.expression" size="small" show-word-limit
:placeholder="expression"/> :placeholder="expression"/>
</el-col> </el-col>
<el-col class="extract-btn"> <el-col class="extract-btn">

View File

@ -5,8 +5,8 @@
<i class="el-icon-date" size="small"></i> <i class="el-icon-date" size="small"></i>
<span class="character" @click="scheduleEdit">SCHEDULER</span> <span class="character" @click="scheduleEdit">SCHEDULER</span>
</span> </span>
<el-switch :disabled="!schedule.value" v-model="schedule.enable" @change="scheduleChange"/> <el-switch :disabled="!schedule.value && isReadOnly" v-model="schedule.enable" @change="scheduleChange"/>
<ms-schedule-edit :schedule="schedule" :save="save" :custom-validate="customValidate" ref="scheduleEdit"/> <ms-schedule-edit :is-read-only="isReadOnly" :schedule="schedule" :save="save" :custom-validate="customValidate" ref="scheduleEdit"/>
<crontab-result v-show="false" :ex="schedule.value" ref="crontabResult" @resultListChange="resultListChange"/> <crontab-result v-show="false" :ex="schedule.value" ref="crontabResult" @resultListChange="resultListChange"/>
</div> </div>
<div> <div>
@ -44,6 +44,10 @@
type: Function, type: Function,
default: defaultCustomValidate default: defaultCustomValidate
}, },
isReadOnly: {
type: Boolean,
default: false
}
}, },
methods: { methods: {
scheduleEdit() { scheduleEdit() {

View File

@ -4,12 +4,12 @@
<el-form :model="form" :rules="rules" ref="from"> <el-form :model="form" :rules="rules" ref="from">
<el-form-item <el-form-item
prop="cronValue"> prop="cronValue">
<el-input v-model="form.cronValue" class="inp" :placeholder="$t('schedule.please_input_cron_expression')"/> <el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp" :placeholder="$t('schedule.please_input_cron_expression')"/>
<!-- <el-button type="primary" @click="showCronDialog">{{$t('schedule.generate_expression')}}</el-button>--> <!-- <el-button type="primary" @click="showCronDialog">{{$t('schedule.generate_expression')}}</el-button>-->
<el-button type="primary" @click="saveCron">{{$t('commons.save')}}</el-button> <el-button :disabled="isReadOnly" type="primary" @click="saveCron">{{$t('commons.save')}}</el-button>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-link type="primary" @click="showCronDialog">{{$t('schedule.generate_expression')}}</el-link> <el-link :disabled="isReadOnly" type="primary" @click="showCronDialog">{{$t('schedule.generate_expression')}}</el-link>
</el-form-item> </el-form-item>
<crontab-result :ex="form.cronValue" ref="crontabResult" /> <crontab-result :ex="form.cronValue" ref="crontabResult" />
</el-form> </el-form>
@ -38,6 +38,10 @@
type: Function, type: Function,
default: defaultCustomValidate default: defaultCustomValidate
}, },
isReadOnly: {
type: Boolean,
default: false
}
}, },
watch: { watch: {
'schedule.value'() { 'schedule.value'() {

View File

@ -43,12 +43,12 @@
</el-col> </el-col>
</el-row> </el-row>
<el-divider></el-divider> <el-divider/>
<el-tabs v-model="active" type="border-card" :stretch="true"> <el-tabs v-model="active" type="border-card" :stretch="true">
<el-tab-pane :label="$t('report.test_overview')"> <el-tab-pane :label="$t('report.test_overview')">
<!-- <ms-report-test-overview :id="reportId" :status="status"/>--> <!-- <ms-report-test-overview :id="reportId" :status="status"/>-->
<ms-report-test-overview :report="report"/> <ms-report-test-overview :report="report" ref="testOverview"/>
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('report.test_request_statistics')"> <el-tab-pane :label="$t('report.test_request_statistics')">
<ms-report-request-statistics :report="report"/> <ms-report-request-statistics :report="report"/>
@ -63,8 +63,8 @@
</el-card> </el-card>
<el-dialog :title="$t('report.test_stop_now_confirm')" :visible.sync="dialogFormVisible" width="30%"> <el-dialog :title="$t('report.test_stop_now_confirm')" :visible.sync="dialogFormVisible" width="30%">
<p v-html="$t('report.force_stop_tips')"></p> <p v-html="$t('report.force_stop_tips')"/>
<p v-html="$t('report.stop_tips')"></p> <p v-html="$t('report.stop_tips')"/>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="danger" size="small" @click="stopTest(true)">{{$t('report.force_stop_btn')}} <el-button type="danger" size="small" @click="stopTest(true)">{{$t('report.force_stop_btn')}}
</el-button> </el-button>
@ -190,7 +190,7 @@
} else { } else {
this.report.status = 'Completed'; this.report.status = 'Completed';
} }
}) });
this.dialogFormVisible = false; this.dialogFormVisible = false;
}, },
rerun(testId) { rerun(testId) {
@ -201,7 +201,7 @@
}).then(() => { }).then(() => {
this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => { this.result = this.$post('/performance/run', {id: testId, triggerMode: 'MANUAL'}, (response) => {
this.reportId = response.data; this.reportId = response.data;
this.$router.push({path: '/performance/report/view/' + this.reportId}) this.$router.push({path: '/performance/report/view/' + this.reportId});
// socket // socket
this.initWebSocket(); this.initWebSocket();
}) })
@ -235,16 +235,21 @@
this.reportId = this.$route.path.split('/')[4]; this.reportId = this.$route.path.split('/')[4];
this.result = this.$get("/performance/report/" + this.reportId, res => { this.result = this.$get("/performance/report/" + this.reportId, res => {
let data = res.data; let data = res.data;
this.status = data.status; if (data) {
this.$set(this.report, "id", this.reportId); this.status = data.status;
this.$set(this.report, "status", data.status); this.$set(this.report, "id", this.reportId);
this.checkReportStatus(data.status); this.$set(this.report, "status", data.status);
if (this.status === "Completed" || this.status === "Running") { this.checkReportStatus(data.status);
this.initReportTimeInfo(); if (this.status === "Completed" || this.status === "Running") {
this.initReportTimeInfo();
}
this.initBreadcrumb();
this.initWebSocket();
} else {
this.$error(this.$t('report.not_exist'))
} }
}) });
this.initBreadcrumb();
this.initWebSocket();
}, },
beforeDestroy() { beforeDestroy() {
this.websocket.close() //websocket this.websocket.close() //websocket
@ -288,6 +293,8 @@
} else { } else {
this.clearData(); this.clearData();
} }
} else {
this.$error(this.$t('report.not_exist'));
} }
}); });

View File

@ -66,8 +66,8 @@
<el-table-column label="Throughput"> <el-table-column label="Throughput">
<el-table-column <el-table-column
prop="transactions" prop="transactions"
label="Transactions" label="Transactions/s"
width="100" width="150"
/> />
</el-table-column> </el-table-column>
@ -76,13 +76,13 @@
prop="received" prop="received"
label="Received" label="Received"
align="center" align="center"
width="200" width="150"
/> />
<el-table-column <el-table-column
prop="sent" prop="sent"
label="Sent" label="Sent"
align="center" align="center"
width="200" width="150"
/> />
</el-table-column> </el-table-column>

View File

@ -8,7 +8,11 @@
</template> </template>
<el-table border class="adjust-table" @row-click="link" :data="items" style="width: 100%" @sort-change="sort"> <el-table border class="adjust-table" @row-click="link" :data="items" style="width: 100%" @sort-change="sort">
<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/>
<el-table-column prop="description" :label="$t('commons.description')" show-overflow-tooltip/> <el-table-column prop="description" :label="$t('commons.description')" show-overflow-tooltip>
<template v-slot:default="scope">
<pre>{{scope.row.description}}</pre>
</template>
</el-table-column>
<!--<el-table-column prop="workspaceName" :label="$t('project.owning_workspace')"/>--> <!--<el-table-column prop="workspaceName" :label="$t('project.owning_workspace')"/>-->
<el-table-column <el-table-column
sortable sortable

View File

@ -0,0 +1,78 @@
<template>
<div>
<el-dialog
title="批量编辑用例"
:visible.sync="dialogVisible"
width="25%"
class="batch-edit-dialog"
:destroy-on-close="true"
@close="handleClose"
>
<el-form :model="form" label-position="right" label-width="150px" size="medium" ref="form" :rules="rules">
<el-form-item :label="$t('test_track.case.batch_update', [size])" prop="type">
<el-select v-model="form.type" style="width: 80%">
<el-option label="用例等级" value="priority"/>
<el-option label="类型" value="type"/>
<el-option label="测试方式" value="method"/>
<el-option label="维护人" value="maintainer"/>
</el-select>
</el-form-item>
<el-form-item label="更新后属性值为" prop="value">
<el-select v-model="form.value" style="width: 80%">
<el-option label="值1" value="value1"/>
<el-option label="值2" value="value2"/>
</el-select>
</el-form-item>
</el-form>
<template v-slot:footer>
<ms-dialog-footer
@cancel="dialogVisible = false"
@confirm="submit('form')"/>
</template>
</el-dialog>
</div>
</template>
<script>
import MsDialogFooter from "../../../common/components/MsDialogFooter";
export default {
name: "BatchEdit",
components: {
MsDialogFooter
},
data() {
return {
dialogVisible: false,
form: {},
size: 0,
rules: {
type: {required: true, message: "请选择属性", trigger: ['blur','change']},
value: {required: true, message: "请选择属性对应的值", trigger: ['blur','change']}
},
}
},
methods: {
submit(form) {
this.$refs[form].validate((valid) => {
if (valid) {
this.$emit("submit", this.form);
} else {
return false;
}
});
},
open() {
this.dialogVisible = true;
this.size = this.$parent.selectRows.size;
},
handleClose() {
this.form = {};
}
}
}
</script>
<style scoped>
</style>

View File

@ -49,8 +49,8 @@
.show-more-btn { .show-more-btn {
width: 20px; width: 20px;
height: 30px; height: 25px;
line-height: 30px; line-height: 25px;
background-color: #FFF; background-color: #FFF;
} }

View File

@ -119,6 +119,8 @@
:total="total"/> :total="total"/>
</el-card> </el-card>
<batch-edit ref="batchEdit"/>
</div> </div>
</template> </template>
@ -139,6 +141,7 @@
import {_filter, _sort} from "../../../../../common/js/utils"; import {_filter, _sort} from "../../../../../common/js/utils";
import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components"; import {TEST_CASE_CONFIGS} from "../../../common/components/search/search-components";
import ShowMoreBtn from "./ShowMoreBtn"; import ShowMoreBtn from "./ShowMoreBtn";
import BatchEdit from "./BatchEdit";
export default { export default {
name: "TestCaseList", name: "TestCaseList",
@ -149,7 +152,14 @@
MethodTableItem, MethodTableItem,
TypeTableItem, TypeTableItem,
PriorityTableItem, PriorityTableItem,
MsCreateBox, TestCaseImport, TestCaseExport, MsTablePagination, NodeBreadcrumb, MsTableHeader, ShowMoreBtn MsCreateBox,
TestCaseImport,
TestCaseExport,
MsTablePagination,
NodeBreadcrumb,
MsTableHeader,
ShowMoreBtn,
BatchEdit
}, },
data() { data() {
return { return {
@ -326,6 +336,7 @@
this.selectRows.add(row); this.selectRows.add(row);
} }
// todo
if (this.selectRows.size > 1) { if (this.selectRows.size > 1) {
Array.from(this.selectRows).forEach(row => { Array.from(this.selectRows).forEach(row => {
this.$set(row, "showMore", true); this.$set(row, "showMore", true);
@ -393,7 +404,7 @@
this.initTableData(); this.initTableData();
}, },
handleClickStop() { handleClickStop() {
console.log("click stop"); this.$refs.batchEdit.open();
} }
} }
} }

View File

@ -266,6 +266,7 @@ export default {
stop_tips: 'A <strong>Graceful shutdown</strong> will archive the JTL files and then stop the servers.', stop_tips: 'A <strong>Graceful shutdown</strong> will archive the JTL files and then stop the servers.',
force_stop_btn: 'Terminating', force_stop_btn: 'Terminating',
stop_btn: 'Graceful shutdown', stop_btn: 'Graceful shutdown',
not_exist: "Test report does not exist",
}, },
load_test: { load_test: {
operating: 'Operating', operating: 'Operating',
@ -476,6 +477,7 @@ export default {
detail: "Report detail", detail: "Report detail",
delete: "Delete report", delete: "Delete report",
running: "The test is running", running: "The test is running",
not_exist: "Test report does not exist",
}, },
test_track: { test_track: {
test_track: "Track", test_track: "Track",
@ -531,6 +533,7 @@ export default {
relate_test: "Relate test", relate_test: "Relate test",
relate_test_not_find: 'The associated test does not exist, please check the test case', relate_test_not_find: 'The associated test does not exist, please check the test case',
batch_handle: 'Batch processing (select {0} item)', batch_handle: 'Batch processing (select {0} item)',
batch_update: 'Update the attributes of {0} cases',
import: { import: {
import: "Import test case", import: "Import test case",
case_import: "Import test case", case_import: "Import test case",

View File

@ -264,6 +264,7 @@ export default {
stop_tips: '<strong>停止</strong>测试会结束当前测试并保留报告数据', stop_tips: '<strong>停止</strong>测试会结束当前测试并保留报告数据',
force_stop_btn: '强制停止', force_stop_btn: '强制停止',
stop_btn: '停止', stop_btn: '停止',
not_exist: "测试报告不存在",
}, },
load_test: { load_test: {
operating: '操作', operating: '操作',
@ -475,6 +476,7 @@ export default {
detail: "报告详情", detail: "报告详情",
delete: "删除报告", delete: "删除报告",
running: "测试执行中", running: "测试执行中",
not_exist: "测试报告不存在",
}, },
test_track: { test_track: {
test_track: "测试跟踪", test_track: "测试跟踪",
@ -531,6 +533,7 @@ export default {
relate_test: "关联测试", relate_test: "关联测试",
relate_test_not_find: '关联的测试不存在,请检查用例', relate_test_not_find: '关联的测试不存在,请检查用例',
batch_handle: '批量处理 (选中{0}项)', batch_handle: '批量处理 (选中{0}项)',
batch_update: '更新{0}个用例的属性',
import: { import: {
import: "导入用例", import: "导入用例",
case_import: "导入测试用例", case_import: "导入测试用例",

View File

@ -264,6 +264,7 @@ export default {
stop_tips: '<strong>停止</strong>測試會結束當前測試並保留報告數據', stop_tips: '<strong>停止</strong>測試會結束當前測試並保留報告數據',
force_stop_btn: '強制停止', force_stop_btn: '強制停止',
stop_btn: '停止', stop_btn: '停止',
not_exist: "測試報告不存在",
}, },
load_test: { load_test: {
operating: '操作', operating: '操作',
@ -475,6 +476,7 @@ export default {
detail: "報告詳情", detail: "報告詳情",
delete: "刪除報告", delete: "刪除報告",
running: "測試執行中", running: "測試執行中",
not_exist: "測試報告不存在",
}, },
test_track: { test_track: {
test_track: "測試跟踪", test_track: "測試跟踪",
@ -530,6 +532,7 @@ export default {
relate_test: "關聯測試", relate_test: "關聯測試",
relate_test_not_find: '關聯的測試不存在,請檢查用例', relate_test_not_find: '關聯的測試不存在,請檢查用例',
batch_handle: '批量處理 (選中{0}項)', batch_handle: '批量處理 (選中{0}項)',
batch_update: '更新{0}個用例的屬性',
import: { import: {
import: "導入用例", import: "導入用例",
case_import: "導入測試用例", case_import: "導入測試用例",