refactor: 解决冲突
This commit is contained in:
commit
050a519d54
|
@ -7,6 +7,7 @@
|
|||
from test_case_issues, issues
|
||||
where test_case_issues.issues_id = issues.id
|
||||
and test_case_issues.test_case_id = #{caseId}
|
||||
and issues.platform = #{platform};
|
||||
and issues.platform = #{platform}
|
||||
order by issues.create_time DESC
|
||||
</select>
|
||||
</mapper>
|
|
@ -14,6 +14,7 @@ import org.apache.shiro.authz.annotation.RequiresRoles;
|
|||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RequestMapping("testresourcepool")
|
||||
|
@ -56,5 +57,11 @@ public class TestResourcePoolController {
|
|||
return testResourcePoolService.listValidResourcePools();
|
||||
}
|
||||
|
||||
@GetMapping("list/quota/valid")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER, RoleConstants.TEST_VIEWER}, logical = Logical.OR)
|
||||
public List<TestResourcePool> listValidQuotaResourcePools() {
|
||||
return testResourcePoolService.listValidQuotaResourcePools();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -34,8 +34,6 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
@ -44,6 +42,8 @@ import java.time.temporal.ChronoUnit;
|
|||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class PerformanceTestService {
|
||||
|
@ -131,7 +131,7 @@ public class PerformanceTestService {
|
|||
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
|
||||
}
|
||||
|
||||
checkQuota(request);
|
||||
checkQuota(request, true);
|
||||
|
||||
final LoadTestWithBLOBs loadTest = saveLoadTest(request);
|
||||
files.forEach(file -> {
|
||||
|
@ -168,6 +168,7 @@ public class PerformanceTestService {
|
|||
}
|
||||
|
||||
public String edit(EditTestPlanRequest request, List<MultipartFile> files) {
|
||||
checkQuota(request, false);
|
||||
//
|
||||
LoadTestWithBLOBs loadTest = loadTestMapper.selectByPrimaryKey(request.getId());
|
||||
if (loadTest == null) {
|
||||
|
@ -363,7 +364,7 @@ public class PerformanceTestService {
|
|||
}
|
||||
|
||||
public void copy(SaveTestPlanRequest request) {
|
||||
checkQuota(request);
|
||||
checkQuota(request, true);
|
||||
// copy test
|
||||
LoadTestWithBLOBs copy = loadTestMapper.selectByPrimaryKey(request.getId());
|
||||
copy.setId(UUID.randomUUID().toString());
|
||||
|
@ -441,10 +442,10 @@ public class PerformanceTestService {
|
|||
return schedules;
|
||||
}
|
||||
|
||||
private void checkQuota(SaveTestPlanRequest request) {
|
||||
private void checkQuota(TestPlanRequest request, boolean create) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
quotaService.checkLoadTestQuota(request);
|
||||
quotaService.checkLoadTestQuota(request, create);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import io.metersphere.track.request.testplan.SaveTestPlanRequest;
|
||||
import io.metersphere.track.request.testplan.TestPlanRequest;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface QuotaService {
|
||||
|
||||
void checkAPITestQuota();
|
||||
|
||||
void checkLoadTestQuota(SaveTestPlanRequest request);
|
||||
void checkLoadTestQuota(TestPlanRequest request, boolean checkPerformance);
|
||||
|
||||
Set<String> getQuotaResourcePools();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package io.metersphere.service;
|
||||
|
||||
import static io.metersphere.commons.constants.ResourceStatusEnum.INVALID;
|
||||
import static io.metersphere.commons.constants.ResourceStatusEnum.VALID;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.LoadTestMapper;
|
||||
|
@ -7,6 +10,7 @@ import io.metersphere.base.mapper.TestResourceMapper;
|
|||
import io.metersphere.base.mapper.TestResourcePoolMapper;
|
||||
import io.metersphere.commons.constants.ResourceStatusEnum;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.controller.request.resourcepool.QueryResourcePoolRequest;
|
||||
import io.metersphere.dto.NodeDTO;
|
||||
|
@ -21,15 +25,14 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static io.metersphere.commons.constants.ResourceStatusEnum.INVALID;
|
||||
import static io.metersphere.commons.constants.ResourceStatusEnum.VALID;
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @author dongbin
|
||||
|
@ -243,4 +246,19 @@ public class TestResourcePoolService {
|
|||
return testResourcePoolMapper.selectByExample(example);
|
||||
}
|
||||
|
||||
public List<TestResourcePool> listValidQuotaResourcePools() {
|
||||
return filterQuota(listValidResourcePools());
|
||||
}
|
||||
|
||||
private List<TestResourcePool> filterQuota(List<TestResourcePool> list) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
Set<String> pools = quotaService.getQuotaResourcePools();
|
||||
if (!pools.isEmpty()) {
|
||||
return list.stream().filter(pool -> pools.contains(pool.getId())).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -310,7 +310,11 @@ public class IssuesService {
|
|||
return new Issues();
|
||||
}
|
||||
JSONObject jsonObject = JSONObject.parseObject(listJson);
|
||||
return jsonObject.getObject("Bug", Issues.class);
|
||||
JSONObject bug = jsonObject.getJSONObject("Bug");
|
||||
Long created = bug.getLong("created");
|
||||
Issues issues = jsonObject.getObject("Bug", Issues.class);
|
||||
issues.setCreateTime(created);
|
||||
return issues;
|
||||
}
|
||||
|
||||
public Issues getJiraIssues(HttpHeaders headers, String url, String issuesId) {
|
||||
|
@ -326,15 +330,23 @@ public class IssuesService {
|
|||
JSONObject obj = JSONObject.parseObject(body);
|
||||
JSONObject fields = (JSONObject) obj.get("fields");
|
||||
JSONObject statusObj = (JSONObject) fields.get("status");
|
||||
JSONObject assignee = (JSONObject) fields.get("assignee");
|
||||
JSONObject statusCategory = (JSONObject) statusObj.get("statusCategory");
|
||||
|
||||
String id = obj.getString("id");
|
||||
String title = fields.getString("summary");
|
||||
String description = fields.getString("description");
|
||||
String status = statusCategory.getString("key");
|
||||
Long createTime = fields.getLong("created");
|
||||
String lastmodify = "";
|
||||
if (assignee != null) {
|
||||
lastmodify = assignee.getString("displayName");
|
||||
}
|
||||
|
||||
issues.setId(id);
|
||||
issues.setTitle(title);
|
||||
issues.setCreateTime(createTime);
|
||||
issues.setLastmodify(lastmodify);
|
||||
issues.setDescription(description);
|
||||
issues.setStatus(status);
|
||||
issues.setPlatform(IssuesManagePlatform.Jira.toString());
|
||||
|
|
|
@ -269,9 +269,17 @@ public class TestPlanService {
|
|||
List<Issues> issue = issuesService.getIssues(testCase.getCaseId());
|
||||
if (issue.size() > 0) {
|
||||
for (Issues i : issue) {
|
||||
i.setModel(testCase.getModel());
|
||||
i.setModel(testCase.getNodePath());
|
||||
String des = i.getDescription().replaceAll("<p>", "").replaceAll("</p>", "");
|
||||
i.setDescription(des);
|
||||
if (i.getLastmodify() == null || i.getLastmodify() == "") {
|
||||
if (i.getReporter() != null || i.getReporter() != "") {
|
||||
i.setLastmodify(i.getReporter());
|
||||
}
|
||||
}
|
||||
}
|
||||
issues.addAll(issue);
|
||||
Collections.sort(issues, Comparator.comparing(Issues::getCreateTime, (t1, t2) -> t2.compareTo(t1)));
|
||||
}
|
||||
|
||||
components.forEach(component -> {
|
||||
|
|
|
@ -20,7 +20,7 @@ create table if not exists issues
|
|||
(
|
||||
id varchar(50) not null
|
||||
primary key,
|
||||
title varchar(50) null,
|
||||
title varchar(64) null,
|
||||
description text null,
|
||||
status varchar(50) null,
|
||||
create_time bigint(13) null,
|
||||
|
|
|
@ -9,10 +9,10 @@
|
|||
<span>{{ report.projectName }} / </span>
|
||||
<router-link :to="path">{{ report.testName }}</router-link>
|
||||
<span class="time">{{ report.createTime | timestampFormatDate }}</span>
|
||||
<el-button plain type="primary" size="mini" @click="handleExport(report.name)"
|
||||
<!--<el-button plain type="primary" size="mini" @click="handleExport(report.name)"
|
||||
style="margin-left: 1200px">
|
||||
{{$t('test_track.plan_view.export_report')}}
|
||||
</el-button>
|
||||
</el-button>-->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</header>
|
||||
|
@ -114,9 +114,7 @@
|
|||
this.fails = [];
|
||||
this.totalTime = 0
|
||||
this.content.scenarios.forEach((scenario) => {
|
||||
console.log(scenario.responseTime)
|
||||
this.totalTime = this.totalTime + Number(scenario.responseTime)
|
||||
console.log(this.totalTime)
|
||||
let failScenario = Object.assign({}, scenario);
|
||||
if (scenario.error > 0) {
|
||||
this.fails.push(failScenario);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import MsProject from "@/business/components/project/MsProject";
|
||||
|
||||
export default {
|
||||
path: "/api",
|
||||
name: "api",
|
||||
redirect: "/api/home",
|
||||
components: {
|
||||
content: () => import(/* webpackChunkName: "api" */ '@/business/components/api/ApiTest')
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'fucHome',
|
||||
component: () => import(/* webpackChunkName: "api" */ '@/business/components/api/home/ApiTestHome'),
|
||||
},
|
||||
{
|
||||
path: "test/:type",
|
||||
name: "ApiTestConfig",
|
||||
component: () => import(/* webpackChunkName: "api" */ '@/business/components/api/test/ApiTestConfig'),
|
||||
props: (route) => ({id: route.query.id})
|
||||
},
|
||||
{
|
||||
path: "test/list/:projectId",
|
||||
name: "ApiTestList",
|
||||
component: () => import(/* webpackChunkName: "api" */ '@/business/components/api/test/ApiTestList'),
|
||||
},
|
||||
{
|
||||
path: "project/:type",
|
||||
name: "fucProject",
|
||||
component: MsProject,
|
||||
},
|
||||
{
|
||||
path: "report/list/:testId",
|
||||
name: "ApiReportList",
|
||||
component: () => import(/* webpackChunkName: "api" */ '@/business/components/api/report/ApiReportList'),
|
||||
},
|
||||
{
|
||||
path: "report/view/:reportId",
|
||||
name: "ApiReportView",
|
||||
component: () => import(/* webpackChunkName: "api" */ '@/business/components/api/report/ApiReportView'),
|
||||
}
|
||||
]
|
||||
}
|
|
@ -21,130 +21,130 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsDialogFooter from '../../common/components/MsDialogFooter'
|
||||
import {Test} from "./model/ScenarioModel";
|
||||
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
|
||||
import MsApiReportStatus from "../report/ApiReportStatus";
|
||||
import MsApiReportDialog from "./ApiReportDialog";
|
||||
import {removeGoBackListener} from "../../../../common/js/utils";
|
||||
import MsDialogFooter from '../../common/components/MsDialogFooter'
|
||||
import {Test} from "./model/ScenarioModel";
|
||||
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
|
||||
import MsApiReportStatus from "../report/ApiReportStatus";
|
||||
import MsApiReportDialog from "./ApiReportDialog";
|
||||
|
||||
|
||||
export default {
|
||||
name: "OneClickOperation",
|
||||
components: {
|
||||
MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig, MsDialogFooter
|
||||
export default {
|
||||
name: "OneClickOperation",
|
||||
components: {
|
||||
MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig, MsDialogFooter
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
oneClickOperationVisible: false,
|
||||
test: null,
|
||||
tests: [],
|
||||
ruleForm: {},
|
||||
rule: {
|
||||
testName: [
|
||||
{required: true, message: this.$t('api_test.input_name'), trigger: 'blur'},
|
||||
],
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
selectIds: {
|
||||
type: Set
|
||||
},
|
||||
data() {
|
||||
selectNames: {
|
||||
type: Set
|
||||
},
|
||||
selectProjectNames: {
|
||||
type: Set
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openOneClickOperation() {
|
||||
this.oneClickOperationVisible = true;
|
||||
},
|
||||
checkedSaveAndRunTest() {
|
||||
if (this.selectNames.has(this.ruleForm.testName)) {
|
||||
this.$warning(this.$t('load_test.already_exists'));
|
||||
this.oneClickOperationVisible = false;
|
||||
} else {
|
||||
if (this.selectProjectNames.size > 1) {
|
||||
this.$warning(this.$t('load_test.same_project_test'));
|
||||
this.oneClickOperationVisible = false;
|
||||
} else {
|
||||
for (let x of this.selectIds) {
|
||||
this.getTest(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getTest(id) {
|
||||
this.result = this.$get("/api/get/" + id, response => {
|
||||
if (response.data) {
|
||||
let item = response.data;
|
||||
this.tests.push(item);
|
||||
let test = new Test({
|
||||
projectId: item.projectId,
|
||||
name: this.ruleForm.testName,
|
||||
scenarioDefinition: JSON.parse(item.scenarioDefinition),
|
||||
schedule: {},
|
||||
});
|
||||
this.test = this.test || test;
|
||||
if (this.tests.length > 1) {
|
||||
this.test.scenarioDefinition = this.test.scenarioDefinition.concat(test.scenarioDefinition);
|
||||
|
||||
}
|
||||
if (this.tests.length === this.selectIds.size) {
|
||||
this.tests = [];
|
||||
this.saveRunTest();
|
||||
this.oneClickOperationVisible = false;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
saveRunTest() {
|
||||
this.save(() => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.runTest();
|
||||
})
|
||||
},
|
||||
save(callback) {
|
||||
let url = "/api/create";
|
||||
this.result = this.$request(this.getOptions(url), () => {
|
||||
this.create = false;
|
||||
if (callback) callback();
|
||||
});
|
||||
},
|
||||
runTest() {
|
||||
this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
|
||||
this.$success(this.$t('api_test.running'));
|
||||
this.$router.push({
|
||||
path: '/api/report/view/' + response.data
|
||||
})
|
||||
});
|
||||
},
|
||||
getOptions(url) {
|
||||
let formData = new FormData();
|
||||
let requestJson = JSON.stringify(this.test);
|
||||
formData.append('request', new Blob([requestJson], {
|
||||
type: "application/json"
|
||||
}));
|
||||
let jmx = this.test.toJMX();
|
||||
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
|
||||
formData.append("file", new File([blob], jmx.name));
|
||||
return {
|
||||
oneClickOperationVisible: false,
|
||||
test: null,
|
||||
tests: [],
|
||||
ruleForm: {},
|
||||
rule: {
|
||||
testName: [
|
||||
{required: true, message: this.$t('api_test.input_name'), trigger: 'blur'},
|
||||
],
|
||||
method: 'POST',
|
||||
url: url,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
props: {
|
||||
selectIds: {
|
||||
type: Set
|
||||
},
|
||||
selectNames: {
|
||||
type: Set
|
||||
},
|
||||
selectProjectNames: {
|
||||
type: Set
|
||||
}
|
||||
handleClose() {
|
||||
this.ruleForm = {}
|
||||
},
|
||||
methods: {
|
||||
openOneClickOperation() {
|
||||
this.oneClickOperationVisible = true;
|
||||
},
|
||||
checkedSaveAndRunTest() {
|
||||
if (this.selectNames.has(this.testName)) {
|
||||
this.$warning(this.$t('load_test.already_exists'));
|
||||
} else {
|
||||
if (this.selectProjectNames.size > 1) {
|
||||
this.$warning(this.$t('load_test.same_project_test'));
|
||||
} else {
|
||||
for (let x of this.selectIds) {
|
||||
this.getTest(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
getTest(id) {
|
||||
this.result = this.$get("/api/get/" + id, response => {
|
||||
if (response.data) {
|
||||
let item = response.data;
|
||||
this.tests.push(item);
|
||||
let test = new Test({
|
||||
projectId: item.projectId,
|
||||
name: this.ruleForm.testName,
|
||||
scenarioDefinition: JSON.parse(item.scenarioDefinition),
|
||||
schedule: {},
|
||||
});
|
||||
this.test = this.test || test;
|
||||
if (this.tests.length > 1) {
|
||||
this.test.scenarioDefinition = this.test.scenarioDefinition.concat(test.scenarioDefinition);
|
||||
|
||||
}
|
||||
if (this.tests.length === this.selectIds.size) {
|
||||
this.tests = [];
|
||||
this.saveRunTest();
|
||||
this.oneClickOperationVisible = false;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
saveRunTest() {
|
||||
this.save(() => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.runTest();
|
||||
})
|
||||
},
|
||||
save(callback) {
|
||||
let url = "/api/create";
|
||||
this.result = this.$request(this.getOptions(url), () => {
|
||||
this.create = false;
|
||||
if (callback) callback();
|
||||
});
|
||||
},
|
||||
runTest() {
|
||||
this.result = this.$post("/api/run", {id: this.test.id, triggerMode: 'MANUAL'}, (response) => {
|
||||
this.$success(this.$t('api_test.running'));
|
||||
this.$router.push({
|
||||
path: '/api/report/view/' + response.data
|
||||
})
|
||||
});
|
||||
},
|
||||
getOptions(url) {
|
||||
let formData = new FormData();
|
||||
let requestJson = JSON.stringify(this.test);
|
||||
formData.append('request', new Blob([requestJson], {
|
||||
type: "application/json"
|
||||
}));
|
||||
let jmx = this.test.toJMX();
|
||||
let blob = new Blob([jmx.xml], {type: "application/octet-stream"});
|
||||
formData.append("file", new File([blob], jmx.name));
|
||||
return {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
handleClose() {
|
||||
this.ruleForm = {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
|
||||
<script>
|
||||
import {KeyValue, Scenario} from "../model/ScenarioModel";
|
||||
import {MOCKJS_FUNC} from "@/common/js/constants";
|
||||
import {JMETER_FUNC, MOCKJS_FUNC} from "@/common/js/constants";
|
||||
import MsApiVariableAdvance from "@/business/components/api/test/components/ApiVariableAdvance";
|
||||
import MsApiBodyFileUpload from "./body/ApiBodyFileUpload";
|
||||
|
||||
|
@ -127,7 +127,7 @@ export default {
|
|||
};
|
||||
},
|
||||
funcSearch(queryString, cb) {
|
||||
let funcs = MOCKJS_FUNC;
|
||||
let funcs = MOCKJS_FUNC.concat(JMETER_FUNC);
|
||||
let results = queryString ? funcs.filter(this.funcFilter(queryString)) : funcs;
|
||||
// 调用 callback 返回建议列表的数据
|
||||
cb(results);
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<slot name="button"></slot>
|
||||
</span>
|
||||
<span>
|
||||
<ms-table-search-bar :condition.sync="condition" @change="search" class="search-bar"/>
|
||||
<ms-table-search-bar :condition.sync="condition" @change="search" class="search-bar" :tip="tip"/>
|
||||
<ms-table-adv-search-bar :condition.sync="condition" @search="search" v-if="isCombine"/>
|
||||
</span>
|
||||
</el-row>
|
||||
|
@ -65,6 +65,12 @@
|
|||
isTesterPermission: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
tip: {
|
||||
String,
|
||||
default() {
|
||||
return this.$t('commons.search_by_name');
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
class="search"
|
||||
type="text"
|
||||
size="small"
|
||||
:placeholder="$t('commons.search_by_name')"
|
||||
:placeholder="tip"
|
||||
prefix-icon="el-icon-search"
|
||||
@change="search"
|
||||
maxlength="60"
|
||||
|
@ -18,6 +18,12 @@
|
|||
props: {
|
||||
condition: {
|
||||
type: Object
|
||||
},
|
||||
tip: {
|
||||
String,
|
||||
default() {
|
||||
return this.$t('commons.search_by_name');
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
<template>
|
||||
<el-menu-item :index="this.index" @click="changeRoute">
|
||||
<font-awesome-icon :icon="['fa', 'list-ul']"/>
|
||||
<span>{{$t('commons.show_all')}}</span>
|
||||
<span>{{ $t('commons.show_all') }}</span>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsShowAll",
|
||||
props: {
|
||||
index: String
|
||||
},
|
||||
methods: {
|
||||
changeRoute() {
|
||||
// 解决在列表页面点击 显示全部 无效的问题(点击显示全部后改变路由)
|
||||
export default {
|
||||
name: "MsShowAll",
|
||||
props: {
|
||||
index: String
|
||||
},
|
||||
methods: {
|
||||
changeRoute() {
|
||||
// 解决在列表页面点击 显示全部 无效的问题(点击显示全部后改变路由)
|
||||
if (this.$route.path === this.index) {
|
||||
this.$router.replace({path: this.index, query: {type: 'all'}});
|
||||
window.location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
svg + span {
|
||||
padding-left: 5px;
|
||||
}
|
||||
svg + span {
|
||||
padding-left: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,27 +1,11 @@
|
|||
import Vue from "vue";
|
||||
import VueRouter from 'vue-router'
|
||||
import RouterSidebar from "./RouterSidebar";
|
||||
import EditPerformanceTestPlan from "../../performance/test/EditPerformanceTestPlan";
|
||||
import PerformanceTestPlan from "../../performance/test/PerformanceTestPlan";
|
||||
import MsProject from "../../project/MsProject";
|
||||
import PerformanceChart from "../../performance/report/components/PerformanceChart";
|
||||
import PerformanceTestReport from "../../performance/report/PerformanceTestReport";
|
||||
import ApiTest from "../../api/ApiTest";
|
||||
import PerformanceTest from "../../performance/PerformanceTest";
|
||||
import ApiTestConfig from "../../api/test/ApiTestConfig";
|
||||
import PerformanceTestHome from "../../performance/home/PerformanceTestHome";
|
||||
import ApiTestList from "../../api/test/ApiTestList";
|
||||
import ApiTestHome from "../../api/home/ApiTestHome";
|
||||
import PerformanceReportView from "../../performance/report/PerformanceReportView";
|
||||
import ApiReportView from "../../api/report/ApiReportView";
|
||||
import TrackHome from "../../track/home/TrackHome";
|
||||
import TestPlan from "../../track/plan/TestPlan";
|
||||
import TestPlanView from "../../track/plan/view/TestPlanView";
|
||||
import TestCase from "../../track/case/TestCase";
|
||||
import TestTrack from "../../track/TestTrack";
|
||||
import ApiReportList from "../../api/report/ApiReportList";
|
||||
import axios from "axios";
|
||||
import Setting from "@/business/components/settings/router";
|
||||
import API from "@/business/components/api/router";
|
||||
import Performance from "@/business/components/performance/router";
|
||||
import Track from "@/business/components/track/router";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
|
@ -35,158 +19,12 @@ const router = new VueRouter({
|
|||
}
|
||||
},
|
||||
Setting,
|
||||
{
|
||||
path: "/api",
|
||||
name: "api",
|
||||
redirect: "/api/home",
|
||||
components: {
|
||||
content: ApiTest
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'fucHome',
|
||||
component: ApiTestHome,
|
||||
},
|
||||
{
|
||||
path: "test/:type",
|
||||
name: "ApiTestConfig",
|
||||
component: ApiTestConfig,
|
||||
props: (route) => ({id: route.query.id})
|
||||
},
|
||||
{
|
||||
path: "test/list/:projectId",
|
||||
name: "ApiTestList",
|
||||
component: ApiTestList
|
||||
},
|
||||
{
|
||||
path: "project/:type",
|
||||
name: "fucProject",
|
||||
component: MsProject
|
||||
},
|
||||
{
|
||||
path: "report/list/:testId",
|
||||
name: "ApiReportList",
|
||||
component: ApiReportList
|
||||
},
|
||||
{
|
||||
path: "report/view/:reportId",
|
||||
name: "ApiReportView",
|
||||
component: ApiReportView
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/performance",
|
||||
name: "performance",
|
||||
redirect: "/performance/home",
|
||||
components: {
|
||||
content: PerformanceTest
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'perHome',
|
||||
component: PerformanceTestHome,
|
||||
},
|
||||
{
|
||||
path: 'test/create',
|
||||
name: "createPerTest",
|
||||
component: EditPerformanceTestPlan,
|
||||
},
|
||||
{
|
||||
path: "test/edit/:testId",
|
||||
name: "editPerTest",
|
||||
component: EditPerformanceTestPlan,
|
||||
props: {
|
||||
content: (route) => {
|
||||
return {
|
||||
...route.params
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "test/:projectId",
|
||||
name: "perPlan",
|
||||
component: PerformanceTestPlan
|
||||
},
|
||||
{
|
||||
path: "project/:type",
|
||||
name: "perProject",
|
||||
component: MsProject
|
||||
},
|
||||
{
|
||||
path: "report/:type",
|
||||
name: "perReport",
|
||||
component: PerformanceTestReport
|
||||
},
|
||||
{
|
||||
path: "chart",
|
||||
name: "perChart",
|
||||
component: PerformanceChart
|
||||
},
|
||||
{
|
||||
path: "report/view/:reportId",
|
||||
name: "perReportView",
|
||||
component: PerformanceReportView
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/track",
|
||||
name: "track",
|
||||
redirect: "/track/home",
|
||||
components: {
|
||||
content: TestTrack
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'trackHome',
|
||||
component: TrackHome,
|
||||
},
|
||||
{
|
||||
path: 'case/create',
|
||||
name: 'testCaseCreate',
|
||||
component: TestCase,
|
||||
},
|
||||
{
|
||||
path: 'case/:projectId',
|
||||
name: 'testCase',
|
||||
component: TestCase,
|
||||
},
|
||||
{
|
||||
path: 'case/edit/:caseId',
|
||||
name: 'testCaseEdit',
|
||||
component: TestCase,
|
||||
},
|
||||
{
|
||||
path: "plan/:type",
|
||||
name: "testPlan",
|
||||
component: TestPlan
|
||||
},
|
||||
{
|
||||
path: "plan/view/:planId",
|
||||
name: "planView",
|
||||
component: TestPlanView
|
||||
},
|
||||
{
|
||||
path: "plan/view/edit/:caseId",
|
||||
name: "planViewEdit",
|
||||
component: TestPlanView
|
||||
},
|
||||
{
|
||||
path: "project/:type",
|
||||
name: "trackProject",
|
||||
component: MsProject
|
||||
}
|
||||
]
|
||||
}
|
||||
API,
|
||||
Performance,
|
||||
Track,
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
//解决localStorage清空,cookie没失效导致的卡死问题
|
||||
if (!localStorage.getItem('Admin-Token')) {
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
@click="rerun(testId)">
|
||||
{{ $t('report.test_execute_again') }}
|
||||
</el-button>
|
||||
<el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)">
|
||||
<!-- <el-button :disabled="isReadOnly" type="info" plain size="mini" @click="exports(reportName)">
|
||||
{{$t('report.export')}}
|
||||
</el-button>
|
||||
</el-button>-->
|
||||
<!--
|
||||
<el-button :disabled="isReadOnly" type="warning" plain size="mini">
|
||||
{{$t('report.compare')}}
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import MsProject from "@/business/components/project/MsProject";
|
||||
|
||||
const PerformanceTest = () => import(/* webpackChunkName: "performance" */ '@/business/components/performance/PerformanceTest')
|
||||
const PerformanceTestHome = () => import(/* webpackChunkName: "performance" */ '@/business/components/performance/home/PerformanceTestHome')
|
||||
const EditPerformanceTestPlan = () => import(/* webpackChunkName: "performance" */ '@/business/components/performance/test/EditPerformanceTestPlan')
|
||||
const PerformanceTestPlan = () => import(/* webpackChunkName: "performance" */ '@/business/components/performance/test/PerformanceTestPlan')
|
||||
const PerformanceTestReport = () => import(/* webpackChunkName: "performance" */ '@/business/components/performance/report/PerformanceTestReport')
|
||||
const PerformanceChart = () => import(/* webpackChunkName: "performance" */ '@/business/components/performance/report/components/PerformanceChart')
|
||||
const PerformanceReportView = () => import(/* webpackChunkName: "performance" */ '@/business/components/performance/report/PerformanceReportView')
|
||||
|
||||
export default {
|
||||
path: "/performance",
|
||||
name: "performance",
|
||||
redirect: "/performance/home",
|
||||
components: {
|
||||
content: PerformanceTest
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'perHome',
|
||||
component: PerformanceTestHome,
|
||||
},
|
||||
{
|
||||
path: 'test/create',
|
||||
name: "createPerTest",
|
||||
component: EditPerformanceTestPlan,
|
||||
},
|
||||
{
|
||||
path: "test/edit/:testId",
|
||||
name: "editPerTest",
|
||||
component: EditPerformanceTestPlan,
|
||||
props: {
|
||||
content: (route) => {
|
||||
return {
|
||||
...route.params
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "test/:projectId",
|
||||
name: "perPlan",
|
||||
component: PerformanceTestPlan
|
||||
},
|
||||
{
|
||||
path: "project/:type",
|
||||
name: "perProject",
|
||||
component: MsProject
|
||||
},
|
||||
{
|
||||
path: "report/:type",
|
||||
name: "perReport",
|
||||
component: PerformanceTestReport
|
||||
},
|
||||
{
|
||||
path: "chart",
|
||||
name: "perChart",
|
||||
component: PerformanceChart
|
||||
},
|
||||
{
|
||||
path: "report/view/:reportId",
|
||||
name: "perReportView",
|
||||
component: PerformanceReportView
|
||||
}
|
||||
]
|
||||
}
|
|
@ -22,11 +22,12 @@
|
|||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="2">
|
||||
<el-button :disabled="isReadOnly" type="primary" plain @click="save">{{$t('commons.save')}}</el-button>
|
||||
<el-button :disabled="isReadOnly" type="primary" plain @click="save">{{ $t('commons.save') }}</el-button>
|
||||
<el-button :disabled="isReadOnly" type="primary" plain @click="saveAndRun">
|
||||
{{$t('load_test.save_and_run')}}
|
||||
{{ $t('load_test.save_and_run') }}
|
||||
</el-button>
|
||||
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{ $t('commons.cancel') }}
|
||||
</el-button>
|
||||
<el-button :disabled="isReadOnly" type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
|
||||
|
||||
<ms-schedule-config :schedule="testPlan.schedule" :save="saveCronExpression" @scheduleChange="saveSchedule"
|
||||
:check-open="checkScheduleEdit" :custom-validate="durationValidate"/>
|
||||
|
@ -52,272 +53,272 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import PerformanceBasicConfig from "./components/PerformanceBasicConfig";
|
||||
import PerformancePressureConfig from "./components/PerformancePressureConfig";
|
||||
import PerformanceAdvancedConfig from "./components/PerformanceAdvancedConfig";
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
|
||||
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
||||
import PerformanceBasicConfig from "./components/PerformanceBasicConfig";
|
||||
import PerformancePressureConfig from "./components/PerformancePressureConfig";
|
||||
import PerformanceAdvancedConfig from "./components/PerformanceAdvancedConfig";
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import {checkoutTestManagerOrTestUser} from "../../../../common/js/utils";
|
||||
import MsScheduleConfig from "../../common/components/MsScheduleConfig";
|
||||
|
||||
export default {
|
||||
name: "EditPerformanceTestPlan",
|
||||
components: {
|
||||
MsScheduleConfig,
|
||||
PerformancePressureConfig,
|
||||
PerformanceBasicConfig,
|
||||
PerformanceAdvancedConfig,
|
||||
MsContainer,
|
||||
MsMainContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
testPlan: {schedule: {}},
|
||||
listProjectPath: "/project/listAll",
|
||||
savePath: "/performance/save",
|
||||
editPath: "/performance/edit",
|
||||
runPath: "/performance/run",
|
||||
projects: [],
|
||||
active: '0',
|
||||
testId: '',
|
||||
isReadOnly: false,
|
||||
tabs: [{
|
||||
title: this.$t('load_test.basic_config'),
|
||||
id: '0',
|
||||
component: 'PerformanceBasicConfig'
|
||||
}, {
|
||||
title: this.$t('load_test.pressure_config'),
|
||||
id: '1',
|
||||
component: 'PerformancePressureConfig'
|
||||
}, {
|
||||
title: this.$t('load_test.advanced_config'),
|
||||
id: '2',
|
||||
component: 'PerformanceAdvancedConfig'
|
||||
}]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route'(to) {
|
||||
// 如果是创建测试
|
||||
if (to.name === 'createPerTest') {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (to.name !== 'editPerTest') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isReadOnly = false;
|
||||
if (!checkoutTestManagerOrTestUser()) {
|
||||
this.isReadOnly = true;
|
||||
}
|
||||
this.getTest(to.params.testId);
|
||||
export default {
|
||||
name: "EditPerformanceTestPlan",
|
||||
components: {
|
||||
MsScheduleConfig,
|
||||
PerformancePressureConfig,
|
||||
PerformanceBasicConfig,
|
||||
PerformanceAdvancedConfig,
|
||||
MsContainer,
|
||||
MsMainContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
testPlan: {schedule: {}},
|
||||
listProjectPath: "/project/listAll",
|
||||
savePath: "/performance/save",
|
||||
editPath: "/performance/edit",
|
||||
runPath: "/performance/run",
|
||||
projects: [],
|
||||
active: '0',
|
||||
testId: '',
|
||||
isReadOnly: false,
|
||||
tabs: [{
|
||||
title: this.$t('load_test.basic_config'),
|
||||
id: '0',
|
||||
component: 'PerformanceBasicConfig'
|
||||
}, {
|
||||
title: this.$t('load_test.pressure_config'),
|
||||
id: '1',
|
||||
component: 'PerformancePressureConfig'
|
||||
}, {
|
||||
title: this.$t('load_test.advanced_config'),
|
||||
id: '2',
|
||||
component: 'PerformanceAdvancedConfig'
|
||||
}]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route'(to) {
|
||||
// 如果是创建测试
|
||||
if (to.name === 'createPerTest') {
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
|
||||
if (to.name !== 'editPerTest') {
|
||||
return;
|
||||
}
|
||||
|
||||
},
|
||||
created() {
|
||||
this.isReadOnly = false;
|
||||
if (!checkoutTestManagerOrTestUser()) {
|
||||
this.isReadOnly = true;
|
||||
}
|
||||
this.getTest(this.$route.params.testId);
|
||||
this.listProjects();
|
||||
this.getTest(to.params.testId);
|
||||
}
|
||||
|
||||
},
|
||||
created() {
|
||||
this.isReadOnly = false;
|
||||
if (!checkoutTestManagerOrTestUser()) {
|
||||
this.isReadOnly = true;
|
||||
}
|
||||
this.getTest(this.$route.params.testId);
|
||||
this.listProjects();
|
||||
},
|
||||
mounted() {
|
||||
this.importAPITest();
|
||||
},
|
||||
methods: {
|
||||
importAPITest() {
|
||||
let apiTest = this.$store.state.api.test;
|
||||
if (apiTest && apiTest.name) {
|
||||
this.$set(this.testPlan, "projectId", apiTest.projectId);
|
||||
this.$set(this.testPlan, "name", apiTest.name);
|
||||
let blob = new Blob([apiTest.jmx.xml], {type: "application/octet-stream"});
|
||||
let file = new File([blob], apiTest.jmx.name);
|
||||
this.$refs.basicConfig.beforeUpload(file);
|
||||
this.$refs.basicConfig.handleUpload({file: file});
|
||||
this.active = '1';
|
||||
this.$store.commit("clearTest");
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.importAPITest();
|
||||
},
|
||||
methods: {
|
||||
importAPITest() {
|
||||
let apiTest = this.$store.state.api.test;
|
||||
if (apiTest && apiTest.name) {
|
||||
this.$set(this.testPlan, "projectId", apiTest.projectId);
|
||||
this.$set(this.testPlan, "name", apiTest.name);
|
||||
let blob = new Blob([apiTest.jmx.xml], {type: "application/octet-stream"});
|
||||
let file = new File([blob], apiTest.jmx.name);
|
||||
this.$refs.basicConfig.beforeUpload(file);
|
||||
this.$refs.basicConfig.handleUpload({file: file});
|
||||
this.active = '1';
|
||||
this.$store.commit("clearTest");
|
||||
}
|
||||
},
|
||||
getTest(testId) {
|
||||
if (testId) {
|
||||
this.testId = testId;
|
||||
this.result = this.$get('/performance/get/' + testId, response => {
|
||||
if (response.data) {
|
||||
this.testPlan = response.data;
|
||||
if (!this.testPlan.schedule) {
|
||||
this.testPlan.schedule = {};
|
||||
}
|
||||
getTest(testId) {
|
||||
if (testId) {
|
||||
this.testId = testId;
|
||||
this.result = this.$get('/performance/get/' + testId, response => {
|
||||
if (response.data) {
|
||||
this.testPlan = response.data;
|
||||
if (!this.testPlan.schedule) {
|
||||
this.testPlan.schedule = {};
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
listProjects() {
|
||||
this.result = this.$get(this.listProjectPath, response => {
|
||||
this.projects = response.data;
|
||||
})
|
||||
},
|
||||
save() {
|
||||
if (!this.validTestPlan()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let options = this.getSaveOption();
|
||||
|
||||
this.result = this.$request(options, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.advancedConfig.cancelAllEdit();
|
||||
this.$router.push({path: '/performance/test/all'})
|
||||
});
|
||||
},
|
||||
saveAndRun() {
|
||||
if (!this.validTestPlan()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let options = this.getSaveOption();
|
||||
|
||||
this.result = this.$request(options, (response) => {
|
||||
this.testPlan.id = response.data;
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.result = this.$post(this.runPath, {id: this.testPlan.id, triggerMode: 'MANUAL'}, (response) => {
|
||||
let reportId = response.data;
|
||||
this.$router.push({path: '/performance/report/view/' + reportId})
|
||||
})
|
||||
});
|
||||
},
|
||||
getSaveOption() {
|
||||
let formData = new FormData();
|
||||
let url = this.testPlan.id ? this.editPath : this.savePath;
|
||||
|
||||
if (this.$refs.basicConfig.uploadList.length > 0) {
|
||||
this.$refs.basicConfig.uploadList.forEach(f => {
|
||||
formData.append("file", f);
|
||||
});
|
||||
}
|
||||
// 基本配置
|
||||
this.testPlan.updatedFileList = this.$refs.basicConfig.updatedFileList();
|
||||
// 压力配置
|
||||
this.testPlan.loadConfiguration = JSON.stringify(this.$refs.pressureConfig.convertProperty());
|
||||
this.testPlan.testResourcePoolId = this.$refs.pressureConfig.resourcePool;
|
||||
// 高级配置
|
||||
this.testPlan.advancedConfiguration = JSON.stringify(this.$refs.advancedConfig.configurations());
|
||||
|
||||
// file属性不需要json化
|
||||
let requestJson = JSON.stringify(this.testPlan, function (key, value) {
|
||||
return key === "file" ? undefined : value
|
||||
});
|
||||
|
||||
formData.append('request', new Blob([requestJson], {
|
||||
type: "application/json"
|
||||
}));
|
||||
|
||||
return {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
cancel() {
|
||||
});
|
||||
}
|
||||
},
|
||||
listProjects() {
|
||||
this.result = this.$get(this.listProjectPath, response => {
|
||||
this.projects = response.data;
|
||||
})
|
||||
},
|
||||
save() {
|
||||
if (!this.validTestPlan()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let options = this.getSaveOption();
|
||||
|
||||
this.result = this.$request(options, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.advancedConfig.cancelAllEdit();
|
||||
this.$router.push({path: '/performance/test/all'})
|
||||
},
|
||||
validTestPlan() {
|
||||
if (!this.testPlan.name) {
|
||||
this.$error(this.$t('load_test.test_name_is_null'));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
},
|
||||
saveAndRun() {
|
||||
if (!this.validTestPlan()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.testPlan.projectId) {
|
||||
this.$error(this.$t('load_test.project_is_null'));
|
||||
return false;
|
||||
}
|
||||
let options = this.getSaveOption();
|
||||
|
||||
if (!this.$refs.basicConfig.validConfig()) {
|
||||
return false;
|
||||
}
|
||||
this.result = this.$request(options, (response) => {
|
||||
this.testPlan.id = response.data;
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.result = this.$post(this.runPath, {id: this.testPlan.id, triggerMode: 'MANUAL'}, (response) => {
|
||||
let reportId = response.data;
|
||||
this.$router.push({path: '/performance/report/view/' + reportId})
|
||||
})
|
||||
});
|
||||
},
|
||||
getSaveOption() {
|
||||
let formData = new FormData();
|
||||
let url = this.testPlan.id ? this.editPath : this.savePath;
|
||||
|
||||
if (!this.$refs.pressureConfig.validConfig()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.$refs.advancedConfig.validConfig()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// todo: 其他校验
|
||||
|
||||
return true;
|
||||
},
|
||||
changeTabActive(activeName) {
|
||||
this.$nextTick(() => {
|
||||
this.active = activeName;
|
||||
if (this.$refs.basicConfig.uploadList.length > 0) {
|
||||
this.$refs.basicConfig.uploadList.forEach(f => {
|
||||
formData.append("file", f);
|
||||
});
|
||||
},
|
||||
saveCronExpression(cronExpression) {
|
||||
this.testPlan.schedule.enable = true;
|
||||
this.testPlan.schedule.value = cronExpression;
|
||||
this.saveSchedule();
|
||||
},
|
||||
saveSchedule() {
|
||||
this.checkScheduleEdit();
|
||||
let param = {};
|
||||
param = this.testPlan.schedule;
|
||||
param.resourceId = this.testPlan.id;
|
||||
let url = '/performance/schedule/create';
|
||||
if (param.id) {
|
||||
url = '/performance/schedule/update';
|
||||
}
|
||||
this.$post(url, param, response => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.getTest(this.testPlan.id);
|
||||
});
|
||||
},
|
||||
checkScheduleEdit() {
|
||||
if (!this.testPlan.id) {
|
||||
this.$message(this.$t('api_test.environment.please_save_test'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
durationValidate(intervalTime) {
|
||||
let duration = this.$refs.pressureConfig.duration * 60 * 1000;
|
||||
if (intervalTime < duration) {
|
||||
return {
|
||||
pass: false,
|
||||
info: this.$t('load_test.schedule_tip')
|
||||
}
|
||||
}
|
||||
// 基本配置
|
||||
this.testPlan.updatedFileList = this.$refs.basicConfig.updatedFileList();
|
||||
// 压力配置
|
||||
this.testPlan.loadConfiguration = JSON.stringify(this.$refs.pressureConfig.convertProperty());
|
||||
this.testPlan.testResourcePoolId = this.$refs.pressureConfig.resourcePool;
|
||||
// 高级配置
|
||||
this.testPlan.advancedConfiguration = JSON.stringify(this.$refs.advancedConfig.configurations());
|
||||
|
||||
// file属性不需要json化
|
||||
let requestJson = JSON.stringify(this.testPlan, function (key, value) {
|
||||
return key === "file" ? undefined : value
|
||||
});
|
||||
|
||||
formData.append('request', new Blob([requestJson], {
|
||||
type: "application/json"
|
||||
}));
|
||||
|
||||
return {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
data: formData,
|
||||
headers: {
|
||||
'Content-Type': undefined
|
||||
}
|
||||
};
|
||||
},
|
||||
cancel() {
|
||||
this.$router.push({path: '/performance/test/all'})
|
||||
},
|
||||
validTestPlan() {
|
||||
if (!this.testPlan.name) {
|
||||
this.$error(this.$t('load_test.test_name_is_null'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.testPlan.projectId) {
|
||||
this.$error(this.$t('load_test.project_is_null'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.$refs.basicConfig.validConfig()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.$refs.pressureConfig.validConfig()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.$refs.advancedConfig.validConfig()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// todo: 其他校验
|
||||
|
||||
return true;
|
||||
},
|
||||
changeTabActive(activeName) {
|
||||
this.$nextTick(() => {
|
||||
this.active = activeName;
|
||||
});
|
||||
},
|
||||
saveCronExpression(cronExpression) {
|
||||
this.testPlan.schedule.enable = true;
|
||||
this.testPlan.schedule.value = cronExpression;
|
||||
this.saveSchedule();
|
||||
},
|
||||
saveSchedule() {
|
||||
this.checkScheduleEdit();
|
||||
let param = {};
|
||||
param = this.testPlan.schedule;
|
||||
param.resourceId = this.testPlan.id;
|
||||
let url = '/performance/schedule/create';
|
||||
if (param.id) {
|
||||
url = '/performance/schedule/update';
|
||||
}
|
||||
this.$post(url, param, response => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.getTest(this.testPlan.id);
|
||||
});
|
||||
},
|
||||
checkScheduleEdit() {
|
||||
if (!this.testPlan.id) {
|
||||
this.$message(this.$t('api_test.environment.please_save_test'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
durationValidate(intervalTime) {
|
||||
let duration = this.$refs.pressureConfig.duration * 60 * 1000;
|
||||
if (intervalTime < duration) {
|
||||
return {
|
||||
pass: true
|
||||
pass: false,
|
||||
info: this.$t('load_test.schedule_tip')
|
||||
}
|
||||
}
|
||||
return {
|
||||
pass: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.testplan-config {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.testplan-config {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
min-width: 130px;
|
||||
}
|
||||
.el-select {
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.edit-testplan-container .input-with-select .el-input-group__prepend {
|
||||
background-color: #fff;
|
||||
}
|
||||
.edit-testplan-container .input-with-select .el-input-group__prepend {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.advanced-config {
|
||||
height: calc(100vh - 280px);
|
||||
overflow: auto;
|
||||
}
|
||||
.advanced-config {
|
||||
height: calc(100vh - 280px);
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
<div>{{ row.description }}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
|
@ -30,13 +30,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsPerformanceTestStatus",
|
||||
export default {
|
||||
name: "MsPerformanceTestStatus",
|
||||
|
||||
props: {
|
||||
row: Object
|
||||
}
|
||||
props: {
|
||||
row: Object
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
<div>
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<h3>{{$t('load_test.params')}}</h3>
|
||||
<el-button :disabled="readOnly" icon="el-icon-circle-plus-outline" plain size="mini" @click="add('params')">{{$t('commons.add')}}
|
||||
<h3>{{ $t('load_test.params') }}</h3>
|
||||
<el-button :disabled="readOnly" icon="el-icon-circle-plus-outline" plain size="mini" @click="add('params')">
|
||||
{{ $t('commons.add') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -26,7 +27,7 @@
|
|||
:placeholder="$t('load_test.param_name')"
|
||||
clearable>
|
||||
</el-input>
|
||||
<span>{{row.name}}</span>
|
||||
<span>{{ row.name }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
@ -60,12 +61,13 @@
|
|||
v-model="row.value"
|
||||
:placeholder="$t('load_test.param_value')"
|
||||
clearable></el-input>
|
||||
<span>{{row.value}}</span>
|
||||
<span>{{ row.value }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" :label="$t('load_test.operating')">
|
||||
<template v-slot:default="{row, $index}">
|
||||
<ms-table-operator-button :disabled="readOnly" :tip="$t('commons.delete')" icon="el-icon-delete" type="danger"
|
||||
<ms-table-operator-button :disabled="readOnly" :tip="$t('commons.delete')" icon="el-icon-delete"
|
||||
type="danger"
|
||||
@exec="del(row, 'params', $index)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -77,10 +79,11 @@
|
|||
<el-col :span="8">
|
||||
<el-form :inline="true">
|
||||
<el-form-item>
|
||||
<div>{{$t('load_test.connect_timeout')}}</div>
|
||||
<div>{{ $t('load_test.connect_timeout') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input-number :disabled="readOnly" size="mini" v-model="timeout" :min="10" :max="100000"></el-input-number>
|
||||
<el-input-number :disabled="readOnly" size="mini" v-model="timeout" :min="10"
|
||||
:max="100000"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
ms
|
||||
|
@ -92,10 +95,11 @@
|
|||
<el-col :span="8">
|
||||
<el-form :inline="true">
|
||||
<el-form-item>
|
||||
<div>{{$t('load_test.custom_http_code')}}</div>
|
||||
<div>{{ $t('load_test.custom_http_code') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input :disabled="readOnly" size="mini" v-model="statusCodeStr" :placeholder="$t('load_test.separated_by_commas')"
|
||||
<el-input :disabled="readOnly" size="mini" v-model="statusCodeStr"
|
||||
:placeholder="$t('load_test.separated_by_commas')"
|
||||
@input="checkStatusCode"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -105,169 +109,169 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
|
||||
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
|
||||
|
||||
export default {
|
||||
name: "PerformanceAdvancedConfig",
|
||||
components: {MsTableOperatorButton},
|
||||
data() {
|
||||
return {
|
||||
timeout: 2000,
|
||||
statusCode: [],
|
||||
domains: [],
|
||||
params: [],
|
||||
statusCodeStr: '',
|
||||
}
|
||||
export default {
|
||||
name: "PerformanceAdvancedConfig",
|
||||
components: {MsTableOperatorButton},
|
||||
data() {
|
||||
return {
|
||||
timeout: 2000,
|
||||
statusCode: [],
|
||||
domains: [],
|
||||
params: [],
|
||||
statusCodeStr: '',
|
||||
}
|
||||
},
|
||||
props: {
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
props: {
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
testId: String,
|
||||
},
|
||||
mounted() {
|
||||
testId: String,
|
||||
},
|
||||
mounted() {
|
||||
if (this.testId) {
|
||||
this.getAdvancedConfig();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
testId() {
|
||||
if (this.testId) {
|
||||
this.getAdvancedConfig();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
testId () {
|
||||
if (this.testId) {
|
||||
this.getAdvancedConfig();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAdvancedConfig() {
|
||||
this.$get('/performance/get-advanced-config/' + this.testId, (response) => {
|
||||
if (response.data) {
|
||||
let data = JSON.parse(response.data);
|
||||
this.timeout = data.timeout || 10;
|
||||
this.statusCode = data.statusCode || [];
|
||||
this.statusCodeStr = this.statusCode.join(',');
|
||||
this.domains = data.domains || [];
|
||||
this.params = data.params || [];
|
||||
/*this.domains.forEach(d => d.edit = false);
|
||||
this.params.forEach(d => d.edit = false);*/
|
||||
}
|
||||
});
|
||||
},
|
||||
add(dataName) {
|
||||
if (dataName === 'domains') {
|
||||
this[dataName].push({
|
||||
domain: 'fit2cloud.com',
|
||||
enable: true,
|
||||
ip: '127.0.0.1',
|
||||
edit: true,
|
||||
});
|
||||
}
|
||||
if (dataName === 'params') {
|
||||
this[dataName].push({
|
||||
name: 'param1',
|
||||
enable: true,
|
||||
value: '0',
|
||||
edit: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getAdvancedConfig() {
|
||||
this.$get('/performance/get-advanced-config/' + this.testId, (response) => {
|
||||
if (response.data) {
|
||||
let data = JSON.parse(response.data);
|
||||
this.timeout = data.timeout || 10;
|
||||
this.statusCode = data.statusCode || [];
|
||||
this.statusCodeStr = this.statusCode.join(',');
|
||||
this.domains = data.domains || [];
|
||||
this.params = data.params || [];
|
||||
/*this.domains.forEach(d => d.edit = false);
|
||||
this.params.forEach(d => d.edit = false);*/
|
||||
}
|
||||
});
|
||||
},
|
||||
add(dataName) {
|
||||
if (dataName === 'domains') {
|
||||
this[dataName].push({
|
||||
domain: 'fit2cloud.com',
|
||||
enable: true,
|
||||
ip: '127.0.0.1',
|
||||
edit: true,
|
||||
});
|
||||
edit(row) {
|
||||
row.edit = !row.edit
|
||||
},
|
||||
del(row, dataName, index) {
|
||||
this[dataName].splice(index, 1);
|
||||
},
|
||||
confirmEdit(row) {
|
||||
row.edit = false;
|
||||
row.enable = true;
|
||||
},
|
||||
groupBy(data, key) {
|
||||
return data.reduce((p, c) => {
|
||||
let name = c[key];
|
||||
if (!p.hasOwnProperty(name)) {
|
||||
p[name] = 0;
|
||||
}
|
||||
if (dataName === 'params') {
|
||||
this[dataName].push({
|
||||
name: 'param1',
|
||||
enable: true,
|
||||
value: '0',
|
||||
edit: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
edit(row) {
|
||||
row.edit = !row.edit
|
||||
},
|
||||
del(row, dataName, index) {
|
||||
this[dataName].splice(index, 1);
|
||||
},
|
||||
confirmEdit(row) {
|
||||
row.edit = false;
|
||||
row.enable = true;
|
||||
},
|
||||
groupBy(data, key) {
|
||||
return data.reduce((p, c) => {
|
||||
let name = c[key];
|
||||
if (!p.hasOwnProperty(name)) {
|
||||
p[name] = 0;
|
||||
}
|
||||
p[name]++;
|
||||
return p;
|
||||
}, {});
|
||||
},
|
||||
validConfig() {
|
||||
let counts = this.groupBy(this.domains, 'domain');
|
||||
for (let c in counts) {
|
||||
if (counts[c] > 1) {
|
||||
this.$error(this.$t('load_test.domain_is_duplicate'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
counts = this.groupBy(this.params, 'name');
|
||||
for (let c in counts) {
|
||||
if (counts[c] > 1) {
|
||||
this.$error(this.$t('load_test.param_is_duplicate'));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.domains.filter(d => !d.domain || !d.ip).length > 0) {
|
||||
this.$error(this.$t('load_test.domain_ip_is_empty'));
|
||||
p[name]++;
|
||||
return p;
|
||||
}, {});
|
||||
},
|
||||
validConfig() {
|
||||
let counts = this.groupBy(this.domains, 'domain');
|
||||
for (let c in counts) {
|
||||
if (counts[c] > 1) {
|
||||
this.$error(this.$t('load_test.domain_is_duplicate'));
|
||||
return false;
|
||||
}
|
||||
if (this.params.filter(d => !d.name || !d.value).length > 0) {
|
||||
this.$error(this.$t('load_test.param_name_value_is_empty'));
|
||||
}
|
||||
counts = this.groupBy(this.params, 'name');
|
||||
for (let c in counts) {
|
||||
if (counts[c] > 1) {
|
||||
this.$error(this.$t('load_test.param_is_duplicate'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
checkStatusCode() {
|
||||
let license_num = this.statusCodeStr;
|
||||
license_num = license_num.replace(/[^\d,]/g, ''); // 清除“数字”和“.”以外的字符
|
||||
this.statusCodeStr = license_num;
|
||||
},
|
||||
cancelAllEdit() {
|
||||
this.domains.forEach(d => d.edit = false);
|
||||
this.params.forEach(d => d.edit = false);
|
||||
},
|
||||
configurations() {
|
||||
let statusCode = [];
|
||||
if (this.statusCodeStr) {
|
||||
statusCode = this.statusCodeStr.split(',');
|
||||
}
|
||||
return {
|
||||
timeout: this.timeout,
|
||||
statusCode: statusCode,
|
||||
params: this.params,
|
||||
domains: this.domains,
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
if (this.domains.filter(d => !d.domain || !d.ip).length > 0) {
|
||||
this.$error(this.$t('load_test.domain_ip_is_empty'));
|
||||
return false;
|
||||
}
|
||||
if (this.params.filter(d => !d.name || !d.value).length > 0) {
|
||||
this.$error(this.$t('load_test.param_name_value_is_empty'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
checkStatusCode() {
|
||||
let license_num = this.statusCodeStr;
|
||||
license_num = license_num.replace(/[^\d,]/g, ''); // 清除“数字”和“.”以外的字符
|
||||
this.statusCodeStr = license_num;
|
||||
},
|
||||
cancelAllEdit() {
|
||||
this.domains.forEach(d => d.edit = false);
|
||||
this.params.forEach(d => d.edit = false);
|
||||
},
|
||||
configurations() {
|
||||
let statusCode = [];
|
||||
if (this.statusCodeStr) {
|
||||
statusCode = this.statusCodeStr.split(',');
|
||||
}
|
||||
return {
|
||||
timeout: this.timeout,
|
||||
statusCode: statusCode,
|
||||
params: this.params,
|
||||
domains: this.domains,
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-row {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.el-row {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
padding-right: 0px;
|
||||
}
|
||||
.edit-input {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.tb-edit .el-textarea {
|
||||
display: none;
|
||||
}
|
||||
.tb-edit .el-textarea {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tb-edit .current-row .el-textarea {
|
||||
display: block;
|
||||
}
|
||||
.tb-edit .current-row .el-textarea {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tb-edit .current-row .el-textarea + span {
|
||||
display: none;
|
||||
}
|
||||
.tb-edit .current-row .el-textarea + span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.el-col {
|
||||
text-align: left;
|
||||
}
|
||||
.el-col {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.el-col .el-table {
|
||||
align: center;
|
||||
}
|
||||
.el-col .el-table {
|
||||
align: center;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<i class="el-icon-upload"/>
|
||||
<div class="el-upload__text" v-html="$t('load_test.upload_tips')"></div>
|
||||
<template v-slot:tip>
|
||||
<div class="el-upload__tip">{{$t('load_test.upload_type')}}</div>
|
||||
<div class="el-upload__tip">{{ $t('load_test.upload_type') }}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
|
||||
|
@ -42,9 +42,11 @@
|
|||
<el-table-column
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<el-button @click="handleDownload(scope.row)" :disabled="!scope.row.id || isReadOnly" type="primary" icon="el-icon-download"
|
||||
<el-button @click="handleDownload(scope.row)" :disabled="!scope.row.id || isReadOnly" type="primary"
|
||||
icon="el-icon-download"
|
||||
size="mini" circle/>
|
||||
<el-button :disabled="isReadOnly" @click="handleDelete(scope.row, scope.$index)" type="danger" icon="el-icon-delete" size="mini"
|
||||
<el-button :disabled="isReadOnly" @click="handleDelete(scope.row, scope.$index)" type="danger"
|
||||
icon="el-icon-delete" size="mini"
|
||||
circle/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -53,170 +55,170 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {Message} from "element-ui";
|
||||
import {Message} from "element-ui";
|
||||
|
||||
export default {
|
||||
name: "PerformanceBasicConfig",
|
||||
props: {
|
||||
testPlan: {
|
||||
type: Object
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
export default {
|
||||
name: "PerformanceBasicConfig",
|
||||
props: {
|
||||
testPlan: {
|
||||
type: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
getFileMetadataPath: "/performance/file/metadata",
|
||||
jmxDownloadPath: '/performance/file/download',
|
||||
jmxDeletePath: '/performance/file/delete',
|
||||
fileList: [],
|
||||
tableData: [],
|
||||
uploadList: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
getFileMetadataPath: "/performance/file/metadata",
|
||||
jmxDownloadPath: '/performance/file/download',
|
||||
jmxDeletePath: '/performance/file/delete',
|
||||
fileList: [],
|
||||
tableData: [],
|
||||
uploadList: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
if (this.testPlan.id) {
|
||||
this.getFileMetadata(this.testPlan)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
testPlan() {
|
||||
if (this.testPlan.id) {
|
||||
this.getFileMetadata(this.testPlan)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
testPlan() {
|
||||
if (this.testPlan.id) {
|
||||
this.getFileMetadata(this.testPlan)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFileMetadata(testPlan) {
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
this.uploadList = [];
|
||||
this.result = this.$get(this.getFileMetadataPath + "/" + testPlan.id, response => {
|
||||
let files = response.data;
|
||||
|
||||
if (!files) {
|
||||
Message.error({message: this.$t('load_test.related_file_not_found'), showClose: true});
|
||||
return;
|
||||
}
|
||||
// deep copy
|
||||
this.fileList = JSON.parse(JSON.stringify(files));
|
||||
this.tableData = JSON.parse(JSON.stringify(files));
|
||||
this.tableData.map(f => {
|
||||
f.size = f.size + ' Bytes';
|
||||
});
|
||||
})
|
||||
},
|
||||
beforeUpload(file) {
|
||||
if (!this.fileValidator(file)) {
|
||||
/// todo: 显示错误信息
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.tableData.filter(f => f.name === file.name).length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
return false;
|
||||
}
|
||||
|
||||
let type = file.name.substring(file.name.lastIndexOf(".") + 1);
|
||||
|
||||
this.tableData.push({
|
||||
name: file.name,
|
||||
size: file.size + ' Bytes', /// todo: 按照大小显示Byte、KB、MB等
|
||||
type: type.toUpperCase(),
|
||||
updateTime: file.lastModified,
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
handleUpload(uploadResources) {
|
||||
this.uploadList.push(uploadResources.file);
|
||||
},
|
||||
handleDownload(file) {
|
||||
let data = {
|
||||
name: file.name,
|
||||
id: file.id,
|
||||
};
|
||||
let config = {
|
||||
url: this.jmxDownloadPath,
|
||||
method: 'post',
|
||||
data: data,
|
||||
responseType: 'blob'
|
||||
};
|
||||
this.result = this.$request(config).then(response => {
|
||||
const content = response.data;
|
||||
const blob = new Blob([content]);
|
||||
if ("download" in document.createElement("a")) {
|
||||
// 非IE下载
|
||||
// chrome/firefox
|
||||
let aTag = document.createElement('a');
|
||||
aTag.download = file.name;
|
||||
aTag.href = URL.createObjectURL(blob);
|
||||
aTag.click();
|
||||
URL.revokeObjectURL(aTag.href)
|
||||
} else {
|
||||
// IE10+下载
|
||||
navigator.msSaveBlob(blob, this.filename)
|
||||
}
|
||||
}).catch(e => {
|
||||
Message.error({message: e.message, showClose: true});
|
||||
});
|
||||
},
|
||||
handleDelete(file, index) {
|
||||
this.$alert(this.$t('load_test.delete_file_confirm') + file.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this._handleDelete(file, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
_handleDelete(file, index) {
|
||||
this.fileList.splice(index, 1);
|
||||
this.tableData.splice(index, 1);
|
||||
//
|
||||
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
|
||||
if (i > -1) {
|
||||
this.uploadList.splice(i, 1);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFileMetadata(testPlan) {
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
this.uploadList = [];
|
||||
this.result = this.$get(this.getFileMetadataPath + "/" + testPlan.id, response => {
|
||||
let files = response.data;
|
||||
|
||||
if (!files) {
|
||||
Message.error({message: this.$t('load_test.related_file_not_found'), showClose: true});
|
||||
return;
|
||||
}
|
||||
// deep copy
|
||||
this.fileList = JSON.parse(JSON.stringify(files));
|
||||
this.tableData = JSON.parse(JSON.stringify(files));
|
||||
this.tableData.map(f => {
|
||||
f.size = f.size + ' Bytes';
|
||||
});
|
||||
})
|
||||
},
|
||||
beforeUpload(file) {
|
||||
if (!this.fileValidator(file)) {
|
||||
/// todo: 显示错误信息
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.tableData.filter(f => f.name === file.name).length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
return false;
|
||||
}
|
||||
|
||||
let type = file.name.substring(file.name.lastIndexOf(".") + 1);
|
||||
|
||||
this.tableData.push({
|
||||
name: file.name,
|
||||
size: file.size + ' Bytes', /// todo: 按照大小显示Byte、KB、MB等
|
||||
type: type.toUpperCase(),
|
||||
updateTime: file.lastModified,
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
handleUpload(uploadResources) {
|
||||
this.uploadList.push(uploadResources.file);
|
||||
},
|
||||
handleDownload(file) {
|
||||
let data = {
|
||||
name: file.name,
|
||||
id: file.id,
|
||||
};
|
||||
let config = {
|
||||
url: this.jmxDownloadPath,
|
||||
method: 'post',
|
||||
data: data,
|
||||
responseType: 'blob'
|
||||
};
|
||||
this.result = this.$request(config).then(response => {
|
||||
const content = response.data;
|
||||
const blob = new Blob([content]);
|
||||
if ("download" in document.createElement("a")) {
|
||||
// 非IE下载
|
||||
// chrome/firefox
|
||||
let aTag = document.createElement('a');
|
||||
aTag.download = file.name;
|
||||
aTag.href = URL.createObjectURL(blob);
|
||||
aTag.click();
|
||||
URL.revokeObjectURL(aTag.href)
|
||||
} else {
|
||||
// IE10+下载
|
||||
navigator.msSaveBlob(blob, this.filename)
|
||||
}
|
||||
}).catch(e => {
|
||||
Message.error({message: e.message, showClose: true});
|
||||
});
|
||||
},
|
||||
handleDelete(file, index) {
|
||||
this.$alert(this.$t('load_test.delete_file_confirm') + file.name + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
this._handleDelete(file, index);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
_handleDelete(file, index) {
|
||||
this.fileList.splice(index, 1);
|
||||
this.tableData.splice(index, 1);
|
||||
//
|
||||
let i = this.uploadList.findIndex(upLoadFile => upLoadFile.name === file.name);
|
||||
if (i > -1) {
|
||||
this.uploadList.splice(i, 1);
|
||||
}
|
||||
},
|
||||
handleExceed() {
|
||||
this.$error(this.$t('load_test.file_size_limit'));
|
||||
},
|
||||
fileValidator(file) {
|
||||
/// todo: 是否需要对文件内容和大小做限制
|
||||
return file.size > 0;
|
||||
},
|
||||
updatedFileList() {
|
||||
return this.fileList;// 表示修改了已经上传的文件列表
|
||||
},
|
||||
validConfig() {
|
||||
let newJmxNum = 0, oldJmxNum = 0;
|
||||
if (this.uploadList.length > 0) {
|
||||
newJmxNum = this.uploadList.filter(f => f.name.toLowerCase().endsWith(".jmx")).length;
|
||||
}
|
||||
if (this.fileList.length > 0) {
|
||||
oldJmxNum = this.fileList.filter(f => f.name.toLowerCase().endsWith(".jmx")).length;
|
||||
}
|
||||
if (newJmxNum + oldJmxNum !== 1) {
|
||||
this.$error(this.$t('load_test.jmx_is_null'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
handleExceed() {
|
||||
this.$error(this.$t('load_test.file_size_limit'));
|
||||
},
|
||||
}
|
||||
fileValidator(file) {
|
||||
/// todo: 是否需要对文件内容和大小做限制
|
||||
return file.size > 0;
|
||||
},
|
||||
updatedFileList() {
|
||||
return this.fileList;// 表示修改了已经上传的文件列表
|
||||
},
|
||||
validConfig() {
|
||||
let newJmxNum = 0, oldJmxNum = 0;
|
||||
if (this.uploadList.length > 0) {
|
||||
newJmxNum = this.uploadList.filter(f => f.name.toLowerCase().endsWith(".jmx")).length;
|
||||
}
|
||||
if (this.fileList.length > 0) {
|
||||
oldJmxNum = this.fileList.filter(f => f.name.toLowerCase().endsWith(".jmx")).length;
|
||||
}
|
||||
if (newJmxNum + oldJmxNum !== 1) {
|
||||
this.$error(this.$t('load_test.jmx_is_null'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.basic-config {
|
||||
width: 100%
|
||||
}
|
||||
.basic-config {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.last-modified {
|
||||
margin-left: 5px;
|
||||
}
|
||||
.last-modified {
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<el-col :span="10">
|
||||
<el-form :inline="true">
|
||||
<el-form-item>
|
||||
<div class="config-form-label">{{$t('load_test.thread_num')}}</div>
|
||||
<div class="config-form-label">{{ $t('load_test.thread_num') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input-number
|
||||
|
@ -18,7 +18,7 @@
|
|||
</el-form>
|
||||
<el-form :inline="true">
|
||||
<el-form-item>
|
||||
<div class="config-form-label">{{$t('load_test.duration')}}</div>
|
||||
<div class="config-form-label">{{ $t('load_test.duration') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input-number
|
||||
|
@ -33,7 +33,7 @@
|
|||
<el-form :inline="true">
|
||||
<el-form-item>
|
||||
<el-form-item>
|
||||
<div class="config-form-label">{{$t('load_test.rps_limit')}}</div>
|
||||
<div class="config-form-label">{{ $t('load_test.rps_limit') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-switch v-model="rpsLimitEnable"/>
|
||||
|
@ -52,7 +52,7 @@
|
|||
|
||||
<el-form :inline="true" class="input-bottom-border">
|
||||
<el-form-item>
|
||||
<div>{{$t('load_test.ramp_up_time_within')}}</div>
|
||||
<div>{{ $t('load_test.ramp_up_time_within') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input-number
|
||||
|
@ -65,7 +65,7 @@
|
|||
size="mini"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div>{{$t('load_test.ramp_up_time_minutes')}}</div>
|
||||
<div>{{ $t('load_test.ramp_up_time_minutes') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input-number
|
||||
|
@ -78,12 +78,12 @@
|
|||
size="mini"/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div>{{$t('load_test.ramp_up_time_times')}}</div>
|
||||
<div>{{ $t('load_test.ramp_up_time_times') }}</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form :inline="true" class="input-bottom-border">
|
||||
<el-form-item>
|
||||
<div>{{$t('load_test.select_resource_pool')}}</div>
|
||||
<div>{{ $t('load_test.select_resource_pool') }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="resourcePool" :disabled="isReadOnly" size="mini">
|
||||
|
@ -98,7 +98,7 @@
|
|||
</el-form>
|
||||
</el-col>
|
||||
<el-col :span="14">
|
||||
<div class="title">{{$t('load_test.pressure_prediction_chart')}}</div>
|
||||
<div class="title">{{ $t('load_test.pressure_prediction_chart') }}</div>
|
||||
<chart class="chart-container" ref="chart1" :options="orgOptions" :autoresize="true"></chart>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
@ -106,263 +106,263 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from "echarts";
|
||||
import echarts from "echarts";
|
||||
|
||||
const TARGET_LEVEL = "TargetLevel";
|
||||
const RAMP_UP = "RampUp";
|
||||
const STEPS = "Steps";
|
||||
const DURATION = "duration";
|
||||
const RPS_LIMIT = "rpsLimit";
|
||||
const RPS_LIMIT_ENABLE = "rpsLimitEnable";
|
||||
const TARGET_LEVEL = "TargetLevel";
|
||||
const RAMP_UP = "RampUp";
|
||||
const STEPS = "Steps";
|
||||
const DURATION = "duration";
|
||||
const RPS_LIMIT = "rpsLimit";
|
||||
const RPS_LIMIT_ENABLE = "rpsLimitEnable";
|
||||
|
||||
export default {
|
||||
name: "PerformancePressureConfig",
|
||||
props: {
|
||||
testPlan: {
|
||||
type: Object
|
||||
},
|
||||
testId: {
|
||||
type: String
|
||||
},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
export default {
|
||||
name: "PerformancePressureConfig",
|
||||
props: {
|
||||
testPlan: {
|
||||
type: Object
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
threadNumber: 10,
|
||||
duration: 10,
|
||||
rampUpTime: 10,
|
||||
step: 10,
|
||||
rpsLimit: 10,
|
||||
rpsLimitEnable: false,
|
||||
orgOptions: {},
|
||||
resourcePool: null,
|
||||
resourcePools: [],
|
||||
}
|
||||
testId: {
|
||||
type: String
|
||||
},
|
||||
mounted() {
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
threadNumber: 10,
|
||||
duration: 10,
|
||||
rampUpTime: 10,
|
||||
step: 10,
|
||||
rpsLimit: 10,
|
||||
rpsLimitEnable: false,
|
||||
orgOptions: {},
|
||||
resourcePool: null,
|
||||
resourcePools: [],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.testId) {
|
||||
this.getLoadConfig();
|
||||
} else {
|
||||
this.calculateChart();
|
||||
}
|
||||
this.resourcePool = this.testPlan.testResourcePoolId;
|
||||
this.getResourcePools();
|
||||
},
|
||||
watch: {
|
||||
testPlan(n) {
|
||||
this.resourcePool = n.testResourcePoolId;
|
||||
},
|
||||
testId() {
|
||||
if (this.testId) {
|
||||
this.getLoadConfig();
|
||||
} else {
|
||||
this.calculateChart();
|
||||
}
|
||||
this.resourcePool = this.testPlan.testResourcePoolId;
|
||||
this.getResourcePools();
|
||||
},
|
||||
watch: {
|
||||
testPlan(n) {
|
||||
this.resourcePool = n.testResourcePoolId;
|
||||
},
|
||||
testId() {
|
||||
if (this.testId) {
|
||||
this.getLoadConfig();
|
||||
} else {
|
||||
this.calculateChart();
|
||||
},
|
||||
methods: {
|
||||
getResourcePools() {
|
||||
this.result = this.$get('/testresourcepool/list/quota/valid', response => {
|
||||
this.resourcePools = response.data;
|
||||
// 如果当前的资源池无效 设置 null
|
||||
if (response.data.filter(p => p.id === this.resourcePool).length === 0) {
|
||||
this.resourcePool = null;
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getResourcePools() {
|
||||
this.result = this.$get('/testresourcepool/list/all/valid', response => {
|
||||
this.resourcePools = response.data;
|
||||
// 如果当前的资源池无效 设置 null
|
||||
if (response.data.filter(p => p.id === this.resourcePool).length === 0) {
|
||||
this.resourcePool = null;
|
||||
getLoadConfig() {
|
||||
if (this.testId) {
|
||||
|
||||
this.$get('/performance/get-load-config/' + this.testId, (response) => {
|
||||
if (response.data) {
|
||||
let data = JSON.parse(response.data);
|
||||
|
||||
data.forEach(d => {
|
||||
switch (d.key) {
|
||||
case TARGET_LEVEL:
|
||||
this.threadNumber = d.value;
|
||||
break;
|
||||
case RAMP_UP:
|
||||
this.rampUpTime = d.value;
|
||||
break;
|
||||
case DURATION:
|
||||
this.duration = d.value;
|
||||
break;
|
||||
case STEPS:
|
||||
this.step = d.value;
|
||||
break;
|
||||
case RPS_LIMIT:
|
||||
this.rpsLimit = d.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this.threadNumber = this.threadNumber || 10;
|
||||
this.duration = this.duration || 30;
|
||||
this.rampUpTime = this.rampUpTime || 12;
|
||||
this.step = this.step || 3;
|
||||
this.rpsLimit = this.rpsLimit || 10;
|
||||
|
||||
this.calculateChart();
|
||||
}
|
||||
})
|
||||
},
|
||||
getLoadConfig() {
|
||||
if (this.testId) {
|
||||
|
||||
this.$get('/performance/get-load-config/' + this.testId, (response) => {
|
||||
if (response.data) {
|
||||
let data = JSON.parse(response.data);
|
||||
|
||||
data.forEach(d => {
|
||||
switch (d.key) {
|
||||
case TARGET_LEVEL:
|
||||
this.threadNumber = d.value;
|
||||
break;
|
||||
case RAMP_UP:
|
||||
this.rampUpTime = d.value;
|
||||
break;
|
||||
case DURATION:
|
||||
this.duration = d.value;
|
||||
break;
|
||||
case STEPS:
|
||||
this.step = d.value;
|
||||
break;
|
||||
case RPS_LIMIT:
|
||||
this.rpsLimit = d.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this.threadNumber = this.threadNumber || 10;
|
||||
this.duration = this.duration || 30;
|
||||
this.rampUpTime = this.rampUpTime || 12;
|
||||
this.step = this.step || 3;
|
||||
this.rpsLimit = this.rpsLimit || 10;
|
||||
|
||||
this.calculateChart();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
calculateChart() {
|
||||
if (this.duration < this.rampUpTime) {
|
||||
this.rampUpTime = this.duration;
|
||||
}
|
||||
if (this.rampUpTime < this.step) {
|
||||
this.step = this.rampUpTime;
|
||||
}
|
||||
this.orgOptions = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: []
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: '{a}: {c0}',
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: 'User',
|
||||
data: [],
|
||||
type: 'line',
|
||||
step: 'start',
|
||||
smooth: false,
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgba(137, 189, 27, 0.3)'
|
||||
}, {
|
||||
offset: 0.8,
|
||||
color: 'rgba(137, 189, 27, 0)'
|
||||
}], false),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(137,189,27)',
|
||||
borderColor: 'rgba(137,189,2,0.27)',
|
||||
borderWidth: 12
|
||||
}
|
||||
},
|
||||
}]
|
||||
};
|
||||
let timePeriod = Math.floor(this.rampUpTime / this.step);
|
||||
let timeInc = timePeriod;
|
||||
|
||||
let threadPeriod = Math.floor(this.threadNumber / this.step);
|
||||
let threadInc1 = Math.floor(this.threadNumber / this.step);
|
||||
let threadInc2 = Math.ceil(this.threadNumber / this.step);
|
||||
let inc2count = this.threadNumber - this.step * threadInc1;
|
||||
for (let i = 0; i <= this.duration; i++) {
|
||||
// x 轴
|
||||
this.orgOptions.xAxis.data.push(i);
|
||||
if (i > timePeriod) {
|
||||
timePeriod += timeInc;
|
||||
if (inc2count > 0) {
|
||||
threadPeriod = threadPeriod + threadInc2;
|
||||
inc2count--;
|
||||
} else {
|
||||
threadPeriod = threadPeriod + threadInc1;
|
||||
}
|
||||
if (threadPeriod > this.threadNumber) {
|
||||
threadPeriod = this.threadNumber;
|
||||
}
|
||||
this.orgOptions.series[0].data.push(threadPeriod);
|
||||
} else {
|
||||
this.orgOptions.series[0].data.push(threadPeriod);
|
||||
}
|
||||
}
|
||||
},
|
||||
validConfig() {
|
||||
if (!this.resourcePool) {
|
||||
this.$warning(this.$t('load_test.resource_pool_is_null'));
|
||||
// 资源池为空,设置参数为资源池所在Tab的name
|
||||
this.$emit('changeActive', '1');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.threadNumber || !this.duration || !this.rampUpTime || !this.step || !this.rpsLimit) {
|
||||
this.$warning(this.$t('load_test.pressure_config_params_is_empty'));
|
||||
this.$emit('changeActive', '1');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
convertProperty() {
|
||||
/// todo:下面4个属性是jmeter ConcurrencyThreadGroup plugin的属性,这种硬编码不太好吧,在哪能转换这种属性?
|
||||
return [
|
||||
{key: TARGET_LEVEL, value: this.threadNumber},
|
||||
{key: RAMP_UP, value: this.rampUpTime},
|
||||
{key: STEPS, value: this.step},
|
||||
{key: DURATION, value: this.duration},
|
||||
{key: RPS_LIMIT, value: this.rpsLimit},
|
||||
{key: RPS_LIMIT_ENABLE, value: this.rpsLimitEnable},
|
||||
];
|
||||
});
|
||||
}
|
||||
},
|
||||
calculateChart() {
|
||||
if (this.duration < this.rampUpTime) {
|
||||
this.rampUpTime = this.duration;
|
||||
}
|
||||
if (this.rampUpTime < this.step) {
|
||||
this.step = this.rampUpTime;
|
||||
}
|
||||
this.orgOptions = {
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: []
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter: '{a}: {c0}',
|
||||
axisPointer: {
|
||||
lineStyle: {
|
||||
color: '#57617B'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
name: 'User',
|
||||
data: [],
|
||||
type: 'line',
|
||||
step: 'start',
|
||||
smooth: false,
|
||||
symbolSize: 5,
|
||||
showSymbol: false,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
width: 1
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
normal: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: 'rgba(137, 189, 27, 0.3)'
|
||||
}, {
|
||||
offset: 0.8,
|
||||
color: 'rgba(137, 189, 27, 0)'
|
||||
}], false),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)',
|
||||
shadowBlur: 10
|
||||
}
|
||||
},
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: 'rgb(137,189,27)',
|
||||
borderColor: 'rgba(137,189,2,0.27)',
|
||||
borderWidth: 12
|
||||
}
|
||||
},
|
||||
}]
|
||||
};
|
||||
let timePeriod = Math.floor(this.rampUpTime / this.step);
|
||||
let timeInc = timePeriod;
|
||||
|
||||
let threadPeriod = Math.floor(this.threadNumber / this.step);
|
||||
let threadInc1 = Math.floor(this.threadNumber / this.step);
|
||||
let threadInc2 = Math.ceil(this.threadNumber / this.step);
|
||||
let inc2count = this.threadNumber - this.step * threadInc1;
|
||||
for (let i = 0; i <= this.duration; i++) {
|
||||
// x 轴
|
||||
this.orgOptions.xAxis.data.push(i);
|
||||
if (i > timePeriod) {
|
||||
timePeriod += timeInc;
|
||||
if (inc2count > 0) {
|
||||
threadPeriod = threadPeriod + threadInc2;
|
||||
inc2count--;
|
||||
} else {
|
||||
threadPeriod = threadPeriod + threadInc1;
|
||||
}
|
||||
if (threadPeriod > this.threadNumber) {
|
||||
threadPeriod = this.threadNumber;
|
||||
}
|
||||
this.orgOptions.series[0].data.push(threadPeriod);
|
||||
} else {
|
||||
this.orgOptions.series[0].data.push(threadPeriod);
|
||||
}
|
||||
}
|
||||
},
|
||||
validConfig() {
|
||||
if (!this.resourcePool) {
|
||||
this.$warning(this.$t('load_test.resource_pool_is_null'));
|
||||
// 资源池为空,设置参数为资源池所在Tab的name
|
||||
this.$emit('changeActive', '1');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.threadNumber || !this.duration || !this.rampUpTime || !this.step || !this.rpsLimit) {
|
||||
this.$warning(this.$t('load_test.pressure_config_params_is_empty'));
|
||||
this.$emit('changeActive', '1');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
convertProperty() {
|
||||
/// todo:下面4个属性是jmeter ConcurrencyThreadGroup plugin的属性,这种硬编码不太好吧,在哪能转换这种属性?
|
||||
return [
|
||||
{key: TARGET_LEVEL, value: this.threadNumber},
|
||||
{key: RAMP_UP, value: this.rampUpTime},
|
||||
{key: STEPS, value: this.step},
|
||||
{key: DURATION, value: this.duration},
|
||||
{key: RPS_LIMIT, value: this.rpsLimit},
|
||||
{key: RPS_LIMIT_ENABLE, value: this.rpsLimitEnable},
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.pressure-config-container .el-input {
|
||||
width: 130px;
|
||||
.pressure-config-container .el-input {
|
||||
width: 130px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.pressure-config-container .config-form-label {
|
||||
width: 130px;
|
||||
}
|
||||
.pressure-config-container .config-form-label {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.pressure-config-container .input-bottom-border input {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #DCDFE6;
|
||||
}
|
||||
.pressure-config-container .input-bottom-border input {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #DCDFE6;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
}
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.el-col .el-form {
|
||||
margin-top: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
.el-col .el-form {
|
||||
margin-top: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.el-col {
|
||||
margin-top: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
.el-col {
|
||||
margin-top: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: 60px;
|
||||
}
|
||||
.title {
|
||||
margin-left: 60px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,86 +1,72 @@
|
|||
import Setting from "@/business/components/settings/Setting";
|
||||
import User from "@/business/components/settings/system/User";
|
||||
import Organization from "@/business/components/settings/system/Organization";
|
||||
import OrganizationMember from "@/business/components/settings/organization/OrganizationMember";
|
||||
import OrganizationWorkspace from "@/business/components/settings/organization/OrganizationWorkspace";
|
||||
import ServiceIntegration from "@/business/components/settings/organization/ServiceIntegration";
|
||||
import PersonSetting from "@/business/components/settings/personal/PersonSetting";
|
||||
import ApiKeys from "@/business/components/settings/personal/ApiKeys";
|
||||
import Member from "@/business/components/settings/workspace/WorkspaceMember";
|
||||
import SystemWorkspace from "@/business/components/settings/system/SystemWorkspace";
|
||||
import TestResourcePool from "@/business/components/settings/system/TestResourcePool";
|
||||
import SystemParameterSetting from "@/business/components/settings/system/SystemParameterSetting";
|
||||
import TestCaseReportTemplate from "@/business/components/settings/workspace/TestCaseReportTemplate";
|
||||
|
||||
const requireContext = require.context('@/business/components/xpack/', true, /router\.js$/)
|
||||
|
||||
export default {
|
||||
path: "/setting",
|
||||
name: "Setting",
|
||||
components: {
|
||||
content: Setting
|
||||
content: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/Setting')
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
component: User,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/system/User'),
|
||||
meta: {system: true, title: 'commons.user'}
|
||||
},
|
||||
{
|
||||
path: 'organization',
|
||||
component: Organization,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/system/Organization'),
|
||||
meta: {system: true, title: 'commons.organization'}
|
||||
},
|
||||
{
|
||||
path: 'systemworkspace',
|
||||
component: SystemWorkspace,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/system/SystemWorkspace'),
|
||||
meta: {system: true, title: 'commons.workspace'}
|
||||
},
|
||||
{
|
||||
path: 'testresourcepool',
|
||||
component: TestResourcePool,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/system/TestResourcePool'),
|
||||
meta: {system: true, title: 'commons.test_resource_pool'}
|
||||
},
|
||||
{
|
||||
path: 'systemparametersetting',
|
||||
component: SystemParameterSetting,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/system/SystemParameterSetting'),
|
||||
meta: {system: true, title: 'commons.system_parameter_setting'}
|
||||
},
|
||||
...requireContext.keys().map(key => requireContext(key).system),
|
||||
{
|
||||
path: 'organizationmember',
|
||||
component: OrganizationMember,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/organization/OrganizationMember'),
|
||||
meta: {organization: true, title: 'commons.member'}
|
||||
},
|
||||
{
|
||||
path: 'organizationworkspace',
|
||||
component: OrganizationWorkspace,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/organization/OrganizationWorkspace'),
|
||||
meta: {organization: true, title: 'commons.workspace'}
|
||||
},
|
||||
{
|
||||
path: 'serviceintegration',
|
||||
component: ServiceIntegration,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/organization/ServiceIntegration'),
|
||||
meta: {organization: true, title: 'organization.service_integration'}
|
||||
},
|
||||
{
|
||||
path: 'member',
|
||||
component: Member,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/workspace/WorkspaceMember'),
|
||||
meta: {workspace: true, title: 'commons.member'}
|
||||
},
|
||||
{
|
||||
path: 'testcase/report/template',
|
||||
name: 'testCaseReportTemplate',
|
||||
component: TestCaseReportTemplate,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/workspace/TestCaseReportTemplate'),
|
||||
meta: {workspace: true, title: 'test_track.plan_view.report_template'}
|
||||
},
|
||||
{
|
||||
path: 'personsetting',
|
||||
component: PersonSetting,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/personal/PersonSetting'),
|
||||
meta: {person: true, title: 'commons.personal_setting'}
|
||||
},
|
||||
{
|
||||
path: 'apikeys',
|
||||
component: ApiKeys,
|
||||
component: () => import(/* webpackChunkName: "setting" */ '@/business/components/settings/personal/ApiKeys'),
|
||||
meta: {person: true, title: 'commons.api_keys'}
|
||||
},
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<el-card class="card-content" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
|
||||
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="initTableData"
|
||||
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="initTableData" :tip="$t('commons.search_by_name_or_id')"
|
||||
:create-tip="$t('test_track.case.create')" @create="testCaseCreate">
|
||||
<template v-slot:title>
|
||||
<node-breadcrumb class="table-title" :nodes="selectParentNodes" @refresh="refresh"/>
|
||||
|
|
|
@ -137,6 +137,9 @@
|
|||
this.getCaseNames();
|
||||
}
|
||||
},
|
||||
updated() {
|
||||
this.toggleSelection(this.testCases);
|
||||
},
|
||||
methods: {
|
||||
openTestCaseRelevanceDialog() {
|
||||
this.initData();
|
||||
|
@ -178,7 +181,12 @@
|
|||
this.selectIds.add(item.id);
|
||||
});
|
||||
} else {
|
||||
this.selectIds.clear();
|
||||
// this.selectIds.clear();
|
||||
this.testCases.forEach(item => {
|
||||
if (this.selectIds.has(item.id)) {
|
||||
this.selectIds.delete(item.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
handleSelectionChange(selection, row) {
|
||||
|
@ -215,6 +223,16 @@
|
|||
_filter(filters, this.condition);
|
||||
this.initData();
|
||||
},
|
||||
toggleSelection(rows) {
|
||||
rows.forEach(row => {
|
||||
this.selectIds.forEach(id => {
|
||||
if (row.id === id) {
|
||||
// true 是为选中
|
||||
this.$refs.table.toggleRowSelection(row, true)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -194,7 +194,7 @@
|
|||
type="text"
|
||||
:placeholder="$t('test_track.issue.input_title')"
|
||||
v-model="testCase.issues.title"
|
||||
maxlength="100"
|
||||
maxlength="60"
|
||||
show-word-limit
|
||||
/>
|
||||
<ckeditor :editor="editor" :disabled="isReadOnly" :config="editorConfig"
|
||||
|
@ -208,7 +208,7 @@
|
|||
<el-col :span="20" :offset="1" class="issues-edit">
|
||||
<el-table border class="adjust-table" :data="issues" style="width: 100%">
|
||||
<el-table-column prop="id" :label="$t('test_track.issue.id')" show-overflow-tooltip/>
|
||||
<el-table-column prop="title" :label="$t('test_track.issue.title')"/>
|
||||
<el-table-column prop="title" :label="$t('test_track.issue.title')" show-overflow-tooltip/>
|
||||
<el-table-column prop="description" :label="$t('test_track.issue.description')">
|
||||
<template v-slot:default="scope">
|
||||
<el-popover
|
||||
|
@ -218,9 +218,7 @@
|
|||
>
|
||||
<ckeditor :editor="editor" disabled
|
||||
v-model="scope.row.description"/>
|
||||
<!-- <span v-html="scope.row.description"/>-->
|
||||
<!-- <span slot="reference">{{scope.row.description}}</span>-->
|
||||
<el-button slot="reference" type="text">预览</el-button>
|
||||
<el-button slot="reference" type="text">{{$t('test_track.issue.preview')}}</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
@ -398,6 +396,7 @@
|
|||
}
|
||||
this.testCase = item;
|
||||
this.initTest();
|
||||
this.getIssues(testCase.caseId);
|
||||
this.stepResultChange();
|
||||
},
|
||||
openTestCaseEdit(testCase) {
|
||||
|
@ -405,7 +404,6 @@
|
|||
this.activeTab = 'detail';
|
||||
listenGoBack(this.handleClose);
|
||||
this.initData(testCase);
|
||||
this.getIssues(testCase.caseId);
|
||||
},
|
||||
initTest() {
|
||||
this.$nextTick(() => {
|
||||
|
@ -445,7 +443,7 @@
|
|||
});
|
||||
},
|
||||
getRelatedTest() {
|
||||
if (this.testCase.method == 'auto' && this.testCase.testId) {
|
||||
if (this.testCase.method == 'auto' && this.testCase.testId && this.testCase.testId != 'other') {
|
||||
this.$get('/' + this.testCase.type + '/get/' + this.testCase.testId, response => {
|
||||
let data = response.data;
|
||||
if (data) {
|
||||
|
@ -455,6 +453,8 @@
|
|||
this.$warning(this.$t("test_track.case.relate_test_not_find"));
|
||||
}
|
||||
});
|
||||
} else if (this.testCase.testId === 'other') {
|
||||
this.$warning(this.$t("test_track.case.other_relate_test_not_find"));
|
||||
}
|
||||
},
|
||||
issuesChange() {
|
||||
|
@ -497,7 +497,10 @@
|
|||
this.result = this.$post("/issues/add", param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.getIssues(param.testCaseId);
|
||||
})
|
||||
});
|
||||
this.issuesSwitch = false;
|
||||
this.testCase.issues.title = "";
|
||||
this.testCase.issues.content = "";
|
||||
},
|
||||
getIssues(caseId) {
|
||||
this.result = this.$get("/issues/get/" + caseId, response => {
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
trigger="hover">
|
||||
<el-table border class="adjust-table" :data="scope.row.issuesContent" style="width: 100%">
|
||||
<!-- <el-table-column prop="id" label="缺陷ID" show-overflow-tooltip/>-->
|
||||
<el-table-column prop="title" :label="$t('test_track.issue.title')"/>
|
||||
<el-table-column prop="title" :label="$t('test_track.issue.title')" show-overflow-tooltip/>
|
||||
<el-table-column prop="description" :label="$t('test_track.issue.description')">
|
||||
<template v-slot:default="scope">
|
||||
<el-popover
|
||||
|
@ -123,9 +123,7 @@
|
|||
>
|
||||
<ckeditor :editor="editor" disabled
|
||||
v-model="scope.row.description"/>
|
||||
<!-- <span v-html="scope.row.description"/>-->
|
||||
<!-- <span slot="reference">{{scope.row.description}}</span>-->
|
||||
<el-button slot="reference" type="text">预览</el-button>
|
||||
<el-button slot="reference" type="text">{{$t('test_track.issue.preview')}}</el-button>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
|
|
@ -32,14 +32,18 @@
|
|||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="reporter"
|
||||
:label="$t('test_track.module.current_owner')"
|
||||
show-overflow-tooltip>
|
||||
prop="lastmodify"
|
||||
:label="$t('test_track.module.current_owner')"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="createTime"
|
||||
:label="$t('test_track.module.creation_time')">
|
||||
prop="createTime"
|
||||
:label="$t('test_track.module.creation_time')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import MsProject from "@/business/components/project/MsProject";
|
||||
|
||||
const TestTrack = () => import(/* webpackChunkName: "track" */ '@/business/components/track/TestTrack')
|
||||
const TrackHome = () => import(/* webpackChunkName: "track" */ '@/business/components/track/home/TrackHome')
|
||||
const TestCase = () => import(/* webpackChunkName: "track" */ '@/business/components/track/case/TestCase')
|
||||
const TestPlan = () => import(/* webpackChunkName: "track" */ '@/business/components/track/plan/TestPlan')
|
||||
const TestPlanView = () => import(/* webpackChunkName: "track" */ '@/business/components/track/plan/view/TestPlanView')
|
||||
|
||||
export default {
|
||||
path: "/track",
|
||||
name: "track",
|
||||
redirect: "/track/home",
|
||||
components: {
|
||||
content: TestTrack
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'home',
|
||||
name: 'trackHome',
|
||||
component: TrackHome,
|
||||
},
|
||||
{
|
||||
path: 'case/create',
|
||||
name: 'testCaseCreate',
|
||||
component: TestCase,
|
||||
},
|
||||
{
|
||||
path: 'case/:projectId',
|
||||
name: 'testCase',
|
||||
component: TestCase,
|
||||
},
|
||||
{
|
||||
path: 'case/edit/:caseId',
|
||||
name: 'testCaseEdit',
|
||||
component: TestCase,
|
||||
},
|
||||
{
|
||||
path: "plan/:type",
|
||||
name: "testPlan",
|
||||
component: TestPlan
|
||||
},
|
||||
{
|
||||
path: "plan/view/:planId",
|
||||
name: "planView",
|
||||
component: TestPlanView
|
||||
},
|
||||
{
|
||||
path: "plan/view/edit/:caseId",
|
||||
name: "planViewEdit",
|
||||
component: TestPlanView
|
||||
},
|
||||
{
|
||||
path: "project/:type",
|
||||
name: "trackProject",
|
||||
component: MsProject
|
||||
}
|
||||
]
|
||||
}
|
|
@ -60,6 +60,7 @@ export default {
|
|||
not_filled: 'Not filled',
|
||||
please_select: 'Please select',
|
||||
search_by_name: 'Search by name',
|
||||
search_by_name_or_id: 'Search by name or id',
|
||||
personal_information: 'Personal Information',
|
||||
exit_system: 'Exit System',
|
||||
verification: 'Verification',
|
||||
|
@ -593,6 +594,7 @@ export default {
|
|||
create_module_first: "Please create module first",
|
||||
relate_test: "Relate test",
|
||||
relate_test_not_find: 'The associated test does not exist, please check the test case',
|
||||
other_relate_test_not_find: 'Associated test name, please go to the third party platform to execute',
|
||||
batch_handle: 'Batch processing (select {0} item)',
|
||||
batch_update: 'Update the attributes of {0} cases',
|
||||
select_catalog: 'Please select use case catalog',
|
||||
|
@ -729,6 +731,7 @@ export default {
|
|||
close: "Close",
|
||||
title_description_required: "Title and description are required",
|
||||
close_success: "Closed successfully",
|
||||
preview: "Preview"
|
||||
}
|
||||
},
|
||||
test_resource_pool: {
|
||||
|
|
|
@ -60,6 +60,7 @@ export default {
|
|||
not_filled: '未填写',
|
||||
please_select: '请选择',
|
||||
search_by_name: '根据名称搜索',
|
||||
search_by_name_or_id: '根据ID或名称搜索',
|
||||
personal_information: '个人信息',
|
||||
exit_system: '退出系统',
|
||||
verification: '验证',
|
||||
|
@ -596,6 +597,7 @@ export default {
|
|||
create_module_first: "请先新建模块",
|
||||
relate_test: "关联测试",
|
||||
relate_test_not_find: '关联的测试不存在,请检查用例',
|
||||
other_relate_test_not_find: '关联的测试名,请前往第三方平台执行',
|
||||
batch_handle: '批量处理 (选中{0}项)',
|
||||
batch_update: '更新{0}个用例的属性',
|
||||
select_catalog: '请选择用例目录',
|
||||
|
@ -732,6 +734,7 @@ export default {
|
|||
close: "关闭缺陷",
|
||||
title_description_required: "标题和描述必填",
|
||||
close_success: "关闭成功",
|
||||
preview: "预览"
|
||||
}
|
||||
},
|
||||
test_resource_pool: {
|
||||
|
|
|
@ -60,6 +60,7 @@ export default {
|
|||
not_filled: '未填寫',
|
||||
please_select: '請選擇',
|
||||
search_by_name: '根據名稱搜索',
|
||||
search_by_name_or_id: '根據ID或名稱搜索',
|
||||
personal_information: '個人信息',
|
||||
exit_system: '退出系統',
|
||||
verification: '驗證',
|
||||
|
@ -593,6 +594,7 @@ export default {
|
|||
create_module_first: "請先新建模塊",
|
||||
relate_test: "關聯測試",
|
||||
relate_test_not_find: '關聯的測試不存在,請檢查用例',
|
||||
other_relate_test_not_find: '關聯的測試名,請前往協力廠商平臺執行',
|
||||
batch_handle: '批量處理 (選中{0}項)',
|
||||
batch_update: '更新{0}個用例的屬性',
|
||||
select_catalog: '請選擇用例目錄',
|
||||
|
@ -729,6 +731,7 @@ export default {
|
|||
close: "關閉缺陷",
|
||||
title_description_required: "標題和描述必填",
|
||||
close_success: "關閉成功",
|
||||
preview: "預覽"
|
||||
}
|
||||
},
|
||||
test_resource_pool: {
|
||||
|
|
Loading…
Reference in New Issue