Merge branch 'master' of https://github.com/metersphere/metersphere
This commit is contained in:
commit
28fa069390
|
@ -121,6 +121,14 @@ public class MsScenario extends MsTestElement {
|
|||
variables.stream().filter(ScenarioVariable::isConstantValid).forEach(keyValue ->
|
||||
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
|
||||
);
|
||||
|
||||
List<ScenarioVariable> variableList = variables.stream().filter(ScenarioVariable::isListValid).collect(Collectors.toList());
|
||||
variableList.forEach(item -> {
|
||||
String[] arrays = item.getValue().split(",");
|
||||
for (int i = 0; i < arrays.length; i++) {
|
||||
arguments.addArgument(item.getName() + "_" + (i + 1), arrays[i], "=");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (config != null && config.getConfig() != null && config.getConfig().getCommonConfig() != null
|
||||
&& CollectionUtils.isNotEmpty(config.getConfig().getCommonConfig().getVariables())) {
|
||||
|
|
|
@ -41,8 +41,7 @@ public class ScenarioVariable {
|
|||
private String maxNumber;
|
||||
|
||||
public boolean isConstantValid() {
|
||||
if ((StringUtils.equals(this.type, VariableTypeConstants.CONSTANT.name())
|
||||
|| StringUtils.equals(this.type, VariableTypeConstants.LIST.name())) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
|
||||
if (StringUtils.equals(this.type, VariableTypeConstants.CONSTANT.name()) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -55,6 +54,13 @@ public class ScenarioVariable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isListValid() {
|
||||
if (StringUtils.equals(this.type, VariableTypeConstants.LIST.name()) && StringUtils.isNotEmpty(name) && StringUtils.isNotEmpty(value) && value.indexOf(",") != -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isCounterValid() {
|
||||
if (StringUtils.equals(this.type, VariableTypeConstants.COUNTER.name()) && StringUtils.isNotEmpty(name)) {
|
||||
return true;
|
||||
|
|
|
@ -11,4 +11,5 @@ public interface ExtTestPlanLoadCaseMapper {
|
|||
List<String> selectIdsNotInPlan(@Param("projectId") String projectId, @Param("planId") String planId);
|
||||
List<TestPlanLoadCaseDTO> selectTestPlanLoadCaseList(@Param("request") LoadCaseRequest request);
|
||||
void updateCaseStatus(@Param("reportId") String reportId, @Param("status") String status);
|
||||
List<String> getStatusByTestPlanId(@Param("planId") String planId);
|
||||
}
|
||||
|
|
|
@ -62,4 +62,7 @@
|
|||
</foreach>
|
||||
</if>
|
||||
</select>
|
||||
<select id="getStatusByTestPlanId" resultType="java.lang.String">
|
||||
select status from test_plan_load_case tplc where tplc.test_plan_id = #{planId}
|
||||
</select>
|
||||
</mapper>
|
|
@ -19,10 +19,7 @@ import io.metersphere.api.jmeter.JMeterService;
|
|||
import io.metersphere.api.service.ApiAutomationService;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.*;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
|
@ -103,6 +100,10 @@ public class TestPlanService {
|
|||
private JMeterService jMeterService;
|
||||
@Resource
|
||||
private ApiAutomationService apiAutomationService;
|
||||
@Resource
|
||||
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
|
||||
@Resource
|
||||
private ExtTestPlanLoadCaseMapper extTestPlanLoadCaseMapper;
|
||||
|
||||
public synchronized void addTestPlan(AddTestPlanRequest testPlan) {
|
||||
if (getTestPlanByName(testPlan.getName()).size() > 0) {
|
||||
|
@ -550,18 +551,10 @@ public class TestPlanService {
|
|||
}
|
||||
|
||||
public void editTestPlanStatus(String planId) {
|
||||
List<String> statusList = extTestPlanTestCaseMapper.getStatusByPlanId(planId);
|
||||
TestPlan testPlan = new TestPlan();
|
||||
testPlan.setId(planId);
|
||||
for (String status : statusList) {
|
||||
if (StringUtils.equals(status, TestPlanTestCaseStatus.Prepare.name())
|
||||
|| StringUtils.equals(status, TestPlanTestCaseStatus.Underway.name())) {
|
||||
testPlan.setStatus(TestPlanStatus.Underway.name());
|
||||
testPlanMapper.updateByPrimaryKeySelective(testPlan);
|
||||
return;
|
||||
}
|
||||
}
|
||||
testPlan.setStatus(TestPlanStatus.Completed.name());
|
||||
String status = calcTestPlanStatus(planId);
|
||||
testPlan.setStatus(status);
|
||||
testPlanMapper.updateByPrimaryKeySelective(testPlan);
|
||||
TestPlan testPlans = getTestPlan(planId);
|
||||
List<String> userIds = new ArrayList<>();
|
||||
|
@ -590,6 +583,36 @@ public class TestPlanService {
|
|||
|
||||
}
|
||||
|
||||
|
||||
private String calcTestPlanStatus(String planId) {
|
||||
// test-plan-functional-case status
|
||||
List<String> funcStatusList = extTestPlanTestCaseMapper.getStatusByPlanId(planId);
|
||||
for (String funcStatus : funcStatusList) {
|
||||
if (StringUtils.equals(funcStatus, TestPlanTestCaseStatus.Prepare.name())
|
||||
|| StringUtils.equals(funcStatus, TestPlanTestCaseStatus.Underway.name())) {
|
||||
return TestPlanStatus.Underway.name();
|
||||
}
|
||||
}
|
||||
|
||||
// test-plan-api-case status
|
||||
List<String> apiStatusList = extTestPlanApiCaseMapper.getStatusByTestPlanId(planId);
|
||||
for (String apiStatus : apiStatusList) {
|
||||
if (apiStatus == null) {
|
||||
return TestPlanStatus.Underway.name();
|
||||
}
|
||||
}
|
||||
|
||||
// test-plan-load-case status
|
||||
List<String> loadStatusList = extTestPlanLoadCaseMapper.getStatusByTestPlanId(planId);
|
||||
for (String loadStatus : loadStatusList) {
|
||||
if (loadStatus == null) {
|
||||
return TestPlanStatus.Underway.name();
|
||||
}
|
||||
}
|
||||
|
||||
return TestPlanStatus.Completed.name();
|
||||
}
|
||||
|
||||
public String getProjectNameByPlanId(String testPlanId) {
|
||||
List<String> projectIds = testPlanProjectService.getProjectIdsByPlanId(testPlanId);
|
||||
ProjectExample projectExample = new ProjectExample();
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
highlight-current
|
||||
@node-expand="nodeExpand"
|
||||
@node-collapse="nodeCollapse"
|
||||
:allow-drop="allowDrop" @node-drag-end="allowDrag" @node-click="nodeClick" v-if="!loading" draggable>
|
||||
:allow-drop="allowDrop" @node-drag-end="allowDrag" @node-click="nodeClick" v-if="!loading" draggable class="ms-is-leaf">
|
||||
<span class="custom-tree-node father" slot-scope="{ node, data}" style="width: 96%">
|
||||
<!-- 步骤组件-->
|
||||
<ms-component-config :type="data.type" :scenario="data" :response="response" :currentScenario="currentScenario"
|
||||
|
@ -1035,7 +1035,7 @@
|
|||
color: #7C3985;
|
||||
}
|
||||
|
||||
/deep/ .is-leaf {
|
||||
.ms-is-leaf >>> .is-leaf {
|
||||
color: transparent;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,96 +1,97 @@
|
|||
<template>
|
||||
<div>
|
||||
<ms-run :debug="true" :environment="currentEnvironmentId" :reportId="reportId" :run-data="debugData"
|
||||
@runRefresh="runRefresh" ref="runTest"/>
|
||||
<api-base-component
|
||||
@copy="copyRow"
|
||||
@remove="remove"
|
||||
:data="controller"
|
||||
:draggable="true"
|
||||
color="#02A7F0"
|
||||
background-color="#F4F4F5"
|
||||
:title="$t('api_test.automation.loop_controller')" v-loading="loading">
|
||||
|
||||
<api-base-component
|
||||
@copy="copyRow"
|
||||
@remove="remove"
|
||||
:data="controller"
|
||||
:draggable="true"
|
||||
color="#02A7F0"
|
||||
background-color="#F4F4F5"
|
||||
:title="$t('api_test.automation.loop_controller')" v-loading="loading">
|
||||
<template v-slot:headerLeft>
|
||||
<i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}" @click="active(controller)" style="margin-right: 10px"/>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="LOOP_COUNT">{{$t('loop.loops_title')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="FOREACH">{{$t('loop.foreach')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio>
|
||||
</template>
|
||||
|
||||
<template v-slot:headerLeft>
|
||||
<i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}" @click="active(controller)" style="margin-right: 10px"/>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="LOOP_COUNT">{{$t('loop.loops_title')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="FOREACH">{{$t('loop.foreach')}}</el-radio>
|
||||
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio>
|
||||
</template>
|
||||
|
||||
<template v-slot:message>
|
||||
<template v-slot:message>
|
||||
<span v-if="requestResult && requestResult.scenarios && requestResult.scenarios.length > 0 " style="color: #8c939d;margin-right: 10px">
|
||||
循环{{requestResult.scenarios.length}}次 成功{{success}}次 失败{{error}}次
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-slot:button>
|
||||
<el-button @click="runDebug" :tip="$t('api_test.run')" icon="el-icon-video-play" style="background-color: #409EFF;color: white;" size="mini" circle/>
|
||||
</template>
|
||||
<div v-if="controller.loopType==='LOOP_COUNT'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.loops')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.loops" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">次</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.proceed')}}</span>
|
||||
<el-tooltip class="item" effect="dark" content="默认为开启,当循环下只有一个请求时,可以开启/关闭;当循环下超过一个请求时,则只能开启。" placement="top">>
|
||||
<el-switch v-model="controller.countController.proceed" @change="switchChange"/>
|
||||
<template v-slot:button>
|
||||
<el-button @click="runDebug" :tip="$t('api_test.run')" icon="el-icon-video-play" style="background-color: #409EFF;color: white;" size="mini" circle/>
|
||||
</template>
|
||||
<div v-if="controller.loopType==='LOOP_COUNT'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.loops')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.loops" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0"/>
|
||||
<span class="ms-span ms-radio">次</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.countController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<span class="ms-span ms-radio">{{$t('loop.proceed')}}</span>
|
||||
<el-tooltip class="item" effect="dark" content="默认为开启,当循环下只有一个请求时,可以开启/关闭;当循环下超过一个请求时,则只能开启。" placement="top">>
|
||||
<el-switch v-model="controller.countController.proceed" @change="switchChange"/>
|
||||
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div v-else-if="controller.loopType==='FOREACH'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-input :placeholder="$t('api_test.request.condition_variable')" v-model="controller.forEachController.inputVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="1" style="margin-top: 6px">
|
||||
<span style="margin:10px 10px 10px">in</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-input :placeholder="$t('api_test.request.condition_variable')" v-model="controller.forEachController.returnVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.forEachController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else draggable>
|
||||
<el-input size="small" v-model="controller.whileController.variable" style="width: 20%" :placeholder="$t('api_test.request.condition_variable')"/>
|
||||
<div v-else-if="controller.loopType==='FOREACH'" draggable>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-input placeholder="输出变量名称" v-model="controller.forEachController.returnVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="1" style="margin-top: 6px">
|
||||
<span style="margin:10px 10px 10px">in</span>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-input placeholder="输入变量前缀" v-model="controller.forEachController.inputVal" size="small"/>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<span class="ms-span ms-radio">{{$t('loop.interval')}}</span>
|
||||
<el-input-number size="small" v-model="controller.forEachController.interval" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="0" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<div v-else draggable>
|
||||
<el-input size="small" v-model="controller.whileController.variable" style="width: 20%" :placeholder="$t('api_test.request.condition_variable')"/>
|
||||
|
||||
<el-select v-model="controller.whileController.operator" :placeholder="$t('commons.please_select')" size="small"
|
||||
@change="change" style="width: 10%;margin-left: 10px">
|
||||
<el-option v-for="o in operators" :key="o.value" :label="$t(o.label)" :value="o.value"/>
|
||||
</el-select>
|
||||
<el-input size="small" v-model="controller.whileController.value" :placeholder="$t('api_test.value')" v-if="!hasEmptyOperator" style="width: 20%;margin-left: 20px"/>
|
||||
<span class="ms-span ms-radio">{{$t('loop.timeout')}}</span>
|
||||
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="1" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</div>
|
||||
<el-select v-model="controller.whileController.operator" :placeholder="$t('commons.please_select')" size="small"
|
||||
@change="change" style="width: 10%;margin-left: 10px">
|
||||
<el-option v-for="o in operators" :key="o.value" :label="$t(o.label)" :value="o.value"/>
|
||||
</el-select>
|
||||
<el-input size="small" v-model="controller.whileController.value" :placeholder="$t('api_test.value')" v-if="!hasEmptyOperator" style="width: 20%;margin-left: 20px"/>
|
||||
<span class="ms-span ms-radio">{{$t('loop.timeout')}}</span>
|
||||
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="1" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</div>
|
||||
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in requestResult.scenarios" :key="index">
|
||||
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
|
||||
<api-response-component :result="result"/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in requestResult.scenarios" :key="index">
|
||||
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
|
||||
<api-response-component :result="result"/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<ms-run :debug="true" :environment="currentEnvironmentId" :reportId="reportId" :run-data="debugData"
|
||||
@runRefresh="runRefresh" ref="runTest"/>
|
||||
</api-base-component>
|
||||
|
||||
</api-base-component>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -199,8 +200,8 @@
|
|||
this.$warning("当前循环下没有请求,不能执行")
|
||||
return;
|
||||
}
|
||||
this.controller.active = true;
|
||||
this.loading = true;
|
||||
|
||||
this.debugData = {
|
||||
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
|
||||
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare,
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<el-col :span="20">
|
||||
<el-autocomplete
|
||||
size="small"
|
||||
class="inline-input"
|
||||
style="width: 100%"
|
||||
v-model="editData.encoding"
|
||||
:fetch-suggestions="querySearch"
|
||||
:placeholder="$t('commons.input_content')"
|
||||
|
@ -110,7 +110,7 @@
|
|||
},
|
||||
|
||||
handleClick() {
|
||||
let config = {complete: this.complete, step: this.step};
|
||||
let config = {complete: this.complete, step: this.step, delimiter: this.editData.delimiter ? this.editData.delimiter : ","};
|
||||
this.allDatas = [];
|
||||
// 本地文件
|
||||
if (this.editData.files && this.editData.files.length > 0 && this.editData.files[0].file) {
|
||||
|
@ -152,5 +152,7 @@
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
ms-is-leaf >>> .is-leaf {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -107,6 +107,9 @@
|
|||
},
|
||||
addParameters(v) {
|
||||
v.id = getUUID();
|
||||
if (v.type === 'CSV') {
|
||||
v.delimiter = ",";
|
||||
}
|
||||
this.variables.push(v);
|
||||
let index = 1;
|
||||
this.variables.forEach(item => {
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
<template>
|
||||
<div class="metric-container">
|
||||
<el-row type="flex">
|
||||
<el-col>
|
||||
<div style="font-size: 14px;color: #AAAAAA;float: left">请求名称 :</div>
|
||||
<div style="font-size: 14px;color:#61C550;margin-top:2px;margin-left:10px;float: left">{{response.name}}</div>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<div style="font-size: 14px;color: #AAAAAA;float: left">{{$t('api_report.response_code')}} :</div>
|
||||
<div style="font-size: 14px;color:#61C550;margin-top:2px;margin-left:10px;float: left">{{response.responseResult.responseCode ? response.responseResult.responseCode :'0'}}</div>
|
||||
|
|
|
@ -75,6 +75,9 @@
|
|||
<el-tag size="mini" type="success" v-else-if="row.caseStatus === 'success'">
|
||||
{{ row.caseStatus }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" v-else-if="row.caseStatus === 'run'">
|
||||
{{ row.caseStatus }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -264,17 +267,22 @@ export default {
|
|||
title: loadCase.caseName,
|
||||
message: this.$t('test_track.plan.load_case.exec').toString()
|
||||
});
|
||||
this.initTable();
|
||||
this.updateStatus(loadCase, 'run');
|
||||
}).catch(() => {
|
||||
this.$post('/test/plan/load/case/update', {id: loadCase.id, status: "error"}, () => {
|
||||
this.initTable();
|
||||
});
|
||||
this.updateStatus(loadCase, 'error');
|
||||
this.$notify.error({
|
||||
title: loadCase.caseName,
|
||||
message: this.$t('test_track.plan.load_case.error').toString()
|
||||
});
|
||||
})
|
||||
},
|
||||
updateStatus(loadCase, status) {
|
||||
this.$post('/test/plan/load/case/update', {id: loadCase.id, status: status}, () => {
|
||||
this.$post('/test/plan/edit/status/' + loadCase.testPlanId, {},() => {
|
||||
this.initTable();
|
||||
});
|
||||
});
|
||||
},
|
||||
handleDelete(loadCase) {
|
||||
this.result = this.$get('/test/plan/load/case/delete/' + loadCase.id, () => {
|
||||
this.$success(this.$t('test_track.cancel_relevance_success'));
|
||||
|
|
Loading…
Reference in New Issue