Merge branch 'master' of github.com:metersphere/metersphere

This commit is contained in:
chenjianxing 2021-03-15 20:46:53 +08:00
commit 17e9c9dd93
34 changed files with 1727 additions and 115 deletions

View File

@ -215,7 +215,6 @@ public class ApiDefinitionService {
ApiDefinitionExample example = new ApiDefinitionExample(); ApiDefinitionExample example = new ApiDefinitionExample();
if (request.getProtocol().equals(RequestType.HTTP)) { if (request.getProtocol().equals(RequestType.HTTP)) {
example.createCriteria().andMethodEqualTo(request.getMethod()).andStatusNotEqualTo("Trash") example.createCriteria().andMethodEqualTo(request.getMethod()).andStatusNotEqualTo("Trash")
.andProtocolEqualTo(request.getProtocol()).andPathEqualTo(request.getPath())
.andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId()); .andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId());
return apiDefinitionMapper.selectByExample(example); return apiDefinitionMapper.selectByExample(example);
} else { } else {

View File

@ -71,5 +71,10 @@ public interface ExtTestCaseMapper {
List<TrackCountResult> countRelevanceMaintainer(@Param("projectId") String projectId); List<TrackCountResult> countRelevanceMaintainer(@Param("projectId") String projectId);
int getTestPlanBug(@Param("planId") String planId);
int getTestPlanCase(@Param("planId") String planId);
int getTestPlanPassCase(@Param("planId") String planId);
} }

View File

@ -342,8 +342,9 @@
</select> </select>
<select id="countCoverage" resultType="io.metersphere.track.response.TrackCountResult"> <select id="countCoverage" resultType="io.metersphere.track.response.TrackCountResult">
SELECT count(test_case.id) AS countNumber, if(test_case.test_id is null,"uncoverage","coverage") AS groupField SELECT count(test_case.id) AS countNumber,
FROM test_case WHERE test_case.project_id=#{projectId} GROUP BY groupField if(test_case.test_id is null,"uncoverage","coverage") AS groupField
FROM test_case WHERE test_case.project_id=#{projectId} and test_case.type != 'functional' GROUP BY groupField
</select> </select>
<select id="countFuncMaintainer" resultType="io.metersphere.track.response.TrackCountResult"> <select id="countFuncMaintainer" resultType="io.metersphere.track.response.TrackCountResult">
select count(tc.id) as countNumber, user.name as groupField from test_case tc right join user on tc.maintainer = user.id select count(tc.id) as countNumber, user.name as groupField from test_case tc right join user on tc.maintainer = user.id
@ -355,6 +356,50 @@
where tc.project_id = #{projectId} and tc.test_id is not null where tc.project_id = #{projectId} and tc.test_id is not null
group by tc.maintainer group by tc.maintainer
</select> </select>
<select id="getTestPlanBug" resultType="int">
select count(tci.issues_id) from test_plan_test_case tptc join test_case_issues tci on tptc.case_id = tci.test_case_id
where tptc.plan_id = #{planId};
</select>
<select id="getTestPlanCase" resultType="int">
select count(s)
from (
select id as s
from test_plan_test_case tptc
where tptc.plan_id = #{planId}
union all
select id as s
from test_plan_api_scenario tpas
where tpas.test_plan_id = #{planId}
union all
select id as s
from test_plan_api_case tpac
where tpac.test_plan_id = #{planId}
union all
select id as s
from test_plan_load_case tplc
where tplc.test_plan_id = #{planId}
) as temp
</select>
<select id="getTestPlanPassCase" resultType="int">
select count(s)
from (
select id as s
from test_plan_test_case tptc
where tptc.plan_id = #{planId} and tptc.status = 'Pass'
union all
select id as s
from test_plan_api_scenario tpas
where tpas.test_plan_id = #{planId} and tpas.last_result = 'Success'
union all
select id as s
from test_plan_api_case tpac
where tpac.test_plan_id = #{planId} and tpac.status = 'success'
union all
select id as s
from test_plan_load_case tplc
where tplc.test_plan_id = #{planId} and tplc.status = 'success'
) as temp
</select>
</mapper> </mapper>

View File

@ -48,6 +48,7 @@ public class ShiroUtils {
public static void ignoreCsrfFilter(Map<String, String> filterChainDefinitionMap) { public static void ignoreCsrfFilter(Map<String, String> filterChainDefinitionMap) {
filterChainDefinitionMap.put("/", "apikey, authc"); // 跳转到 / 不用校验 csrf filterChainDefinitionMap.put("/", "apikey, authc"); // 跳转到 / 不用校验 csrf
filterChainDefinitionMap.put("/language", "apikey, authc");// 跳转到 /language 不用校验 csrf
filterChainDefinitionMap.put("/document", "apikey, authc"); // 跳转到 /document 不用校验 csrf filterChainDefinitionMap.put("/document", "apikey, authc"); // 跳转到 /document 不用校验 csrf
} }

View File

@ -676,6 +676,13 @@ public class JmeterDocumentParser implements DocumentParser {
((List<?>) durations).remove(0); ((List<?>) durations).remove(0);
duration = o.toString(); duration = o.toString();
} }
Object units = context.getProperty("unit");
String unit = "S";
if (units instanceof List) {
Object o = ((List<?>) units).get(0);
((List<?>) units).remove(0);
unit = o.toString();
}
Object deleteds = context.getProperty("deleted"); Object deleteds = context.getProperty("deleted");
String deleted = "false"; String deleted = "false";
if (deleteds instanceof List) { if (deleteds instanceof List) {
@ -691,6 +698,17 @@ public class JmeterDocumentParser implements DocumentParser {
enabled = o.toString(); enabled = o.toString();
} }
switch (unit) {
case "M":
duration = String.valueOf(Long.parseLong(duration) * 60);
break;
case "H":
duration = String.valueOf(Long.parseLong(duration) * 60 * 60);
break;
default:
break;
}
threadGroup.setAttribute("enabled", enabled); threadGroup.setAttribute("enabled", enabled);
if (BooleanUtils.toBoolean(deleted)) { if (BooleanUtils.toBoolean(deleted)) {
threadGroup.setAttribute("enabled", "false"); threadGroup.setAttribute("enabled", "false");
@ -761,6 +779,13 @@ public class JmeterDocumentParser implements DocumentParser {
((List<?>) holds).remove(0); ((List<?>) holds).remove(0);
hold = o.toString(); hold = o.toString();
} }
Object units = context.getProperty("unit");
String unit = "S";
if (units instanceof List) {
Object o = ((List<?>) units).get(0);
((List<?>) units).remove(0);
unit = o.toString();
}
Object deleteds = context.getProperty("deleted"); Object deleteds = context.getProperty("deleted");
String deleted = "false"; String deleted = "false";
if (deleteds instanceof List) { if (deleteds instanceof List) {
@ -776,6 +801,17 @@ public class JmeterDocumentParser implements DocumentParser {
enabled = o.toString(); enabled = o.toString();
} }
switch (unit) {
case "M":
hold = String.valueOf(Long.parseLong(hold) * 60);
break;
case "H":
hold = String.valueOf(Long.parseLong(hold) * 60 * 60);
break;
default:
break;
}
threadGroup.setAttribute("enabled", enabled); threadGroup.setAttribute("enabled", enabled);
if (BooleanUtils.toBoolean(deleted)) { if (BooleanUtils.toBoolean(deleted)) {
threadGroup.setAttribute("enabled", "false"); threadGroup.setAttribute("enabled", "false");
@ -928,10 +964,10 @@ public class JmeterDocumentParser implements DocumentParser {
} }
private Element createStringProp(Document document, String name, String value) { private Element createStringProp(Document document, String name, String value) {
Element unit = document.createElement(STRING_PROP); Element element = document.createElement(STRING_PROP);
unit.setAttribute("name", name); element.setAttribute("name", name);
unit.appendChild(document.createTextNode(value)); element.appendChild(document.createTextNode(value));
return unit; return element;
} }
private void processThreadGroupName(Element threadGroup) { private void processThreadGroupName(Element threadGroup) {

View File

@ -87,7 +87,7 @@ public class TrackController {
} }
@GetMapping("/bug/count/{projectId}") @GetMapping("/bug/count/{projectId}")
public BugStatustics getBugStatustics(@PathVariable String projectId) { public BugStatustics getBugStatistics(@PathVariable String projectId) {
return trackService.getBugStatustics(projectId); return trackService.getBugStatistics(projectId);
} }
} }

View File

@ -8,7 +8,7 @@ import lombok.Setter;
public class TestPlanBugCount { public class TestPlanBugCount {
private int index; private int index;
private String planName; private String planName;
private long creatTime; private long createTime;
private String status; private String status;
private int caseSize; private int caseSize;
private int bugSize; private int bugSize;

View File

@ -144,20 +144,22 @@ public class TrackStatisticsDTO {
public void countRelevance(List<TrackCountResult> relevanceResults) { public void countRelevance(List<TrackCountResult> relevanceResults) {
for (TrackCountResult countResult : relevanceResults) { for (TrackCountResult countResult : relevanceResults) {
switch (countResult.getGroupField().toUpperCase()){ switch (countResult.getGroupField()){
case TrackCount.API: case TrackCount.API:
this.apiCaseCount += countResult.getCountNumber(); this.apiCaseCount += countResult.getCountNumber();
this.allRelevanceCaseCount += countResult.getCountNumber();
break; break;
case TrackCount.PERFORMANCE: case TrackCount.PERFORMANCE:
this.performanceCaseCount += countResult.getCountNumber(); this.performanceCaseCount += countResult.getCountNumber();
this.allRelevanceCaseCount += countResult.getCountNumber();
break; break;
case TrackCount.AUTOMATION: case TrackCount.AUTOMATION:
this.scenarioCaseCount += countResult.getCountNumber(); this.scenarioCaseCount += countResult.getCountNumber();
this.allRelevanceCaseCount += countResult.getCountNumber();
break; break;
default: default:
break; break;
} }
this.allRelevanceCaseCount += countResult.getCountNumber();
} }
} }

View File

@ -12,6 +12,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -110,59 +111,58 @@ public class TrackService {
return charts; return charts;
} }
public BugStatustics getBugStatustics(String projectId) { public BugStatustics getBugStatistics(String projectId) {
TestPlanExample example = new TestPlanExample(); TestPlanExample example = new TestPlanExample();
example.createCriteria().andProjectIdEqualTo(projectId); example.createCriteria().andProjectIdEqualTo(projectId);
List<TestPlan> plans = testPlanMapper.selectByExample(example); List<TestPlan> plans = testPlanMapper.selectByExample(example);
List<TestPlanBugCount> list = new ArrayList<>(); List<TestPlanBugCount> list = new ArrayList<>();
BugStatustics bugStatustics = new BugStatustics(); BugStatustics bugStatustics = new BugStatustics();
int index = 1; int index = 1;
int totalBugSize = 0;
int totalCaseSize = 0;
for (TestPlan plan : plans) { for (TestPlan plan : plans) {
TestPlanBugCount testPlanBug = new TestPlanBugCount(); TestPlanBugCount testPlanBug = new TestPlanBugCount();
testPlanBug.setIndex(index++); testPlanBug.setIndex(index++);
testPlanBug.setPlanName(plan.getName()); testPlanBug.setPlanName(plan.getName());
testPlanBug.setCreatTime(plan.getCreateTime()); testPlanBug.setCreateTime(plan.getCreateTime());
testPlanBug.setStatus(plan.getStatus()); testPlanBug.setStatus(plan.getStatus());
testPlanBug.setCaseSize(getPlanCaseSize(plan.getId()));
testPlanBug.setBugSize(getPlanBugSize(plan.getId())); int planCaseSize = getPlanCaseSize(plan.getId());
testPlanBug.setPassRage(getPlanPassRage(plan.getId())); testPlanBug.setCaseSize(planCaseSize);
int planBugSize = getPlanBugSize(plan.getId());
testPlanBug.setBugSize(planBugSize);
testPlanBug.setPassRage(getPlanPassRage(plan.getId(), planCaseSize));
list.add(testPlanBug); list.add(testPlanBug);
totalBugSize += planBugSize;
totalCaseSize += planCaseSize;
} }
// todo
bugStatustics.setList(list); bugStatustics.setList(list);
bugStatustics.setRage("1"); float rage =totalCaseSize == 0 ? 0 : (float) totalBugSize * 100 / totalCaseSize;
bugStatustics.setBugTotalSize(2); DecimalFormat df = new DecimalFormat("0.0");
bugStatustics.setRage(df.format(rage) + "%");
bugStatustics.setBugTotalSize(totalBugSize);
return bugStatustics; return bugStatustics;
} }
private int getPlanCaseSize(String planId) { private int getPlanCaseSize(String planId) {
TestPlanTestCaseExample testPlanTestCaseExample = new TestPlanTestCaseExample(); return extTestCaseMapper.getTestPlanCase(planId);
testPlanTestCaseExample.createCriteria().andPlanIdEqualTo(planId);
List<TestPlanTestCase> testPlanTestCases = testPlanTestCaseMapper.selectByExample(testPlanTestCaseExample);
TestPlanApiCaseExample testPlanApiCaseExample = new TestPlanApiCaseExample();
testPlanApiCaseExample.createCriteria().andTestPlanIdEqualTo(planId);
List<TestPlanApiCase> testPlanApiCases = testPlanApiCaseMapper.selectByExample(testPlanApiCaseExample);
TestPlanLoadCaseExample example = new TestPlanLoadCaseExample();
example.createCriteria().andTestPlanIdEqualTo(planId);
List<TestPlanLoadCase> testPlanLoadCases = testPlanLoadCaseMapper.selectByExample(example);
TestPlanApiScenarioExample testPlanApiScenarioExample = new TestPlanApiScenarioExample();
testPlanApiCaseExample.createCriteria().andTestPlanIdEqualTo(planId);
List<TestPlanApiScenario> testPlanApiScenarios = testPlanApiScenarioMapper.selectByExample(testPlanApiScenarioExample);
return testPlanTestCases.size() + testPlanApiCases.size() + testPlanLoadCases.size() + testPlanApiScenarios.size();
} }
private int getPlanBugSize(String planId) { private int getPlanBugSize(String planId) {
return 1; return extTestCaseMapper.getTestPlanBug(planId);
} }
private String getPlanPassRage(String planId) { private String getPlanPassRage(String planId, int totalSize) {
return "10%"; if (totalSize == 0) {
return "-";
}
int passSize = extTestCaseMapper.getTestPlanPassCase(planId);
float rage = (float) passSize * 100 / totalSize;
DecimalFormat df = new DecimalFormat("0.0");
return df.format(rage) + "%";
} }
} }

View File

@ -114,12 +114,13 @@
<el-col :span="3" class="ms-col-one ms-font"> <el-col :span="3" class="ms-col-one ms-font">
<el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox> <el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox>
</el-col> </el-col>
<el-col :span="7"> <el-col :span="6">
<env-popover :env-map="projectEnvMap" :project-ids="projectIds" @setProjectEnvMap="setProjectEnvMap" <env-popover :env-map="projectEnvMap" :project-ids="projectIds" @setProjectEnvMap="setProjectEnvMap"
:project-list="projectList" ref="envPopover"/> :project-list="projectList" ref="envPopover"/>
</el-col> </el-col>
<el-col :span="2"> <el-col :span="3">
<el-button :disabled="scenarioDefinition.length < 1" size="small" type="primary" v-prevent-re-click @click="runDebug">{{$t('api_test.request.debug')}}</el-button> <el-button :disabled="scenarioDefinition.length < 1" size="small" type="primary" v-prevent-re-click @click="runDebug">{{$t('api_test.request.debug')}}</el-button>
<font-awesome-icon class="alt-ico" :icon="['fa', 'expand-alt']" size="lg" @click="fullScreen"/>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
@ -192,6 +193,17 @@
class="ms-sc-variable-header"/> class="ms-sc-variable-header"/>
<!--外部导入--> <!--外部导入-->
<api-import v-if="type!=='detail'" ref="apiImport" :saved="false" @refresh="apiImport"/> <api-import v-if="type!=='detail'" ref="apiImport" :saved="false" @refresh="apiImport"/>
<!--步骤最大化-->
<ms-drawer :visible="drawer" :size="100" @close="close" direction="right" :show-full-screen="false" :is-show-close="false" style="overflow: hidden">
<template v-slot:header>
<scenario-header :currentScenario="currentScenario" :projectEnvMap="projectEnvMap" :projectIds="projectIds" :projectList="projectList" :scenarioDefinition="scenarioDefinition" :enableCookieShare="enableCookieShare"
@closePage="close" @showAllBtn="showAllBtn" @runDebug="runDebug" @showScenarioParameters="showScenarioParameters" ref="maximizeHeader"/>
</template>
<maximize-scenario :scenario-definition="scenarioDefinition" :moduleOptions="moduleOptions" :currentScenario="currentScenario" :type="type" ref="maximizeScenario"/>
</ms-drawer>
</div> </div>
</el-card> </el-card>
</template> </template>
@ -224,6 +236,9 @@
import MsComponentConfig from "./component/ComponentConfig"; import MsComponentConfig from "./component/ComponentConfig";
import {handleCtrlSEvent} from "../../../../../common/js/utils"; import {handleCtrlSEvent} from "../../../../../common/js/utils";
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover"; import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
import MaximizeScenario from "./maximize/MaximizeScenario";
import ScenarioHeader from "./maximize/ScenarioHeader";
import MsDrawer from "../../../common/components/MsDrawer";
let jsonPath = require('jsonpath'); let jsonPath = require('jsonpath');
export default { export default {
@ -243,7 +258,10 @@
MsApiCustomize, MsApiCustomize,
ApiImport, ApiImport,
MsComponentConfig, MsComponentConfig,
EnvPopover EnvPopover,
MaximizeScenario,
ScenarioHeader,
MsDrawer
}, },
data() { data() {
return { return {
@ -293,6 +311,7 @@
projectEnvMap: new Map, projectEnvMap: new Map,
projectList: [], projectList: [],
debugResult: new Map, debugResult: new Map,
drawer: false,
} }
}, },
created() { created() {
@ -423,6 +442,9 @@
} }
}, },
methods: { methods: {
showAllBtn() {
this.$refs.maximizeScenario.showAll();
},
addListener() { addListener() {
document.addEventListener("keydown", this.createCtrlSHandle); document.addEventListener("keydown", this.createCtrlSHandle);
// document.addEventListener("keydown", (even => handleCtrlSEvent(even, this.$refs.httpApi.saveApi))); // document.addEventListener("keydown", (even => handleCtrlSEvent(even, this.$refs.httpApi.saveApi)));
@ -443,6 +465,7 @@
// //
this.editScenario(); this.editScenario();
} }
this.$refs.maximizeHeader.getVariableSize();
this.reload(); this.reload();
}, },
showButton(...names) { showButton(...names) {
@ -697,8 +720,7 @@
} }
this.sort(); this.sort();
this.reload(); this.reload();
} },
,
reload() { reload() {
this.loading = true this.loading = true
this.$nextTick(() => { this.$nextTick(() => {
@ -1046,7 +1068,14 @@
// //
this.debugResult = result; this.debugResult = result;
this.sort() this.sort()
},
fullScreen() {
this.drawer = true;
},
close() {
this.drawer = false;
} }
} }
} }
</script> </script>
@ -1112,7 +1141,7 @@
} }
/deep/ .el-card__body { /deep/ .el-card__body {
padding: 15px; padding: 10px;
} }
/deep/ .el-drawer__body { /deep/ .el-drawer__body {
@ -1182,4 +1211,17 @@
.ms-sc-variable-header >>> .el-dialog__body { .ms-sc-variable-header >>> .el-dialog__body {
padding: 0px 20px; padding: 0px 20px;
} }
.alt-ico {
font-size: 15px;
margin: 0px 10px 0px;
color: #8c939d;
}
.alt-ico:hover {
color: black;
cursor: pointer;
font-size: 18px;
}
</style> </style>

View File

@ -16,6 +16,9 @@ export const ELEMENTS = new Map([
['Extract', []], ['Extract', []],
['JmeterElement', []], ['JmeterElement', []],
['CustomizeReq', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]], ['CustomizeReq', ["ConstantTimer", "JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
['MaxSamplerProxy', ["JSR223PreProcessor", "JSR223PostProcessor", "Assertions", "Extract"]],
['AllSamplerProxy', ["HTTPSamplerProxy", "DubboSampler", "JDBCSampler", "TCPSampler"]],
]) ])
export const ELEMENT_TYPE = { export const ELEMENT_TYPE = {

View File

@ -1,47 +1,46 @@
<template> <template>
<el-card class="api-component"> <el-card class="api-component">
<div class="header" @click="active(data)"> <div class="header" @click="active(data)">
<slot name="beforeHeaderLeft"> <slot name="beforeHeaderLeft">
<div v-if="data.index" class="el-step__icon is-text" style="margin-right: 10px;" :style="{'color': color, 'background-color': backgroundColor}"> <div v-if="data.index" class="el-step__icon is-text" style="margin-right: 10px;" :style="{'color': color, 'background-color': backgroundColor}">
<div class="el-step__icon-inner">{{data.index}}</div> <div class="el-step__icon-inner">{{data.index}}</div>
</div> </div>
<el-button class="ms-left-buttion" size="small" :style="{'color': color, 'background-color': backgroundColor}">{{title}}</el-button> <el-button class="ms-left-buttion" size="mini" :style="{'color': color, 'background-color': backgroundColor}">{{title}}</el-button>
<el-tag size="mini" v-if="data.method">{{data.method}}</el-tag>
</slot> </slot>
<span @click.stop> <span @click.stop>
<slot name="headerLeft"> <slot name="headerLeft">
<i class="icon el-icon-arrow-right" :class="{'is-active': data.active}" <i class="icon el-icon-arrow-right" :class="{'is-active': data.active}"
@click="active(data)" v-if="data.type!='scenario'"/> @click="active(data)" v-if="data.type!='scenario' && !isMax "/>
<el-input :draggable="draggable" v-if="isShowInput && isShowNameInput" size="small" v-model="data.name" class="name-input" <el-input :draggable="draggable" v-if="isShowInput && isShowNameInput" size="mini" v-model="data.name" class="name-input"
@blur="isShowInput = false" :placeholder="$t('commons.input_name')" ref="nameEdit" :disabled="data.disabled"/> @blur="isShowInput = false" :placeholder="$t('commons.input_name')" ref="nameEdit" :disabled="data.disabled"/>
<span v-else-if="isMax">
<el-tooltip :content="data.name" placement="top">
<span>{{data.name}}</span>
</el-tooltip>
</span>
<span v-else> <span v-else>
{{data.name}} {{data.name}}
<i class="el-icon-edit" style="cursor:pointer" @click="editName" v-tester v-if="data.referenced!='REF' && !data.disabled"/> <i class="el-icon-edit" style="cursor:pointer" @click="editName" v-tester v-if="data.referenced!='REF' && !data.disabled"/>
</span> </span>
</slot> </slot>
<slot name="behindHeaderLeft"></slot> <slot name="behindHeaderLeft" v-if="!isMax"></slot>
</span> </span>
<div class="header-right" @click.stop> <div class="header-right" @click.stop>
<slot name="message"></slot> <slot name="message"></slot>
<el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top"> <el-tooltip :content="$t('test_resource_pool.enable_disable')" placement="top" v-if="showBtn">
<el-switch v-model="data.enable" class="enable-switch"/> <el-switch v-model="data.enable" class="enable-switch"/>
</el-tooltip> </el-tooltip>
<slot name="button"></slot> <slot name="button"></slot>
<el-tooltip content="Copy" placement="top"> <step-extend-btns style="display: contents" @copy="copyRow" @remove="remove" v-if="showBtn"/>
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" :disabled="data && data.disabled"/>
</el-tooltip>
<el-tooltip :content="$t('commons.remove')" placement="top">
<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove" :disabled="data && data.disabled"/>
</el-tooltip>
</div> </div>
</div> </div>
<div class="header"> <div class="header" v-if="!isMax">
<fieldset :disabled="data.disabled" class="ms-fieldset"> <fieldset :disabled="data.disabled" class="ms-fieldset">
<el-collapse-transition> <el-collapse-transition>6.
<div v-if="data.active && showCollapse" :draggable="draggable"> <div v-if="data.active && showCollapse" :draggable="draggable">
<el-divider></el-divider> <el-divider></el-divider>
<slot></slot> <slot></slot>
@ -54,8 +53,11 @@
</template> </template>
<script> <script>
import StepExtendBtns from "../component/StepExtendBtns";
export default { export default {
name: "ApiBaseComponent", name: "ApiBaseComponent",
components: {StepExtendBtns},
data() { data() {
return { return {
isShowInput: false isShowInput: false
@ -63,6 +65,14 @@
}, },
props: { props: {
draggable: Boolean, draggable: Boolean,
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
data: { data: {
type: Object, type: Object,
default() { default() {
@ -104,6 +114,9 @@
this.$refs.nameEdit.focus(); this.$refs.nameEdit.focus();
}); });
} }
if (this.data && this.data.type === "JmeterElement") {
this.data.active = false;
}
}, },
methods: { methods: {
active() { active() {
@ -146,14 +159,26 @@
} }
.header-right { .header-right {
margin-right: 20px; margin-top: 5px;
float: right; float: right;
z-index: 1;
} }
.enable-switch { .enable-switch {
margin-right: 10px; margin-right: 10px;
} }
.node-title {
display: inline-block;
margin: 0px;
overflow-x: hidden;
padding-bottom: 0;
text-overflow: ellipsis;
vertical-align: middle;
white-space: nowrap;
width: 100px;
}
fieldset { fieldset {
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;

View File

@ -9,6 +9,8 @@
:draggable="true" :draggable="true"
:color="displayColor.color" :color="displayColor.color"
:background-color="displayColor.backgroundColor" :background-color="displayColor.backgroundColor"
:is-max="isMax"
:show-btn="showBtn"
:title="displayTitle"> :title="displayTitle">
<template v-slot:behindHeaderLeft> <template v-slot:behindHeaderLeft>
@ -71,6 +73,7 @@
import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiResponseComponent from "./ApiResponseComponent"; import ApiResponseComponent from "./ApiResponseComponent";
import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo"; import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo";
import {ELEMENTS} from "../Setting";
export default { export default {
name: "MsApiComponent", name: "MsApiComponent",
@ -82,6 +85,14 @@
type: Boolean, type: Boolean,
default: false, default: false,
}, },
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
currentEnvironmentId: String, currentEnvironmentId: String,
projectList: Array, projectList: Array,
envMap: Map envMap: Map
@ -122,6 +133,11 @@
} }
} }
} }
// if (this.isMax && this.request && ELEMENTS.get("AllSamplerProxy").indexOf(this.request.type) != -1
// && this.request.hashTree && this.request.hashTree != null && this.request.hashTree.length > 0) {
// this.request.hashTrees = JSON.parse(JSON.stringify(this.request.hashTree));
// this.request.hashTree = undefined;
// }
}, },
computed: { computed: {
displayColor() { displayColor() {
@ -220,6 +236,12 @@
if (!this.request.projectId) { if (!this.request.projectId) {
this.request.projectId = response.data.projectId; this.request.projectId = response.data.projectId;
} }
// if (this.isMax && this.request && ELEMENTS.get("AllSamplerProxy").indexOf(this.request.type) != -1) {
// this.request.hashTrees = [];
// Object.assign(this.request.hashTrees, this.request.hashTree);
// this.request.hashTree = [];
// }
this.reload(); this.reload();
this.sort(); this.sort();
} else { } else {

View File

@ -8,6 +8,8 @@
:show-collapse="false" :show-collapse="false"
:is-show-name-input="!isDeletedOrRef" :is-show-name-input="!isDeletedOrRef"
:is-disabled="true" :is-disabled="true"
:is-max="isMax"
:show-btn="showBtn"
color="#606266" color="#606266"
background-color="#F4F4F5" background-color="#F4F4F5"
:title="$t('api_test.automation.scenario_import')"> :title="$t('api_test.automation.scenario_import')">
@ -36,6 +38,14 @@
props: { props: {
scenario: {}, scenario: {},
node: {}, node: {},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
draggable: { draggable: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@ -1,6 +1,7 @@
<template> <template>
<div class="request-form"> <div class="request-form">
<component :is="component" :scenario="scenario" :controller="scenario" :timer="scenario" :assertions="scenario" :extract="scenario" :jsr223-processor="scenario" :request="scenario" :currentScenario="currentScenario" :currentEnvironmentId="currentEnvironmentId" :node="node" <component :is="component" :isMax="isMax" :show-btn="showBtn"
:scenario="scenario" :controller="scenario" :timer="scenario" :assertions="scenario" :extract="scenario" :jsr223-processor="scenario" :request="scenario" :currentScenario="currentScenario" :currentEnvironmentId="currentEnvironmentId" :node="node"
:draggable="true" :title="title" :color="titleColor" :background-color="backgroundColor" @suggestClick="suggestClick(node)" :response="response" :draggable="true" :title="title" :color="titleColor" :background-color="backgroundColor" @suggestClick="suggestClick(node)" :response="response"
@remove="remove" @copyRow="copyRow" @refReload="refReload" :project-list="projectList" :env-map="envMap"/> @remove="remove" @copyRow="copyRow" @refReload="refReload" :project-list="projectList" :env-map="envMap"/>
</div> </div>
@ -24,6 +25,14 @@
props: { props: {
type: String, type: String,
scenario: {}, scenario: {},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
currentScenario: {}, currentScenario: {},
currentEnvironmentId: String, currentEnvironmentId: String,
response: {}, response: {},

View File

@ -5,6 +5,7 @@
:data="timer" :data="timer"
:draggable="true" :draggable="true"
:show-collapse="false" :show-collapse="false"
:is-max="isMax"
color="#67C23A" color="#67C23A"
background-color="#F2F9EE" background-color="#F2F9EE"
:title="$t('api_test.automation.wait_controller')"> :title="$t('api_test.automation.wait_controller')">
@ -26,6 +27,14 @@
props: { props: {
timer: {}, timer: {},
node: {}, node: {},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
draggable: { draggable: {
type: Boolean, type: Boolean,
default: false, default: false,

View File

@ -5,6 +5,8 @@
:data="controller" :data="controller"
:show-collapse="false" :show-collapse="false"
:draggable="true" :draggable="true"
:is-max="isMax"
:show-btn="showBtn"
color="#E6A23C" color="#E6A23C"
background-color="#FCF6EE" background-color="#FCF6EE"
:title="$t('api_test.automation.if_controller')"> :title="$t('api_test.automation.if_controller')">
@ -33,6 +35,14 @@
props: { props: {
controller: {}, controller: {},
node: {}, node: {},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
index: Object, index: Object,
draggable: { draggable: {
type: Boolean, type: Boolean,

View File

@ -5,6 +5,8 @@
:data="request" :data="request"
:draggable="draggable" :draggable="draggable"
:color="defColor" :color="defColor"
:is-max="isMax"
:show-btn="showBtn"
:background-color="defBackgroundColor" :background-color="defBackgroundColor"
:title="request.elementType"> :title="request.elementType">
@ -31,6 +33,14 @@
default: default:
false false
}, },
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
request: { request: {
type: Object, type: Object,
}, },

View File

@ -6,6 +6,8 @@
:data="jsr223Processor" :data="jsr223Processor"
:draggable="draggable" :draggable="draggable"
:color="color" :color="color"
:is-max="isMax"
:show-btn="showBtn"
:background-color="backgroundColor" :background-color="backgroundColor"
:title="title" v-loading="loading"> :title="title" v-loading="loading">
@ -33,6 +35,14 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
isReadOnly: { isReadOnly: {
type: Boolean, type: Boolean,
default: default:

View File

@ -7,12 +7,14 @@
@remove="remove" @remove="remove"
:data="controller" :data="controller"
:draggable="true" :draggable="true"
:is-max="isMax"
:show-btn="showBtn"
color="#02A7F0" color="#02A7F0"
background-color="#F4F4F5" background-color="#F4F4F5"
:title="$t('api_test.automation.loop_controller')" v-loading="loading"> :title="$t('api_test.automation.loop_controller')" v-loading="loading">
<template v-slot:headerLeft> <template v-slot:headerLeft>
<i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}" @click="active(controller)" style="margin-right: 10px"/> <i class="icon el-icon-arrow-right" :class="{'is-active': controller.active}" @click="active(controller)" style="margin-right: 10px" v-if="!isMax"/>
<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="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="FOREACH">{{$t('loop.foreach')}}</el-radio>
<el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio> <el-radio @change="changeRadio" class="ms-radio" v-model="controller.loopType" label="WHILE">{{$t('loop.while')}}</el-radio>
@ -97,6 +99,14 @@ export default {
currentEnvironmentId: String, currentEnvironmentId: String,
currentScenario: {}, currentScenario: {},
node: {}, node: {},
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
index: Object, index: Object,
draggable: { draggable: {
type: Boolean, type: Boolean,

View File

@ -0,0 +1,98 @@
<template>
<div>
<el-dropdown @command="handleCommand" class="scenario-ext-btn">
<el-link type="primary" :underline="false">
<el-icon class="el-icon-more"></el-icon>
</el-link>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="copy">复制步骤</el-dropdown-item>
<el-dropdown-item command="remove" v-tester>删除步骤</el-dropdown-item>
<el-dropdown-item command="scenarioVar" v-tester>查看场景变量</el-dropdown-item>
<el-dropdown-item command="openScenario" v-tester>打开场景</el-dropdown-item>
<el-dropdown-item command="saveAs" v-tester>另存为接口定义</el-dropdown-item>
<!--<el-tooltip content="Copy" placement="top">-->
<!--<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" :disabled="data && data.disabled"/>-->
<!--</el-tooltip>-->
<!--<el-tooltip :content="$t('commons.remove')" placement="top">-->
<!--<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove" :disabled="data && data.disabled"/>-->
<!--</el-tooltip>-->
</el-dropdown-menu>
</el-dropdown>
<ms-reference-view @openScenario="openScenario" ref="viewRef"/>
<ms-schedule-maintain ref="scheduleMaintain" @refreshTable="refreshTable"/>
</div>
</template>
<script>
import MsReferenceView from "@/business/components/api/automation/scenario/ReferenceView";
import MsScheduleMaintain from "@/business/components/api/automation/schedule/ScheduleMaintain"
import {getCurrentProjectID, getUUID} from "@/common/js/utils";
export default {
name: "StepExtendBtns",
components: {MsReferenceView, MsScheduleMaintain},
props: {
row: Object
},
methods: {
handleCommand(cmd) {
switch (cmd) {
case "copy":
this.$emit('copy');
break;
case "remove":
this.$emit('remove');
break;
case "scenarioVar":
this.$emit('copy');
break;
case "openScenario":
this.$emit('copy');
break;
case "saveAs":
this.$emit('copy');
break;
}
},
createPerformance(row) {
this.infoDb = false;
let url = "/api/automation/genPerformanceTestJmx";
let run = {};
let scenarioIds = [];
scenarioIds.push(row.id);
run.projectId = getCurrentProjectID();
run.ids = scenarioIds;
run.id = getUUID();
run.name = row.name;
this.$post(url, run, response => {
let jmxObj = {};
jmxObj.name = response.data.name;
jmxObj.xml = response.data.xml;
jmxObj.attachFiles = response.data.attachFiles;
jmxObj.attachByteFiles = response.data.attachByteFiles;
this.$store.commit('setTest', {
name: row.name,
jmx: jmxObj
})
this.$router.push({
path: "/performance/test/create"
})
});
},
openScenario(item) {
this.$emit('openScenario', item)
},
refreshTable() {
}
}
}
</script>
<style scoped>
.scenario-ext-btn {
margin-left: 10px;
}
</style>

View File

@ -0,0 +1,146 @@
<template>
<div class="ms-header" @click="showAll">
<el-row>
<div class="ms-div" v-loading="loading">
<!-- 调试部分 -->
<el-row style="margin: 5px">
<el-col :span="6" class="ms-col-one ms-font">
{{$t('api_test.automation.step_total')}}{{scenarioDefinition.length}}
</el-col>
<el-col :span="6" class="ms-col-one ms-font">
<el-link class="head" @click="showScenarioParameters">{{$t('api_test.automation.scenario_total')}}</el-link>
{{varSize }}
</el-col>
<el-col :span="5" class="ms-col-one ms-font">
<el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox>
</el-col>
<el-col :span="7">
<env-popover :env-map="envMap" :project-ids="projectIds" @setProjectEnvMap="setProjectEnvMap"
:project-list="projectList" ref="envPopover" style="margin-top: 0px"/>
</el-col>
</el-row>
</div>
<div class="ms-header-right">
<el-button :disabled="scenarioDefinition.length < 1" size="small" type="primary" v-prevent-re-click @click="runDebug">{{$t('api_test.request.debug')}}</el-button>
<i class="el-icon-close alt-ico" @click="close"/>
</div>
</el-row>
</div>
</template>
<script>
import {checkoutTestManagerOrTestUser, exportPdf} from "@/common/js/utils";
import html2canvas from 'html2canvas';
import EnvPopover from "../../scenario/EnvPopover";
export default {
name: "ScenarioHeader",
components: {EnvPopover},
props: {currentScenario: {}, scenarioDefinition: Array, enableCookieShare: Boolean, projectEnvMap: Map, projectIds: Set, projectList: Array},
data() {
return {
envMap: new Map,
loading: false,
varSize: 0,
}
},
mounted() {
this.envMap = this.projectEnvMap;
this.getVariableSize();
},
methods: {
handleExport() {
let name = this.$t('commons.report_statistics.test_case_analysis');
this.$nextTick(function () {
setTimeout(() => {
html2canvas(document.getElementById('reportAnalysis'), {
scale: 2
}).then(function (canvas) {
exportPdf(name, [canvas]);
});
}, 1000);
});
},
showAll() {
this.$emit('showAllBtn');
},
close() {
this.$emit('closePage');
},
runDebug() {
this.$emit('runDebug');
},
showScenarioParameters() {
this.$emit('showScenarioParameters');
},
getVariableSize() {
let size = 0;
if (this.currentScenario.variables) {
size += this.currentScenario.variables.length;
}
if (this.currentScenario.headers && this.currentScenario.headers.length > 1) {
size += this.currentScenario.headers.length - 1;
}
this.varSize = size;
this.reload();
},
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
setProjectEnvMap(projectEnvMap) {
this.envMap = projectEnvMap;
}
},
}
</script>
<style scoped>
.ms-header {
border-bottom: 1px solid #E6E6E6;
height: 65px;
background-color: #FFF;
}
.ms-div {
float: left;
width: 80%;
margin-left: 20px;
margin-top: 12px;
}
.ms-span {
margin: 0px 10px 10px;
}
.ms-header-right {
float: right;
margin-bottom: 10px;
margin-top: 10px;
margin-right: 20px;
}
.alt-ico {
font-size: 18px;
margin: 10px 0px 0px;
}
.alt-ico:hover {
color: black;
cursor: pointer;
font-size: 18px;
}
.ms-font {
color: #303133;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
font-size: 13px;
}
.ms-col-one {
margin-top: 5px;
}
</style>

View File

@ -6,6 +6,8 @@
@active="active" @active="active"
:data="assertions" :data="assertions"
:draggable="draggable" :draggable="draggable"
:is-max="isMax"
:show-btn="showBtn"
color="#A30014" color="#A30014"
background-color="#F7E6E9" background-color="#F7E6E9"
:title="$t('api_test.definition.request.assertions_rule')"> :title="$t('api_test.definition.request.assertions_rule')">
@ -87,6 +89,14 @@
type: Boolean, type: Boolean,
default: false, default: false,
}, },
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
assertions: {}, assertions: {},
node: {}, node: {},
request: {}, request: {},

View File

@ -5,6 +5,8 @@
@active="active" @active="active"
:data="extract" :data="extract"
:draggable="draggable" :draggable="draggable"
:is-max="isMax"
:show-btn="showBtn"
color="#015478" color="#015478"
background-color="#E6EEF2" background-color="#E6EEF2"
:title="$t('api_test.definition.request.extract_param')"> :title="$t('api_test.definition.request.extract_param')">
@ -41,26 +43,26 @@
</template> </template>
<script> <script>
import {EXTRACT_TYPE} from "../../model/ApiTestModel"; import {EXTRACT_TYPE} from "../../model/ApiTestModel";
import MsApiExtractEdit from "./ApiExtractEdit"; import MsApiExtractEdit from "./ApiExtractEdit";
import MsApiExtractCommon from "./ApiExtractCommon"; import MsApiExtractCommon from "./ApiExtractCommon";
import {getUUID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton"; import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton";
import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest"; import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest";
import {ExtractJSONPath} from "../../../test/model/ScenarioModel"; import {ExtractJSONPath} from "../../../test/model/ScenarioModel";
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent"; import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
export default { export default {
name: "MsApiExtract", name: "MsApiExtract",
components: { components: {
ApiBaseComponent, ApiBaseComponent,
MsApiJsonpathSuggest, MsApiJsonpathSuggest,
ApiJsonPathSuggestButton, ApiJsonPathSuggestButton,
MsApiExtractCommon, MsApiExtractCommon,
MsApiExtractEdit, MsApiExtractEdit,
}, },
props: { props: {
extract: {}, extract: {},
response: {}, response: {},
node: {}, node: {},
customizeStyle: { customizeStyle: {
@ -74,7 +76,15 @@ export default {
draggable: { draggable: {
type: Boolean, type: Boolean,
default: false, default: false,
} },
isMax: {
type: Boolean,
default: false,
},
showBtn: {
type: Boolean,
default: true,
},
}, },
data() { data() {
return { return {

View File

@ -37,6 +37,13 @@
@change="calculateChart(threadGroup)" @change="calculateChart(threadGroup)"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item>
<el-radio-group v-model="threadGroup.unit" :disabled="true">
<el-radio label="S">{{ $t('schedule.cron.seconds') }}</el-radio>
<el-radio label="M">{{ $t('schedule.cron.minutes') }}</el-radio>
<el-radio label="H">{{ $t('schedule.cron.hours') }}</el-radio>
</el-radio-group>
</el-form-item>
<br> <br>
<el-form-item :label="$t('load_test.rps_limit')"> <el-form-item :label="$t('load_test.rps_limit')">
<el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/> <el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/>
@ -59,7 +66,7 @@
@change="calculateChart(threadGroup)" @change="calculateChart(threadGroup)"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_minutes')"> <el-form-item :label="$t('load_test.ramp_up_time_minutes', [getUnitLabel(threadGroup)])">
<el-input-number <el-input-number
:disabled="true" :disabled="true"
:min="1" :min="1"
@ -79,7 +86,7 @@
v-model="threadGroup.rampUpTime" v-model="threadGroup.rampUpTime"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_seconds')"/> <el-form-item :label="$t('load_test.ramp_up_time_seconds', [getUnitLabel(threadGroup)])"/>
</div> </div>
</div> </div>
@ -112,7 +119,7 @@
@change="calculateChart(threadGroup)" @change="calculateChart(threadGroup)"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_seconds')"/> <el-form-item :label="$t('load_test.ramp_up_time_seconds', [getUnitLabel(threadGroup)])"/>
</div> </div>
</el-form> </el-form>
</el-col> </el-col>
@ -137,6 +144,7 @@ const TARGET_LEVEL = "TargetLevel";
const RAMP_UP = "RampUp"; const RAMP_UP = "RampUp";
const STEPS = "Steps"; const STEPS = "Steps";
const DURATION = "duration"; const DURATION = "duration";
const UNIT = "unit";
const RPS_LIMIT = "rpsLimit"; const RPS_LIMIT = "rpsLimit";
const RPS_LIMIT_ENABLE = "rpsLimitEnable"; const RPS_LIMIT_ENABLE = "rpsLimitEnable";
const THREAD_TYPE = "threadType"; const THREAD_TYPE = "threadType";
@ -196,11 +204,10 @@ export default {
this.threadGroups[i].iterateRampUp = item.value; this.threadGroups[i].iterateRampUp = item.value;
break; break;
case DURATION: case DURATION:
if (item.unit) { this.threadGroups[i].duration = item.value;
this.threadGroups[i].duration = item.value; break;
} else { case UNIT:
this.threadGroups[i].duration = item.value * 60; this.threadGroups[i].unit = item.value;
}
break; break;
case STEPS: case STEPS:
this.threadGroups[i].step = item.value; this.threadGroups[i].step = item.value;
@ -506,6 +513,18 @@ export default {
} }
this.calculateTotalChart(); this.calculateTotalChart();
}, },
getUnitLabel(tg) {
if (tg.unit === 'S') {
return this.$t('schedule.cron.seconds');
}
if (tg.unit === 'M') {
return this.$t('schedule.cron.minutes');
}
if (tg.unit === 'H') {
return this.$t('schedule.cron.hours');
}
return this.$t('schedule.cron.seconds');
},
}, },
watch: { watch: {
report: { report: {

View File

@ -467,7 +467,8 @@ export default {
let items = { let items = {
name: name, name: name,
type: 'line', type: 'line',
data: d data: d,
smooth: true
}; };
let seriesArrayNames = seriesArray.map(m => m.name); let seriesArrayNames = seriesArray.map(m => m.name);
if (seriesArrayNames.includes(name)) { if (seriesArrayNames.includes(name)) {

View File

@ -130,7 +130,7 @@
<el-table :data="granularityData"> <el-table :data="granularityData">
<el-table-column property="start" :label="$t('load_test.duration')"> <el-table-column property="start" :label="$t('load_test.duration')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<span>{{ scope.row.start }} - {{ scope.row.end }}</span> <span>{{ scope.row.start }}S - {{ scope.row.end }}S</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column property="granularity" :label="$t('load_test.granularity')"/> <el-table-column property="granularity" :label="$t('load_test.granularity')"/>

View File

@ -47,10 +47,17 @@
:disabled="isReadOnly" :disabled="isReadOnly"
v-model="threadGroup.duration" v-model="threadGroup.duration"
:min="1" :min="1"
:max="172800" :max="99999"
@change="calculateChart(threadGroup)" @change="calculateChart(threadGroup)"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item>
<el-radio-group v-model="threadGroup.unit">
<el-radio label="S">{{ $t('schedule.cron.seconds') }}</el-radio>
<el-radio label="M">{{ $t('schedule.cron.minutes') }}</el-radio>
<el-radio label="H">{{ $t('schedule.cron.hours') }}</el-radio>
</el-radio-group>
</el-form-item>
<br> <br>
<el-form-item :label="$t('load_test.rps_limit')"> <el-form-item :label="$t('load_test.rps_limit')">
<el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/> <el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/>
@ -74,7 +81,7 @@
@change="calculateChart(threadGroup)" @change="calculateChart(threadGroup)"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_minutes')"> <el-form-item :label="$t('load_test.ramp_up_time_minutes', [getUnitLabel(threadGroup)])">
<el-input-number <el-input-number
:disabled="isReadOnly" :disabled="isReadOnly"
:min="1" :min="1"
@ -95,7 +102,7 @@
@change="calculateChart(threadGroup)" @change="calculateChart(threadGroup)"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_seconds')"/> <el-form-item :label="$t('load_test.ramp_up_time_seconds', [getUnitLabel(threadGroup)])"/>
</div> </div>
</div> </div>
@ -128,7 +135,7 @@
v-model="threadGroup.iterateRampUp" v-model="threadGroup.iterateRampUp"
size="mini"/> size="mini"/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('load_test.ramp_up_time_seconds')"/> <el-form-item :label="$t('load_test.ramp_up_time_seconds', [getUnitLabel(threadGroup)])"/>
</div> </div>
</el-form> </el-form>
</el-col> </el-col>
@ -154,6 +161,7 @@ const RAMP_UP = "RampUp";
const ITERATE_RAMP_UP = "iterateRampUpTime"; const ITERATE_RAMP_UP = "iterateRampUpTime";
const STEPS = "Steps"; const STEPS = "Steps";
const DURATION = "duration"; const DURATION = "duration";
const UNIT = "unit";
const RPS_LIMIT = "rpsLimit"; const RPS_LIMIT = "rpsLimit";
const RPS_LIMIT_ENABLE = "rpsLimitEnable"; const RPS_LIMIT_ENABLE = "rpsLimitEnable";
const HOLD = "Hold"; const HOLD = "Hold";
@ -253,11 +261,10 @@ export default {
this.threadGroups[i].iterateRampUp = item.value; this.threadGroups[i].iterateRampUp = item.value;
break; break;
case DURATION: case DURATION:
if (item.unit) { this.threadGroups[i].duration = item.value;
this.threadGroups[i].duration = item.value; break;
} else { case UNIT:
this.threadGroups[i].duration = item.value * 60; this.threadGroups[i].unit = item.value;
}
break; break;
case STEPS: case STEPS:
this.threadGroups[i].step = item.value; this.threadGroups[i].step = item.value;
@ -290,6 +297,7 @@ export default {
break; break;
} }
// //
this.$set(this.threadGroups[i], "unit", this.threadGroups[i].unit || 'S');
this.$set(this.threadGroups[i], "threadType", this.threadGroups[i].threadType || 'DURATION'); this.$set(this.threadGroups[i], "threadType", this.threadGroups[i].threadType || 'DURATION');
this.$set(this.threadGroups[i], "iterateNum", this.threadGroups[i].iterateNum || 1); this.$set(this.threadGroups[i], "iterateNum", this.threadGroups[i].iterateNum || 1);
this.$set(this.threadGroups[i], "iterateRampUp", this.threadGroups[i].iterateRampUp || 10); this.$set(this.threadGroups[i], "iterateRampUp", this.threadGroups[i].iterateRampUp || 10);
@ -576,6 +584,18 @@ export default {
return true; return true;
}, },
getUnitLabel(tg) {
if (tg.unit === 'S') {
return this.$t('schedule.cron.seconds');
}
if (tg.unit === 'M') {
return this.$t('schedule.cron.minutes');
}
if (tg.unit === 'H') {
return this.$t('schedule.cron.hours');
}
return this.$t('schedule.cron.seconds');
},
convertProperty() { convertProperty() {
/// todo4jmeter ConcurrencyThreadGroup plugin /// todo4jmeter ConcurrencyThreadGroup plugin
let result = []; let result = [];
@ -585,7 +605,8 @@ export default {
{key: TARGET_LEVEL, value: this.threadGroups[i].threadNumber}, {key: TARGET_LEVEL, value: this.threadGroups[i].threadNumber},
{key: RAMP_UP, value: this.threadGroups[i].rampUpTime}, {key: RAMP_UP, value: this.threadGroups[i].rampUpTime},
{key: STEPS, value: this.threadGroups[i].step}, {key: STEPS, value: this.threadGroups[i].step},
{key: DURATION, value: this.threadGroups[i].duration, unit: 'S'}, {key: DURATION, value: this.threadGroups[i].duration, unit: this.threadGroups[i].unit},
{key: UNIT, value: this.threadGroups[i].unit},
{key: RPS_LIMIT, value: this.threadGroups[i].rpsLimit}, {key: RPS_LIMIT, value: this.threadGroups[i].rpsLimit},
{key: RPS_LIMIT_ENABLE, value: this.threadGroups[i].rpsLimitEnable}, {key: RPS_LIMIT_ENABLE, value: this.threadGroups[i].rpsLimitEnable},
{key: HOLD, value: this.threadGroups[i].duration - this.threadGroups[i].rampUpTime}, {key: HOLD, value: this.threadGroups[i].duration - this.threadGroups[i].rampUpTime},

View File

@ -31,6 +31,7 @@ export function findThreadGroup(jmxContent, handler) {
tg.enabled = tg.attributes.enabled; tg.enabled = tg.attributes.enabled;
tg.tgType = tg.name; tg.tgType = tg.name;
tg.threadType = 'DURATION'; tg.threadType = 'DURATION';
tg.unit = 'S';
}) })
return threadGroups; return threadGroups;
} }

View File

@ -15,18 +15,19 @@
{{ $t('api_test.home_page.unit_of_measurement') }} {{ $t('api_test.home_page.unit_of_measurement') }}
</span> </span>
<div> <div>
占比: 占比
<el-link type="info" @click="redirectPage('thisWeekCount')" target="_blank" style="color: #000000">{{ rage }} <span class="rage">
</el-link> {{rage}}
</span>
</div> </div>
</div> </div>
</el-aside> </el-aside>
<el-table border :data="tableData" class="adjust-table table-content" height="300"> <el-table border :data="tableData" class="adjust-table table-content" height="300">
<el-table-column prop="index" label="序号" <el-table-column prop="index" label="序号"
width="100" show-overflow-tooltip/> width="60" show-overflow-tooltip/>
<el-table-column prop="planName" label="测试计划名称" <el-table-column prop="planName" label="测试计划名称"
width="130" show-overflow-tooltip/> width="130" show-overflow-tooltip/>
<el-table-column prop="createTime" :label="$t('commons.create_time')" width="130"> <el-table-column prop="createTime" :label="$t('commons.create_time')" width="180" show-overflow-tooltip>
<template v-slot:default="scope"> <template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span> <span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template> </template>
@ -35,6 +36,7 @@
prop="status" prop="status"
column-key="status" column-key="status"
:label="$t('test_track.plan.plan_status')" :label="$t('test_track.plan.plan_status')"
width="100"
show-overflow-tooltip> show-overflow-tooltip>
<template v-slot:default="scope"> <template v-slot:default="scope">
<span @click.stop="clickt = 'stop'"> <span @click.stop="clickt = 'stop'">
@ -106,6 +108,12 @@ export default {
color: var(--count_number); color: var(--count_number);
} }
.rage {
font-family: 'ArialMT', 'Arial', sans-serif;
font-size: 18px;
color: var(--count_number);
}
.main-number-show { .main-number-show {
width: 100px; width: 100px;
height: 100px; height: 100px;

View File

@ -481,7 +481,7 @@ export default {
delete_file: "The file already exists, please delete the file with the same name first!", delete_file: "The file already exists, please delete the file with the same name first!",
thread_num: 'Concurrent users:', thread_num: 'Concurrent users:',
input_thread_num: 'Please enter the number of threads', input_thread_num: 'Please enter the number of threads',
duration: 'Duration time (seconds)', duration: 'Duration time',
granularity: 'Aggregation time (seconds)', granularity: 'Aggregation time (seconds)',
input_duration: 'Please enter a duration', input_duration: 'Please enter a duration',
rps_limit: 'RPS Limit:', rps_limit: 'RPS Limit:',

View File

@ -478,14 +478,14 @@ export default {
delete_file: "文件已存在,请先删除同名文件!", delete_file: "文件已存在,请先删除同名文件!",
thread_num: '并发用户数:', thread_num: '并发用户数:',
input_thread_num: '请输入线程数', input_thread_num: '请输入线程数',
duration: '压测时长(秒)', duration: '压测时长',
granularity: '聚合时间(秒)', granularity: '聚合时间(秒)',
input_duration: '请输入时长', input_duration: '请输入时长',
rps_limit: 'RPS上限', rps_limit: 'RPS上限',
input_rps_limit: '请输入限制', input_rps_limit: '请输入限制',
ramp_up_time_within: '在', ramp_up_time_within: '在',
ramp_up_time_minutes: '内,分', ramp_up_time_minutes: '{0}内,分',
ramp_up_time_seconds: '内增加并发用户', ramp_up_time_seconds: '{0}内增加并发用户',
iterate_num: '迭代次数 (次): ', iterate_num: '迭代次数 (次): ',
by_iteration: '按迭代次数', by_iteration: '按迭代次数',
by_duration: '按持续时间', by_duration: '按持续时间',

View File

@ -478,7 +478,7 @@ export default {
delete_file: "文件已存在,請先刪除同名文件!", delete_file: "文件已存在,請先刪除同名文件!",
thread_num: '並發用戶數:', thread_num: '並發用戶數:',
input_thread_num: '請輸入線程數', input_thread_num: '請輸入線程數',
duration: '壓測時長(秒)', duration: '壓測時長',
granularity: '聚合時間(秒)', granularity: '聚合時間(秒)',
input_duration: '請輸入時長', input_duration: '請輸入時長',
rps_limit: 'RPS上限', rps_limit: 'RPS上限',