This commit is contained in:
chenjianxing 2021-01-22 16:32:24 +08:00
commit 4144a35a26
27 changed files with 176 additions and 54 deletions

View File

@ -1,6 +1,6 @@
package io.metersphere.api.dto.automation;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import lombok.Getter;
import lombok.Setter;
@ -8,7 +8,7 @@ import java.util.List;
@Getter
@Setter
public class ApiScenarioDTO extends ApiScenario {
public class ApiScenarioDTO extends ApiScenarioWithBLOBs {
private String projectName;
private String userName;

View File

@ -13,8 +13,8 @@ public class TaskInfoResult {
private int index;
//任务ID
private String taskID;
//场景名称
private String scenario;
//任务名称
private String name;
//场景ID
private String scenarioId;
//规则

View File

@ -198,6 +198,7 @@
FROM
api_test_case t1
LEFT JOIN api_definition_exec_result t2 ON t1.last_result_id = t2.id
inner join api_definition a on t1.api_definition_id = a.id
LEFT JOIN USER u1 ON t1.update_user_id = u1.id
LEFT JOIN USER u2 ON t1.create_user_id = u2.id
LEFT JOIN USER u3 ON t2.user_id = u3.id
@ -224,6 +225,12 @@
<if test="request.apiDefinitionId != null and request.apiDefinitionId!=''">
AND t1.api_definition_id = #{request.apiDefinitionId}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and a.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
</where>
<if test="request.orders != null and request.orders.size() > 0">

View File

@ -50,14 +50,14 @@
AND create_time BETWEEN #{startTime} and #{endTime}
</select>
<select id="findRunningTaskInfoByProjectID" resultType="io.metersphere.api.dto.datacount.response.TaskInfoResult">
SELECT apiScene.id AS scenarioId,apiScene.`name` AS scenario,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
SELECT apiScene.id AS scenarioId,apiScene.`name` AS `name` ,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
'scenario' AS taskType
FROM api_scenario apiScene
INNER JOIN `schedule` sch ON apiScene.id = sch.resource_id
INNER JOIN `user` u ON u.id = sch.user_id
WHERE sch.`enable` = true AND apiScene.project_id = #{0,jdbcType=VARCHAR}
UNION
SELECT testPlan.id AS scenarioId,testPlan.`name` AS scenario,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
SELECT testPlan.id AS scenarioId,testPlan.`name` AS `name`,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
'testPlan' AS taskType
FROM test_plan testPlan
INNER JOIN `schedule` sch ON testPlan.id = sch.resource_id

@ -1 +1 @@
Subproject commit 132f406fac7fb4d841210343eb98c09f78317f18
Subproject commit 8d175b5363274672ff33a5883b730e8aaa823f8d

View File

@ -72,7 +72,7 @@
</el-row>
<el-row>
<el-col :span="7">
<el-form-item :label="$t('api_test.automation.tag')" prop="tags">
<el-form-item :label="$t('api_test.automation.tag')" prop="tags">
<ms-input-tag :currentScenario="currentScenario" ref="tag"/>
</el-form-item>
</el-col>
@ -615,7 +615,7 @@
this.$refs.tag.open();
},
remove(row, node) {
let name = row.name === undefined ? "" : row.name;
let name = row === undefined || row.name === undefined ? "" : row.name;
this.$alert(this.$t('api_test.definition.request.delete_confirm_step') + ' ' + name + " ", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
@ -696,9 +696,9 @@
}
},
checkDataIsCopy(){
checkDataIsCopy() {
//
if(this.currentScenario.copy){
if (this.currentScenario.copy) {
this.editScenario(false);
}
},

View File

@ -86,6 +86,11 @@
if (!this.data.name) {
this.isShowInput = true;
}
if (this.$refs.nameEdit) {
this.$nextTick(() => {
this.$refs.nameEdit.focus();
});
}
},
methods: {
active() {

View File

@ -11,11 +11,11 @@
:title="displayTitle">
<template v-slot:behindHeaderLeft>
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Deleted'" type="danger">{{$t('api_test.automation.reference_deleted')}}</el-tag>
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Copy'">{{ $t('commons.copy') }}</el-tag>
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced ==='REF'">{{ $t('api_test.scenario.reference') }}</el-tag>
<ms-run :debug="false" :reportId="reportId" :run-data="runData"
@runRefresh="runRefresh" ref="runTest"/>
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Deleted'" type="danger">{{$t('api_test.automation.reference_deleted')}}</el-tag>
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Copy'">{{ $t('commons.copy') }}</el-tag>
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced ==='REF'">{{ $t('api_test.scenario.reference') }}</el-tag>
<ms-run :debug="false" :reportId="reportId" :run-data="runData"
@runRefresh="runRefresh" ref="runTest"/>
</template>
@ -42,7 +42,7 @@
<ms-dubbo-basis-parameters :request="request" v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'" :showScript="false"/>
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
<ms-request-result-tail draggable :currentProtocol="request.protocol" :response="request.requestResult" ref="runResult"/>
<api-response-component :result="request.requestResult"/>
<!-- 保存操作 -->
<el-button type="primary" size="small" style="margin: 20px; float: right" @click="saveTestCase(item)" v-if="!request.referenced">
@ -62,6 +62,8 @@
import MsRun from "../../../definition/components/Run";
import {getUUID} from "@/common/js/utils";
import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiResponseComponent from "./ApiResponseComponent";
export default {
name: "MsApiComponent",
props: {
@ -75,8 +77,9 @@
currentEnvironmentId: String,
},
components: {
ApiBaseComponent,
MsSqlBasisParameters, MsTcpBasisParameters, MsDubboBasisParameters, MsApiRequestForm, MsRequestResultTail, MsRun},
ApiBaseComponent, ApiResponseComponent,
MsSqlBasisParameters, MsTcpBasisParameters, MsDubboBasisParameters, MsApiRequestForm, MsRequestResultTail, MsRun
},
data() {
return {
loading: false,
@ -129,31 +132,31 @@
return this.$t('api_test.automation.api_list_import');
} else if (this.isExternalImport) {
return this.$t('api_test.automation.external_import');
} else if(this.isCustomizeReq) {
} else if (this.isCustomizeReq) {
return this.$t('api_test.automation.customize_req');
}
return "";
},
isApiImport() {
if (this.request.referenced!=undefined && this.request.referenced==='Deleted' || this.request.referenced=='REF' || this.request.referenced==='Copy') {
if (this.request.referenced != undefined && this.request.referenced === 'Deleted' || this.request.referenced == 'REF' || this.request.referenced === 'Copy') {
return true
}
return false;
},
isExternalImport() {
if (this.request.referenced!=undefined && this.request.referenced==='OT_IMPORT') {
if (this.request.referenced != undefined && this.request.referenced === 'OT_IMPORT') {
return true
}
return false;
},
isCustomizeReq() {
if (this.request.referenced==undefined || this.request.referenced==='Created') {
if (this.request.referenced == undefined || this.request.referenced === 'Created') {
return true
}
return false;
},
isDeletedOrRef() {
if (this.request.referenced!= undefined && this.request.referenced === 'Deleted' || this.request.referenced === 'REF') {
if (this.request.referenced != undefined && this.request.referenced === 'Deleted' || this.request.referenced === 'REF') {
return true
}
return false;
@ -218,6 +221,7 @@
this.$error(this.$t('api_test.environment.select_environment'));
return;
}
this.request.active = true;
this.loading = true;
this.runData = [];
this.request.useEnvironment = this.currentEnvironmentId;
@ -250,9 +254,11 @@
margin-right: 20px;
color: #409EFF;
}
/deep/ .el-card__body {
padding: 15px;
}
.tip {
padding: 3px 5px;
font-size: 16px;
@ -260,12 +266,15 @@
border-left: 4px solid #783887;
margin: 20px 0;
}
.name-input {
width: 30%;
}
.el-icon-arrow-right {
margin-right: 5px;
}
.icon.is-active {
transform: rotate(90deg);
}

View File

@ -24,7 +24,7 @@
export default {
name: "ApiResponseComponent",
components: {ElCollapseTransition, MsRequestResultTail, ApiBaseComponent, MsRequestMetric},
props: ['apiItem'],
props: {apiItem: {}, result: {}},
data() {
return {
isActive: false,
@ -32,8 +32,13 @@
}
},
created() {
this.getExecResult();
if (this.apiItem.isActive) {
if (!this.result) {
this.getExecResult();
if (this.apiItem.isActive) {
this.isActive = true;
}
} else {
this.response = this.result;
this.isActive = true;
}
},

View File

@ -10,7 +10,8 @@
:title="$t('api_test.automation.wait_controller')">
<template v-slot:headerLeft>
<el-input-number class="time-input" size="small" v-model="timer.delay" :min="0" :step="1000"/> ms
<el-input-number class="time-input" size="small" v-model="timer.delay" :min="0" :step="1000" ref="nameInput"/>
ms
</template>
</api-base-component>
@ -18,6 +19,7 @@
<script>
import ApiBaseComponent from "../common/ApiBaseComponent";
export default {
name: "MsConstantTimer",
components: {ApiBaseComponent},
@ -32,6 +34,11 @@
data() {
return {}
},
created() {
this.$nextTick(() => {
this.$refs.nameInput.focus();
});
},
methods: {
remove() {
this.$emit('remove', this.timer, this.node);

View File

@ -52,10 +52,10 @@
},
methods: {
remove() {
this.$emit('remove', this.jsr223ProcessorData, this.node);
this.$emit('remove', this.jsr223Processor, this.node);
},
copyRow() {
this.$emit('copyRow', this.jsr223ProcessorData, this.node);
this.$emit('copyRow', this.jsr223Processor, this.node);
},
}
}

View File

@ -84,6 +84,7 @@
editFlag: false,
previewData: [],
columns: [],
allDatas: [],
rule: {
name: [
{required: true, message: this.$t('api_test.variable_name'), trigger: 'blur'},
@ -97,14 +98,20 @@
this.$error(results.errors);
return;
}
if (results.data) {
this.columns = results.data[0];
this.previewData = results.data;
if (this.allDatas) {
this.columns = this.allDatas[0];
this.allDatas.splice(0, 1);
this.previewData = this.allDatas;
}
this.loading = false;
},
step(results, parser) {
this.allDatas.push(results.data);
},
handleClick() {
let config = {complete: this.complete};
let config = {complete: this.complete, step: this.step};
this.allDatas = [];
//
if (this.editData.files && this.editData.files.length > 0 && this.editData.files[0].file) {
this.loading = true;

View File

@ -1,5 +1,5 @@
<template>
<el-dialog :close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible"
<el-dialog :close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible" :append-to-body='true'
@close="close">
<template>
<div>

View File

@ -209,7 +209,7 @@
},
addTab(tab) {
if (tab.name === 'add') {
this.handleCommand("ADD");
this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug");
}
},
handleCommand(e) {

View File

@ -169,10 +169,27 @@
let bodyFiles = this.getBodyUploadFiles(data);
this.$fileUpload(this.reqUrl, null, bodyFiles, data, () => {
this.$success(this.$t('commons.save_success'));
if (this.reqUrl.endsWith('/create')) {
this.saveTestCase(data);
}
this.reqUrl = "/api/definition/update";
this.$emit('saveApi', data);
});
},
saveTestCase(row) {
let tmp = {request: JSON.parse(JSON.stringify(row.request))};
tmp.projectId = getCurrentProjectID();
tmp.active = true;
tmp.priority = "P0";
tmp.name = row.name;
tmp.request.path = row.path;
tmp.request.method = row.method;
tmp.apiDefinitionId = row.id;
let bodyFiles = this.getBodyUploadFiles(tmp);
let url = "/api/testcase/create";
this.$fileUpload(url, null, bodyFiles, tmp, (response) => {
});
},
setParameters(data) {
data.projectId = this.projectId;
this.request.name = this.currentApi.name;
@ -185,6 +202,9 @@
data.request.protocol = this.currentProtocol;
}
data.id = data.request.id;
if (!data.method) {
data.method = this.currentProtocol;
}
data.response = this.response;
},
getBodyUploadFiles(data) {

View File

@ -6,7 +6,7 @@
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -43,7 +43,7 @@
export default {
name: "ApiConfig",
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters,MsJmxStep},
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters, MsJmxStep},
props: {
currentProtocol: String,
scenario: Boolean,
@ -119,6 +119,7 @@
},
saveAs() {
let obj = {request: this.request};
obj.request.id = getUUID();
this.$emit('saveAs', obj);
}
}

View File

@ -19,7 +19,7 @@
@command="handleCommand" size="small" v-if="testCase===undefined && !scenario">
{{$t('commons.test')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-form-item>
@ -58,7 +58,7 @@
export default {
name: "ApiConfig",
components: {MsRequestResultTail, MsResponseResult, MsApiRequestForm, MsRequestMetric, MsResponseText, MsRun,MsJmxStep},
components: {MsRequestResultTail, MsResponseResult, MsApiRequestForm, MsRequestMetric, MsResponseText, MsRun, MsJmxStep},
props: {
currentProtocol: String,
testCase: {},
@ -155,6 +155,8 @@
saveAs() {
this.$refs['debugForm'].validate((valid) => {
if (valid) {
this.debugForm.id = null;
this.request.id = getUUID();
this.debugForm.request = this.request;
this.debugForm.userId = getCurrentUser().id;
this.debugForm.status = "Underway";

View File

@ -6,7 +6,7 @@
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
@ -122,6 +122,7 @@
},
saveAs() {
let obj = {request: this.request};
obj.request.id = getUUID();
this.$emit('saveAs', obj);
}
}

View File

@ -15,7 +15,7 @@
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</el-form-item>
@ -131,6 +131,7 @@
},
saveAs() {
let obj = {request: this.request};
obj.request.id = getUUID();
this.$emit('saveAs', obj);
}
}

View File

@ -7,13 +7,19 @@
</template>
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
<el-table-column prop="index" :label="$t('api_test.home_page.running_task_list.table_coloum.index')" width="80" show-overflow-tooltip/>
<el-table-column prop="scenario" :label="$t('api_test.home_page.running_task_list.table_coloum.scenario')" width="200" >
<el-table-column prop="name" :label="$t('commons.name')" width="200" >
<template v-slot:default="{row}">
<el-link type="info" @click="redirect(row)">
{{ row.scenario }}
{{ row.name }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="taskType" :label="$t('api_test.home_page.running_task_list.table_coloum.task_type')" width="120" show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag v-if="scope.row.taskType == 'scenario'" type="success" effect="plain" :content="$t('api_test.home_page.running_task_list.scenario_schedule')"/>
<ms-tag v-if="scope.row.taskType == 'testPlan'" type="warning" effect="plain" :content="$t('api_test.home_page.running_task_list.test_plan_schedule')"/>
</template>
</el-table-column>
<el-table-column prop="rule" :label="$t('api_test.home_page.running_task_list.table_coloum.run_rule')" width="120" show-overflow-tooltip/>
<el-table-column width="100" :label="$t('api_test.home_page.running_task_list.table_coloum.task_status')">
<template v-slot:default="scope">
@ -46,9 +52,12 @@
<script>
import {getCurrentProjectID} from "@/common/js/utils";
import MsTag from "@/business/components/common/components/MsTag";
export default {
name: "MsRunningTaskList",
components: {
MsTag
},
data() {
return {

View File

@ -10,7 +10,21 @@
<el-table-column
prop="label"
label="Label"
width="450"/>
width="450">
<template v-slot:header="{column}">
<span>Label</span>
<i class="el-icon-search" style="margin-left: 8px;cursor: pointer;font-weight: bold;" @click="click(column)"></i>
<el-input v-model="searchLabel"
placeholder="请输入 Label 搜索"
size="mini"
class="search_input"
style="width: 250px; margin-left: 5px"
v-if="column.showSearch"
clearable
@clear="filterLabel"
@keyup.enter.native="filterLabel"/>
</template>
</el-table-column>
</el-table-column>
<el-table-column label="Executions" align="center">
@ -94,17 +108,35 @@ export default {
data() {
return {
tableData: [],
id: ''
originalData: [],
id: '',
searchLabel: '',
showSearch: false,
showBtn: true,
}
},
methods: {
initTableData() {
this.$get("/performance/report/content/" + this.id).then(res => {
this.tableData = res.data.data;
this.originalData = res.data.data;
}).catch(() => {
this.tableData = [];
})
},
click(column) {
this.searchLabel = '';
this.tableData = this.originalData;
this.$set(column, 'showSearch', !column.showSearch);
},
filterLabel() {
this.tableData = this.searchLabel ? this.originalData.filter(this.createFilter(this.searchLabel)) : this.originalData;
},
createFilter(queryString) {
return item => {
return (item.label.toLowerCase().indexOf(queryString.toLowerCase()) !== -1);
};
},
},
watch: {
report: {
@ -128,5 +160,7 @@ export default {
</script>
<style scoped>
.search_input >>> .el-input__inner {
border-radius: 50px;
}
</style>

View File

@ -291,7 +291,7 @@ export default {
},
fileChange(threadGroups) {
let handler = this.$refs.pressureConfig;
handler.threadGroups = threadGroups;
threadGroups.forEach(tg => {
tg.threadNumber = tg.threadNumber || 10;
tg.duration = tg.duration || 10;
@ -301,8 +301,13 @@ export default {
tg.threadType = tg.threadType || 'DURATION';
tg.iterateNum = tg.iterateNum || 1;
tg.iterateRampUp = tg.iterateRampUp || 10;
handler.calculateChart(tg);
});
this.$set(handler, "threadGroups", threadGroups);
threadGroups.forEach(tg => {
handler.calculateChart(tg);
})
}
}
}

View File

@ -58,6 +58,7 @@
v-model="threadGroup.rpsLimit"
@change="calculateChart(threadGroup)"
:min="1"
:max="500"
size="mini"/>
</el-form-item>
<br>
@ -87,6 +88,7 @@
:disabled="isReadOnly"
v-model="threadGroup.iterateNum"
:min="1"
:max="10000"
@change="calculateChart(threadGroup)"
size="mini"/>
</el-form-item>
@ -98,6 +100,7 @@
:disabled="isReadOnly || !threadGroup.rpsLimitEnable"
v-model="threadGroup.rpsLimit"
:min="1"
:max="500"
size="mini"/>
</el-form-item>
<br>

View File

@ -226,10 +226,7 @@ export default {
if (projectId) {
this.projectId = projectId;
}
this.$refs.nodeTree.result = this.$post("/case/node/list/all/plan",
{testPlanId: this.planId, projectId: this.projectId}, response => {
this.treeNodes = response.data;
});
this.treeNodes = [];
this.selectNodeIds = [];
}
}

View File

@ -919,12 +919,15 @@ export default {
table_coloum: {
index: "Index",
scenario: "Scene",
task_type: "Task Type",
run_rule: "Rule",
task_status: "Status",
next_execution_time: "Next Execution Time",
create_user: "Creator",
update_time: "Update time",
},
scenario_schedule: "Scenario",
test_plan_schedule: "Test plan",
confirm: {
close_title: "Do you want to close this scheduled task",
}

View File

@ -922,12 +922,15 @@ export default {
table_coloum: {
index: "序号",
scenario: "场景名称",
task_type: "任务类型",
run_rule: "运行规则",
task_status: "任务状态",
next_execution_time: "下次执行时间",
create_user: "创建人",
update_time: "更新时间",
},
scenario_schedule: "场景",
test_plan_schedule: "测试计划",
confirm: {
close_title: "要关闭这条定时任务吗?",
}

View File

@ -920,12 +920,15 @@ export default {
table_coloum: {
index: "序號",
scenario: "場景名稱",
task_type: "任務類型",
run_rule: "運行規則",
task_status: "任務狀態",
next_execution_time: "下次執行時間",
create_user: "創建人",
update_time: "更新時間",
},
scenario_schedule: "場景",
test_plan_schedule: "測試計畫",
confirm: {
close_title: "要關閉這條定時任務嗎?",
}