feat(性能测试): 支持组合场景场景
This commit is contained in:
parent
739d194854
commit
4a9ccc2ea5
|
@ -2,6 +2,7 @@ package io.metersphere.base.mapper.ext;
|
|||
|
||||
import io.metersphere.base.domain.LoadTest;
|
||||
import io.metersphere.dto.LoadTestDTO;
|
||||
import io.metersphere.dto.LoadTestFileDTO;
|
||||
import io.metersphere.track.request.testplan.QueryTestPlanRequest;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
|
@ -16,4 +17,7 @@ public interface ExtLoadTestMapper {
|
|||
int checkLoadTestOwner(@Param("testId") String testId, @Param("workspaceIds") Set<String> workspaceIds);
|
||||
|
||||
LoadTest getNextNum(@Param("projectId") String projectId);
|
||||
|
||||
List<LoadTestFileDTO> getProjectFiles(@Param("projectId") String projectId, @Param("loadTypes") List<String> loadType);
|
||||
|
||||
}
|
||||
|
|
|
@ -131,4 +131,16 @@
|
|||
<select id="getNextNum" resultType="io.metersphere.base.domain.LoadTest">
|
||||
select * from load_test lt where lt.project_id = #{projectId} ORDER BY num DESC LIMIT 1;
|
||||
</select>
|
||||
|
||||
<select id="getProjectFiles" resultType="io.metersphere.dto.LoadTestFileDTO">
|
||||
SELECT file_metadata.*, load_test.id as testId, load_test.name as testName
|
||||
FROM load_test
|
||||
JOIN load_test_file ON load_test.id = load_test_file.test_id
|
||||
JOIN file_metadata ON load_test_file.file_id = file_metadata.id
|
||||
WHERE file_metadata.type IN
|
||||
<foreach collection="loadTypes" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
AND project_id = #{projectId,jdbcType=VARCHAR}
|
||||
</select>
|
||||
</mapper>
|
|
@ -1,20 +1,24 @@
|
|||
package io.metersphere.commons.utils;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
public class UrlTestUtils {
|
||||
|
||||
public static boolean testUrlWithTimeOut(String urlString, int timeOutMillSeconds) {
|
||||
public static boolean testUrlWithTimeOut(String address, int timeOutMillSeconds) {
|
||||
try {
|
||||
URL url = new URL(urlString);
|
||||
URLConnection co = url.openConnection();
|
||||
co.setConnectTimeout(timeOutMillSeconds);
|
||||
co.connect();
|
||||
return true;
|
||||
URL urlObj = new URL(address);
|
||||
HttpURLConnection oc = (HttpURLConnection) urlObj.openConnection();
|
||||
oc.setUseCaches(false);
|
||||
oc.setConnectTimeout(timeOutMillSeconds); // 设置超时时间
|
||||
int status = oc.getResponseCode();// 请求状态
|
||||
if (200 == status) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.dto;
|
||||
|
||||
import io.metersphere.base.domain.FileMetadata;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class LoadTestFileDTO extends FileMetadata {
|
||||
private String testId;
|
||||
private String testName;
|
||||
}
|
|
@ -12,6 +12,7 @@ import io.metersphere.commons.utils.SessionUtils;
|
|||
import io.metersphere.controller.request.QueryScheduleRequest;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import io.metersphere.dto.LoadTestDTO;
|
||||
import io.metersphere.dto.LoadTestFileDTO;
|
||||
import io.metersphere.dto.ScheduleDao;
|
||||
import io.metersphere.performance.service.PerformanceTestService;
|
||||
import io.metersphere.service.CheckPermissionService;
|
||||
|
@ -112,6 +113,14 @@ public class PerformanceTestController {
|
|||
return performanceTestService.getJmxContent(testId);
|
||||
}
|
||||
|
||||
@GetMapping("/project/{loadType}/{projectId}/{goPage}/{pageSize}")
|
||||
public Pager<List<LoadTestFileDTO>> getProjectFiles(@PathVariable String projectId, @PathVariable String loadType,
|
||||
@PathVariable int goPage, @PathVariable int pageSize) {
|
||||
checkPermissionService.checkProjectOwner(projectId);
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
return PageUtils.setPageInfo(page, performanceTestService.getProjectFiles(projectId, loadType));
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
public void delete(@RequestBody DeleteTestPlanRequest request) {
|
||||
checkPermissionService.checkPerformanceTestOwner(request.getId());
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.metersphere.config.KafkaProperties;
|
|||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.performance.engine.EngineContext;
|
||||
import io.metersphere.performance.parse.xml.reader.DocumentParser;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
@ -639,6 +640,25 @@ public class JmeterDocumentParser implements DocumentParser {
|
|||
((List<?>) holds).remove(0);
|
||||
hold = o.toString();
|
||||
}
|
||||
Object deleteds = context.getProperty("deleted");
|
||||
String deleted = "false";
|
||||
if (deleteds instanceof List) {
|
||||
Object o = ((List<?>) deleteds).get(0);
|
||||
((List<?>) deleteds).remove(0);
|
||||
deleted = o.toString();
|
||||
}
|
||||
Object enableds = context.getProperty("enabled");
|
||||
String enabled = "true";
|
||||
if (enableds instanceof List) {
|
||||
Object o = ((List<?>) enableds).get(0);
|
||||
((List<?>) enableds).remove(0);
|
||||
enabled = o.toString();
|
||||
}
|
||||
|
||||
threadGroup.setAttribute("enabled", enabled);
|
||||
if (BooleanUtils.toBoolean(deleted)) {
|
||||
threadGroup.setAttribute("enabled", "false");
|
||||
}
|
||||
Element elementProp = document.createElement("elementProp");
|
||||
elementProp.setAttribute("name", "ThreadGroup.main_controller");
|
||||
elementProp.setAttribute("elementType", "com.blazemeter.jmeter.control.VirtualUserController");
|
||||
|
|
|
@ -16,6 +16,7 @@ import io.metersphere.controller.request.OrderRequest;
|
|||
import io.metersphere.controller.request.QueryScheduleRequest;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import io.metersphere.dto.LoadTestDTO;
|
||||
import io.metersphere.dto.LoadTestFileDTO;
|
||||
import io.metersphere.dto.ScheduleDao;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.job.sechedule.PerformanceTestJob;
|
||||
|
@ -38,6 +39,7 @@ import javax.annotation.Resource;
|
|||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.*;
|
||||
|
@ -126,21 +128,54 @@ public class PerformanceTestService {
|
|||
}
|
||||
|
||||
public String save(SaveTestPlanRequest request, List<MultipartFile> files) {
|
||||
if (files == null) {
|
||||
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
|
||||
}
|
||||
checkQuota(request, true);
|
||||
final LoadTestWithBLOBs loadTest = saveLoadTest(request);
|
||||
files.forEach(file -> {
|
||||
final FileMetadata fileMetadata = fileService.saveFile(file);
|
||||
LoadTestWithBLOBs loadTest = saveLoadTest(request);
|
||||
|
||||
// 新选择了一个文件,删除原来的文件
|
||||
List<FileMetadata> importFiles = request.getUpdatedFileList();
|
||||
List<String> importFileIds = importFiles.stream().map(FileMetadata::getId).collect(Collectors.toList());
|
||||
// 导入项目里其他的文件
|
||||
this.importFiles(importFileIds, loadTest.getId());
|
||||
// 保存上传的问题件
|
||||
this.saveUploadFiles(files, loadTest.getId());
|
||||
// 直接上传了jmx,用于API导入的场景
|
||||
String jmx = request.getJmx();
|
||||
if (StringUtils.isNotBlank(jmx)) {
|
||||
byte[] bytes = jmx.getBytes(StandardCharsets.UTF_8);
|
||||
FileMetadata fileMetadata = fileService.saveFile(bytes, request.getName() + ".jmx", (long) bytes.length);
|
||||
LoadTestFile loadTestFile = new LoadTestFile();
|
||||
loadTestFile.setTestId(loadTest.getId());
|
||||
loadTestFile.setFileId(fileMetadata.getId());
|
||||
loadTestFileMapper.insert(loadTestFile);
|
||||
});
|
||||
}
|
||||
return loadTest.getId();
|
||||
}
|
||||
|
||||
private void saveUploadFiles(List<MultipartFile> files, String testId) {
|
||||
if (files != null) {
|
||||
files.forEach(file -> {
|
||||
final FileMetadata fileMetadata = fileService.saveFile(file);
|
||||
LoadTestFile loadTestFile = new LoadTestFile();
|
||||
loadTestFile.setTestId(testId);
|
||||
loadTestFile.setFileId(fileMetadata.getId());
|
||||
loadTestFileMapper.insert(loadTestFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void importFiles(List<String> importFileIds, String testId) {
|
||||
importFileIds.forEach(fileId -> {
|
||||
if (StringUtils.isBlank(fileId)) {
|
||||
return;
|
||||
}
|
||||
FileMetadata fileMetadata = fileService.copyFile(fileId);
|
||||
LoadTestFile loadTestFile = new LoadTestFile();
|
||||
loadTestFile.setTestId(testId);
|
||||
loadTestFile.setFileId(fileMetadata.getId());
|
||||
loadTestFileMapper.insert(loadTestFile);
|
||||
});
|
||||
}
|
||||
|
||||
private LoadTestWithBLOBs saveLoadTest(SaveTestPlanRequest request) {
|
||||
|
||||
LoadTestExample example = new LoadTestExample();
|
||||
|
@ -183,15 +218,19 @@ public class PerformanceTestService {
|
|||
// 相减
|
||||
List<String> deleteFileIds = ListUtils.subtract(originFileIds, updatedFileIds);
|
||||
fileService.deleteFileByIds(deleteFileIds);
|
||||
|
||||
if (files != null) {
|
||||
files.forEach(file -> {
|
||||
final FileMetadata fileMetadata = fileService.saveFile(file);
|
||||
LoadTestFile loadTestFile = new LoadTestFile();
|
||||
loadTestFile.setTestId(request.getId());
|
||||
loadTestFile.setFileId(fileMetadata.getId());
|
||||
loadTestFileMapper.insert(loadTestFile);
|
||||
});
|
||||
// 导入项目里其他的文件
|
||||
List<String> addFileIds = ListUtils.subtract(updatedFileIds, originFileIds);
|
||||
this.importFiles(addFileIds, request.getId());
|
||||
this.saveUploadFiles(files, request.getId());
|
||||
// 直接上传了jmx,用于API导入的场景
|
||||
String jmx = request.getJmx();
|
||||
if (StringUtils.isNotBlank(jmx)) {
|
||||
byte[] bytes = jmx.getBytes(StandardCharsets.UTF_8);
|
||||
FileMetadata fileMetadata = fileService.saveFile(bytes, request.getName() + ".jmx", (long) bytes.length);
|
||||
LoadTestFile loadTestFile = new LoadTestFile();
|
||||
loadTestFile.setTestId(request.getId());
|
||||
loadTestFile.setFileId(fileMetadata.getId());
|
||||
loadTestFileMapper.insert(loadTestFile);
|
||||
}
|
||||
|
||||
loadTest.setName(request.getName());
|
||||
|
@ -508,4 +547,14 @@ public class PerformanceTestService {
|
|||
return Optional.of(loadTest.getNum() + 1).orElse(100001);
|
||||
}
|
||||
}
|
||||
|
||||
public List<LoadTestFileDTO> getProjectFiles(String projectId, String loadType) {
|
||||
List<String> loadTypes = new ArrayList<>();
|
||||
loadTypes.add(StringUtils.upperCase(loadType));
|
||||
if (StringUtils.equalsIgnoreCase(loadType, "resource")) {
|
||||
loadTypes.add(FileType.CSV.name());
|
||||
loadTypes.add(FileType.JAR.name());
|
||||
}
|
||||
return extLoadTestMapper.getProjectFiles(projectId, loadTypes);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -169,4 +169,10 @@ public class FileService {
|
|||
public FileMetadata getFileMetadataById(String fileId) {
|
||||
return fileMetadataMapper.selectByPrimaryKey(fileId);
|
||||
}
|
||||
|
||||
public List<FileMetadata> getProjectJMXs(String projectId) {
|
||||
FileMetadataExample example = new FileMetadataExample();
|
||||
fileMetadataMapper.selectByExample(example);
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,14 @@
|
|||
package io.metersphere.track.request.testplan;
|
||||
|
||||
import io.metersphere.base.domain.FileMetadata;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public class SaveTestPlanRequest extends TestPlanRequest {
|
||||
private List<FileMetadata> updatedFileList;
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ public class TestPlanRequest {
|
|||
|
||||
private String description;
|
||||
|
||||
private String scenarioDefinition;
|
||||
private String jmx;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
|
|
|
@ -8,17 +8,17 @@
|
|||
<el-row>
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item :title="threadGroup.attributes.testname" :name="index"
|
||||
v-for="(threadGroup, index) in threadGroups"
|
||||
v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')"
|
||||
:key="index">
|
||||
<el-col :span="10">
|
||||
<el-form :inline="true">
|
||||
<el-form-item :label="$t('load_test.thread_num')">
|
||||
<el-input-number
|
||||
:disabled="true"
|
||||
:placeholder="$t('load_test.input_thread_num')"
|
||||
v-model="threadGroup.threadNumber"
|
||||
:min="1"
|
||||
size="mini"/>
|
||||
:disabled="true"
|
||||
:placeholder="$t('load_test.input_thread_num')"
|
||||
v-model="threadGroup.threadNumber"
|
||||
:min="1"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
<el-form-item>
|
||||
|
@ -31,72 +31,72 @@
|
|||
<div v-if="threadGroup.threadType === 'DURATION'">
|
||||
<el-form-item :label="$t('load_test.duration')">
|
||||
<el-input-number
|
||||
:disabled="true"
|
||||
v-model="threadGroup.duration"
|
||||
:min="1"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
:disabled="true"
|
||||
v-model="threadGroup.duration"
|
||||
:min="1"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
<el-form-item :label="$t('load_test.rps_limit')">
|
||||
<el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/>
|
||||
|
||||
<el-input-number
|
||||
:disabled="true "
|
||||
v-model="threadGroup.rpsLimit"
|
||||
@change="calculateChart(threadGroup)"
|
||||
:min="1"
|
||||
size="mini"/>
|
||||
:disabled="true "
|
||||
v-model="threadGroup.rpsLimit"
|
||||
@change="calculateChart(threadGroup)"
|
||||
:min="1"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
<el-form-item :label="$t('load_test.ramp_up_time_within')">
|
||||
<el-input-number
|
||||
:disabled="true"
|
||||
:min="1"
|
||||
:max="threadGroup.duration"
|
||||
v-model="threadGroup.rampUpTime"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
:disabled="true"
|
||||
:min="1"
|
||||
:max="threadGroup.duration"
|
||||
v-model="threadGroup.rampUpTime"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('load_test.ramp_up_time_minutes')">
|
||||
<el-input-number
|
||||
:disabled="true"
|
||||
:min="1"
|
||||
:max="Math.min(threadGroup.threadNumber, threadGroup.rampUpTime)"
|
||||
v-model="threadGroup.step"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
:disabled="true"
|
||||
:min="1"
|
||||
:max="Math.min(threadGroup.threadNumber, threadGroup.rampUpTime)"
|
||||
v-model="threadGroup.step"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('load_test.ramp_up_time_times')"/>
|
||||
</div>
|
||||
<div v-if="threadGroup.threadType === 'ITERATION'">
|
||||
<el-form-item :label="$t('load_test.iterate_num')">
|
||||
<el-input-number
|
||||
:disabled="true"
|
||||
v-model="threadGroup.iterateNum"
|
||||
:min="1"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
:disabled="true"
|
||||
v-model="threadGroup.iterateNum"
|
||||
:min="1"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
<el-form-item :label="$t('load_test.rps_limit')">
|
||||
<el-switch v-model="threadGroup.rpsLimitEnable" @change="calculateTotalChart()"/>
|
||||
|
||||
<el-input-number
|
||||
:disabled="true || !threadGroup.rpsLimitEnable"
|
||||
v-model="threadGroup.rpsLimit"
|
||||
@change="calculateChart(threadGroup)"
|
||||
:min="1"
|
||||
size="mini"/>
|
||||
:disabled="true || !threadGroup.rpsLimitEnable"
|
||||
v-model="threadGroup.rpsLimit"
|
||||
@change="calculateChart(threadGroup)"
|
||||
:min="1"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
<el-form-item :label="$t('load_test.ramp_up_time_within')">
|
||||
<el-input-number
|
||||
:disabled="true"
|
||||
:min="1"
|
||||
v-model="threadGroup.iterateRampUp"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
:disabled="true"
|
||||
:min="1"
|
||||
v-model="threadGroup.iterateRampUp"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('load_test.ramp_up_time_seconds')"/>
|
||||
</div>
|
||||
|
@ -126,14 +126,16 @@ const RPS_LIMIT_ENABLE = "rpsLimitEnable";
|
|||
const THREAD_TYPE = "threadType";
|
||||
const ITERATE_NUM = "iterateNum";
|
||||
const ITERATE_RAMP_UP = "iterateRampUpTime";
|
||||
const ENABLED = "enabled";
|
||||
const DELETED = "deleted";
|
||||
|
||||
const hexToRgba = function (hex, opacity) {
|
||||
return 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) + ','
|
||||
+ parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')';
|
||||
+ parseInt('0x' + hex.slice(5, 7)) + ',' + opacity + ')';
|
||||
}
|
||||
const hexToRgb = function (hex) {
|
||||
return 'rgb(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5))
|
||||
+ ',' + parseInt('0x' + hex.slice(5, 7)) + ')';
|
||||
+ ',' + parseInt('0x' + hex.slice(5, 7)) + ')';
|
||||
}
|
||||
|
||||
export default {
|
||||
|
@ -163,83 +165,51 @@ export default {
|
|||
calculateLoadConfiguration: function (data) {
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let d = data[i];
|
||||
if (d instanceof Array) {
|
||||
d.forEach(item => {
|
||||
switch (item.key) {
|
||||
case TARGET_LEVEL:
|
||||
this.threadGroups[i].threadNumber = item.value;
|
||||
break;
|
||||
case RAMP_UP:
|
||||
this.threadGroups[i].rampUpTime = item.value;
|
||||
break;
|
||||
case ITERATE_RAMP_UP:
|
||||
this.threadGroups[i].iterateRampUp = item.value;
|
||||
break;
|
||||
case DURATION:
|
||||
if (item.unit) {
|
||||
this.threadGroups[i].duration = item.value;
|
||||
} else {
|
||||
this.threadGroups[i].duration = item.value * 60;
|
||||
}
|
||||
break;
|
||||
case STEPS:
|
||||
this.threadGroups[i].step = item.value;
|
||||
break;
|
||||
case RPS_LIMIT:
|
||||
this.threadGroups[i].rpsLimit = item.value;
|
||||
break;
|
||||
case RPS_LIMIT_ENABLE:
|
||||
this.threadGroups[i].rpsLimitEnable = item.value;
|
||||
break;
|
||||
case THREAD_TYPE:
|
||||
this.threadGroups[i].threadType = item.value;
|
||||
break;
|
||||
case ITERATE_NUM:
|
||||
this.threadGroups[i].iterateNum = item.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
})
|
||||
this.calculateChart(this.threadGroups[i]);
|
||||
} else {
|
||||
switch (d.key) {
|
||||
d.forEach(item => {
|
||||
switch (item.key) {
|
||||
case TARGET_LEVEL:
|
||||
this.threadGroups[0].threadNumber = d.value;
|
||||
this.threadGroups[i].threadNumber = item.value;
|
||||
break;
|
||||
case RAMP_UP:
|
||||
this.threadGroups[0].rampUpTime = d.value;
|
||||
this.threadGroups[i].rampUpTime = item.value;
|
||||
break;
|
||||
case ITERATE_RAMP_UP:
|
||||
this.threadGroups[0].iterateRampUp = d.value;
|
||||
this.threadGroups[i].iterateRampUp = item.value;
|
||||
break;
|
||||
case DURATION:
|
||||
if (d.unit) {
|
||||
this.threadGroups[0].duration = d.value;
|
||||
if (item.unit) {
|
||||
this.threadGroups[i].duration = item.value;
|
||||
} else {
|
||||
this.threadGroups[0].duration = d.value * 60;
|
||||
this.threadGroups[i].duration = item.value * 60;
|
||||
}
|
||||
break;
|
||||
case STEPS:
|
||||
this.threadGroups[0].step = d.value;
|
||||
this.threadGroups[i].step = item.value;
|
||||
break;
|
||||
case RPS_LIMIT:
|
||||
this.threadGroups[0].rpsLimit = d.value;
|
||||
this.threadGroups[i].rpsLimit = item.value;
|
||||
break;
|
||||
case RPS_LIMIT_ENABLE:
|
||||
this.threadGroups[0].rpsLimitEnable = d.value;
|
||||
this.threadGroups[i].rpsLimitEnable = item.value;
|
||||
break;
|
||||
case THREAD_TYPE:
|
||||
this.threadGroups[0].threadType = d.value;
|
||||
this.threadGroups[i].threadType = item.value;
|
||||
break;
|
||||
case ITERATE_NUM:
|
||||
this.threadGroups[0].iterateNum = d.value;
|
||||
this.threadGroups[i].iterateNum = item.value;
|
||||
break;
|
||||
case ENABLED:
|
||||
this.threadGroups[i].enabled = item.value;
|
||||
break;
|
||||
case DELETED:
|
||||
this.threadGroups[i].deleted = item.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.calculateChart(this.threadGroups[0]);
|
||||
}
|
||||
})
|
||||
this.calculateChart(this.threadGroups[i]);
|
||||
|
||||
}
|
||||
},
|
||||
getLoadConfig() {
|
||||
|
@ -307,6 +277,9 @@ export default {
|
|||
|
||||
|
||||
for (let i = 0; i < handler.threadGroups.length; i++) {
|
||||
if (handler.threadGroups[i].enabled === 'false' || handler.threadGroups[i].deleted === 'true') {
|
||||
continue;
|
||||
}
|
||||
let seriesData = {
|
||||
name: handler.threadGroups[i].attributes.testname,
|
||||
data: [],
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<el-card shadow="always" class="ms-card-index-2">
|
||||
<span class="ms-card-data">
|
||||
<span class="ms-card-data-digital">{{ avgTransactions }}</span>
|
||||
<span class="ms-card-data-unit"> Transactions/s</span>
|
||||
<span class="ms-card-data-unit"> TPS</span>
|
||||
</span>
|
||||
<span class="ms-card-desc">Avg.Transactions</span>
|
||||
</el-card>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
class="input-with-select"
|
||||
maxlength="30" show-word-limit
|
||||
>
|
||||
<template slot="prepend">测试名称</template>
|
||||
<template slot="prepend">{{ $t('load_test.name') }}</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="2">
|
||||
|
@ -32,6 +32,7 @@
|
|||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.pressure_config')">
|
||||
<performance-pressure-config :is-read-only="isReadOnly" :test="test" :test-id="testId"
|
||||
@fileChange="fileChange"
|
||||
ref="pressureConfig" @changeActive="changeTabActive"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('load_test.advanced_config')" class="advanced-config">
|
||||
|
@ -305,6 +306,9 @@ export default {
|
|||
|
||||
this.$set(handler, "threadGroups", threadGroups);
|
||||
|
||||
this.$refs.basicConfig.threadGroups = threadGroups;
|
||||
this.$refs.pressureConfig.threadGroups = threadGroups;
|
||||
|
||||
threadGroups.forEach(tg => {
|
||||
handler.calculateChart(tg);
|
||||
})
|
||||
|
@ -317,7 +321,6 @@ export default {
|
|||
|
||||
.testplan-config {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-select {
|
||||
|
|
|
@ -1,25 +1,89 @@
|
|||
<template>
|
||||
<div v-loading="result.loading">
|
||||
<el-upload
|
||||
accept=".jmx,.csv,.jar"
|
||||
drag
|
||||
action=""
|
||||
:limit="fileNumLimit"
|
||||
multiple
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:disabled="isReadOnly"
|
||||
:file-list="fileList">
|
||||
<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>
|
||||
</template>
|
||||
</el-upload>
|
||||
<el-row type="flex" justify="space-between" align="middle">
|
||||
<h4>{{ $t('load_test.scenario_list') }}</h4>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="start" align="middle">
|
||||
<el-upload
|
||||
style="padding-right: 10px;"
|
||||
accept=".jmx"
|
||||
action=""
|
||||
:limit="fileNumLimit"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUploadJmx"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:disabled="isReadOnly"
|
||||
:file-list="fileList">
|
||||
<ms-table-button :is-tester-permission="true" icon="el-icon-upload2"
|
||||
:content="$t('load_test.upload_jmx')"/>
|
||||
</el-upload>
|
||||
<ms-table-button :is-tester-permission="true" icon="el-icon-circle-plus-outline"
|
||||
:content="$t('load_test.load_exist_jmx')" @click="loadJMX()"/>
|
||||
<ms-table-button :is-tester-permission="true" icon="el-icon-share"
|
||||
@click="loadApiAutomation()"
|
||||
:content="$t('load_test.load_api_automation_jmx')"/>
|
||||
</el-row>
|
||||
<el-table class="basic-config" :data="threadGroups.filter(tg=>tg.deleted=='false')">
|
||||
<el-table-column
|
||||
:label="$t('load_test.scenario_name')">
|
||||
<template v-slot:default="{row}">
|
||||
{{ row.attributes.testname }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="Enable/Disable">
|
||||
<template v-slot:default="{row}">
|
||||
<el-switch v-model="row.enabled"
|
||||
inactive-color="#DCDFE6"
|
||||
active-value="true"
|
||||
inactive-value="false"
|
||||
:disabled="threadGroupDisable(row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="ThreadGroup">
|
||||
<template v-slot:default="{row}">
|
||||
{{ row.name.substring(row.name.lastIndexOf(".") + 1) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-button :disabled="isReadOnly || threadGroupDisable(row)"
|
||||
@click="handleDeleteThreadGroup(row)"
|
||||
type="danger"
|
||||
icon="el-icon-delete" size="mini"
|
||||
circle/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-table class="basic-config" :data="tableData">
|
||||
<el-row type="flex" justify="space-between" align="middle">
|
||||
<h4>{{ $t('load_test.other_resource') }}</h4>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="start" align="middle">
|
||||
<el-upload
|
||||
style="padding-right: 10px;"
|
||||
accept=".jar,.csv"
|
||||
action=""
|
||||
:limit="fileNumLimit"
|
||||
multiple
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUploadFile"
|
||||
:http-request="handleUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:disabled="isReadOnly"
|
||||
:file-list="fileList">
|
||||
<ms-table-button :is-tester-permission="true" icon="el-icon-upload2"
|
||||
:content="$t('load_test.upload_file')"/>
|
||||
</el-upload>
|
||||
|
||||
<ms-table-button :is-tester-permission="true" icon="el-icon-circle-plus-outline"
|
||||
:content="$t('load_test.load_exist_file')" @click="loadFile()"/>
|
||||
</el-row>
|
||||
<el-table class="basic-config" :data="tableData.filter(f => !f.name.toUpperCase().endsWith('.JMX'))">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('load_test.file_name')">
|
||||
|
@ -51,15 +115,80 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog :title="$t('load_test.exist_jmx')" width="70%" :visible.sync="loadFileVisible">
|
||||
|
||||
<el-table class="basic-config" :data="existFiles" v-loading="projectLoadingResult.loading">
|
||||
<el-table-column
|
||||
prop="testName"
|
||||
:label="$t('load_test.test')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('load_test.file_name')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="type"
|
||||
:label="$t('load_test.file_type')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('load_test.last_modify_time')">
|
||||
<template v-slot:default="scope">
|
||||
<i class="el-icon-time"/>
|
||||
<span class="last-modified">{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<ms-table-operator-button :is-tester-permission="true"
|
||||
:tip="$t('api_test.api_import.label')"
|
||||
icon="el-icon-upload"
|
||||
@exec="handleImport(scope.row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<ms-table-pagination :change="getProjectFiles" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-dialog>
|
||||
<el-dialog :title="$t('load_test.scenario_list')" width="60%" :visible.sync="loadApiAutomationVisible">
|
||||
|
||||
<el-table class="basic-config" :data="apiScenarios" v-loading="projectLoadingResult.loading">
|
||||
<el-table-column
|
||||
prop="num"
|
||||
label="ID">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('load_test.scenario_name')">
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<ms-table-operator-button :is-tester-permission="true"
|
||||
:tip="$t('api_test.api_import.label')"
|
||||
icon="el-icon-upload"
|
||||
@exec="handleImportApi(scope.row)"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<ms-table-pagination :change="getProjectFiles" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Message} from "element-ui";
|
||||
import {findThreadGroup} from "@/business/components/performance/test/model/ThreadGroup";
|
||||
import {findTestPlan, findThreadGroup} from "@/business/components/performance/test/model/ThreadGroup";
|
||||
import MsTableButton from "@/business/components/common/components/MsTableButton";
|
||||
import {getCurrentProjectID} from "@/common/js/utils";
|
||||
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
|
||||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||
|
||||
export default {
|
||||
name: "PerformanceBasicConfig",
|
||||
components: {MsTableOperatorButton, MsTablePagination, MsTableButton},
|
||||
props: {
|
||||
test: {
|
||||
type: Object
|
||||
|
@ -72,6 +201,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
result: {},
|
||||
projectLoadingResult: {},
|
||||
getFileMetadataPath: "/performance/file/metadata",
|
||||
jmxDownloadPath: '/performance/file/download',
|
||||
jmxDeletePath: '/performance/file/delete',
|
||||
|
@ -79,6 +209,15 @@ export default {
|
|||
tableData: [],
|
||||
uploadList: [],
|
||||
fileNumLimit: 10,
|
||||
threadGroups: [],
|
||||
loadFileVisible: false,
|
||||
currentPage: 1,
|
||||
pageSize: 5,
|
||||
total: 0,
|
||||
existFiles: [],
|
||||
loadType: 'jmx',
|
||||
apiScenarios: [],
|
||||
loadApiAutomationVisible: false,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
|
@ -98,9 +237,9 @@ export default {
|
|||
if (fileList.length > 0) {
|
||||
let file = fileList[0];
|
||||
let jmxReader = new FileReader();
|
||||
jmxReader.onload = function (event) {
|
||||
let threadGroups = findThreadGroup(event.target.result);
|
||||
self.$emit('fileChange', threadGroups);
|
||||
jmxReader.onload = (event) => {
|
||||
self.threadGroups = findThreadGroup(event.target.result);
|
||||
self.$emit('fileChange', self.threadGroups);
|
||||
};
|
||||
jmxReader.readAsText(file);
|
||||
}
|
||||
|
@ -113,11 +252,11 @@ export default {
|
|||
this.uploadList = [];
|
||||
this.result = this.$get(this.getFileMetadataPath + "/" + test.id, response => {
|
||||
let files = response.data;
|
||||
|
||||
if (!files) {
|
||||
Message.error({message: this.$t('load_test.related_file_not_found'), showClose: true});
|
||||
return;
|
||||
}
|
||||
console.log(files);
|
||||
// deep copy
|
||||
this.fileList = JSON.parse(JSON.stringify(files));
|
||||
this.tableData = JSON.parse(JSON.stringify(files));
|
||||
|
@ -126,12 +265,40 @@ export default {
|
|||
});
|
||||
})
|
||||
},
|
||||
beforeUpload(file) {
|
||||
deleteExistJmx: function () {
|
||||
// 只能上传一个jmx
|
||||
let jmxs = this.tableData.filter(f => {
|
||||
let type = f.name.substring(f.name.lastIndexOf(".") + 1);
|
||||
return type.toUpperCase() === 'JMX';
|
||||
});
|
||||
for (let i = 0; i < jmxs.length; i++) {
|
||||
let index = this.tableData.indexOf(jmxs[i]);
|
||||
if (index > -1) {
|
||||
this.tableData.splice(index, 1);
|
||||
}
|
||||
let index2 = this.uploadList.indexOf(jmxs[i]);
|
||||
if (index2 > -1) {
|
||||
this.uploadList.splice(index2, 1);
|
||||
}
|
||||
}
|
||||
|
||||
jmxs = this.fileList.filter(f => {
|
||||
let type = f.name.substring(f.name.lastIndexOf(".") + 1);
|
||||
return type.toUpperCase() === 'JMX';
|
||||
});
|
||||
for (let i = 0; i < jmxs.length; i++) {
|
||||
let index3 = this.fileList.indexOf(jmxs[i]);
|
||||
if (index3 > -1) {
|
||||
this.fileList.splice(index3, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeUploadJmx(file) {
|
||||
if (!this.fileValidator(file)) {
|
||||
/// todo: 显示错误信息
|
||||
return false;
|
||||
}
|
||||
|
||||
this.deleteExistJmx();
|
||||
if (this.tableData.filter(f => f.name === file.name).length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
return false;
|
||||
|
@ -141,7 +308,28 @@ export default {
|
|||
|
||||
this.tableData.push({
|
||||
name: file.name,
|
||||
size: file.size + ' Bytes', /// todo: 按照大小显示Byte、KB、MB等
|
||||
size: (file.size / 1024).toFixed(2) + ' KB',
|
||||
type: type.toUpperCase(),
|
||||
updateTime: file.lastModified,
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
beforeUploadFile(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 / 1024).toFixed(2) + ' KB',
|
||||
type: type.toUpperCase(),
|
||||
updateTime: file.lastModified,
|
||||
});
|
||||
|
@ -181,25 +369,116 @@ export default {
|
|||
Message.error({message: e.message, showClose: true});
|
||||
});
|
||||
},
|
||||
handleDelete(file, index) {
|
||||
handleImport(row) {
|
||||
if (this.tableData.filter(f => f.name === row.name).length > 0) {
|
||||
this.$error(this.$t('load_test.delete_file'));
|
||||
return;
|
||||
}
|
||||
if (this.loadType === 'resource') {
|
||||
this.fileList.push(row);
|
||||
this.tableData.push(row);
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.loadFileVisible = false;
|
||||
return;
|
||||
}
|
||||
this.result = this.$get('/performance/get-jmx-content/' + row.testId, (response) => {
|
||||
if (response.data) {
|
||||
let testPlan = findTestPlan(response.data);
|
||||
testPlan.elements.forEach(e => {
|
||||
if (e.attributes.name === 'TestPlan.serialize_threadgroups') {
|
||||
this.serializeThreadgroups = Boolean(e.elements[0].text);
|
||||
}
|
||||
});
|
||||
this.threadGroups = findThreadGroup(response.data);
|
||||
this.threadGroups.forEach(tg => {
|
||||
tg.options = {};
|
||||
});
|
||||
this.$emit('fileChange', this.threadGroups);
|
||||
}
|
||||
this.deleteExistJmx();
|
||||
this.fileList.push(row);
|
||||
this.tableData.push(row);
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.loadFileVisible = false;
|
||||
});
|
||||
},
|
||||
countStrToBit(str) {
|
||||
let count = 0
|
||||
const arr = str.split('')
|
||||
arr.forEach(item => {
|
||||
count += Math.ceil(item.charCodeAt().toString(2).length / 8)
|
||||
})
|
||||
return count
|
||||
},
|
||||
handleImportApi(row) {
|
||||
let condition = {
|
||||
projectId: getCurrentProjectID(),
|
||||
ids: [row.id]
|
||||
};
|
||||
this.projectLoadingResult = this.$post('api/automation/export/jmx', condition, response => {
|
||||
let data = response.data[0];
|
||||
this.threadGroups = findThreadGroup(data.jmx);
|
||||
this.threadGroups.forEach(tg => {
|
||||
tg.options = {};
|
||||
});
|
||||
this.$emit('fileChange', this.threadGroups);
|
||||
this.deleteExistJmx();
|
||||
let bytes = this.countStrToBit(data.jmx);
|
||||
this.fileList.push({
|
||||
name: this.test.name + ".jmx",
|
||||
size: bytes,
|
||||
type: "JMX",
|
||||
updateTime: new Date().getTime(),
|
||||
});
|
||||
this.tableData.push({
|
||||
name: this.test.name + ".jmx",
|
||||
size: (bytes / 1024).toFixed(2) + ' KB',
|
||||
type: "JMX",
|
||||
updateTime: new Date().getTime(),
|
||||
});
|
||||
this.test.jmx = data.jmx;
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.loadApiAutomationVisible = false;
|
||||
})
|
||||
},
|
||||
handleDelete(file) {
|
||||
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);
|
||||
this._handleDelete(file);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
_handleDelete(file, index) {
|
||||
this.fileList.splice(index, 1);
|
||||
this.tableData.splice(index, 1);
|
||||
_handleDelete(file) {
|
||||
let index = this.fileList.findIndex(f => f.name === file.name);
|
||||
if (index > -1) {
|
||||
this.fileList.splice(index, 1);
|
||||
}
|
||||
index = this.tableData.findIndex(f => f.name === file.name);
|
||||
if (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);
|
||||
}
|
||||
},
|
||||
handleDeleteThreadGroup(tg) {
|
||||
this.$alert(this.$t('load_test.delete_threadgroup_confirm') + tg.attributes.testname + "?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
if (action === 'confirm') {
|
||||
tg.deleted = 'true';
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
threadGroupDisable(row) {
|
||||
return this.threadGroups.filter(tg => tg.enabled == 'true').length === 1 && row.enabled == 'true';
|
||||
},
|
||||
handleExceed() {
|
||||
this.$error(this.$t('load_test.file_size_limit'));
|
||||
},
|
||||
|
@ -210,6 +489,39 @@ export default {
|
|||
updatedFileList() {
|
||||
return this.fileList;// 表示修改了已经上传的文件列表
|
||||
},
|
||||
loadJMX() {
|
||||
this.loadFileVisible = true;
|
||||
this.loadType = "jmx";
|
||||
this.getProjectFiles();
|
||||
},
|
||||
loadFile() {
|
||||
this.loadFileVisible = true;
|
||||
this.loadType = "resource";
|
||||
this.getProjectFiles();
|
||||
},
|
||||
loadApiAutomation() {
|
||||
this.loadApiAutomationVisible = true;
|
||||
this.getProjectScenarios();
|
||||
},
|
||||
getProjectFiles() {
|
||||
this.projectLoadingResult = this.$get('/performance/project/' + this.loadType + '/' + getCurrentProjectID() + "/" + this.currentPage + "/" + this.pageSize, res => {
|
||||
let data = res.data;
|
||||
this.total = data.itemCount;
|
||||
this.existFiles = data.listObject;
|
||||
})
|
||||
},
|
||||
getProjectScenarios() {
|
||||
let condition = {
|
||||
projectId: getCurrentProjectID(),
|
||||
filters: {status: ["Prepare", "Underway", "Completed"]}
|
||||
}
|
||||
this.projectLoadingResult = this.$post('/api/automation/list/' + this.currentPage + "/" + this.pageSize, condition, res => {
|
||||
let data = res.data;
|
||||
this.total = data.itemCount;
|
||||
this.apiScenarios = data.listObject;
|
||||
})
|
||||
},
|
||||
|
||||
validConfig() {
|
||||
let newJmxNum = 0, oldJmxNum = 0, newCsvNum = 0, oldCsvNum = 0, newJarNum = 0, oldJarNum = 0;
|
||||
if (this.uploadList.length > 0) {
|
||||
|
@ -246,6 +558,10 @@ export default {
|
|||
this.$error(this.$t('load_test.jmx_is_null'));
|
||||
return false;
|
||||
}
|
||||
if (this.threadGroups.filter(tg => tg.attributes.enabled == 'true').length === 0) {
|
||||
this.$error(this.$t('load_test.threadgroup_at_least_one'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
@ -260,4 +576,8 @@ export default {
|
|||
.last-modified {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.el-dialog >>> .el-dialog__body {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<el-row>
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item :title="threadGroup.attributes.testname" :name="index"
|
||||
v-for="(threadGroup, index) in threadGroups"
|
||||
v-for="(threadGroup, index) in threadGroups.filter(th=>th.enabled === 'true' && th.deleted=='false')"
|
||||
:key="index">
|
||||
<el-col :span="10">
|
||||
<el-form :inline="true">
|
||||
|
@ -140,6 +140,8 @@ const RPS_LIMIT_ENABLE = "rpsLimitEnable";
|
|||
const HOLD = "Hold";
|
||||
const THREAD_TYPE = "threadType";
|
||||
const ITERATE_NUM = "iterateNum";
|
||||
const ENABLED = "enabled";
|
||||
const DELETED = "deleted";
|
||||
|
||||
const hexToRgba = function (hex, opacity) {
|
||||
return 'rgba(' + parseInt('0x' + hex.slice(1, 3)) + ',' + parseInt('0x' + hex.slice(3, 5)) + ','
|
||||
|
@ -221,90 +223,57 @@ export default {
|
|||
let data = JSON.parse(response.data);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
let d = data[i];
|
||||
if (d instanceof Array) {
|
||||
d.forEach(item => {
|
||||
switch (item.key) {
|
||||
case TARGET_LEVEL:
|
||||
this.threadGroups[i].threadNumber = item.value;
|
||||
break;
|
||||
case RAMP_UP:
|
||||
this.threadGroups[i].rampUpTime = item.value;
|
||||
break;
|
||||
case ITERATE_RAMP_UP:
|
||||
this.threadGroups[i].iterateRampUp = item.value;
|
||||
break;
|
||||
case DURATION:
|
||||
if (item.unit) {
|
||||
this.threadGroups[i].duration = item.value;
|
||||
} else {
|
||||
this.threadGroups[i].duration = item.value * 60;
|
||||
}
|
||||
break;
|
||||
case STEPS:
|
||||
this.threadGroups[i].step = item.value;
|
||||
break;
|
||||
case RPS_LIMIT:
|
||||
this.threadGroups[i].rpsLimit = item.value;
|
||||
break;
|
||||
case RPS_LIMIT_ENABLE:
|
||||
this.threadGroups[i].rpsLimitEnable = item.value;
|
||||
break;
|
||||
case THREAD_TYPE:
|
||||
this.threadGroups[i].threadType = item.value;
|
||||
break;
|
||||
case ITERATE_NUM:
|
||||
this.threadGroups[i].iterateNum = item.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//
|
||||
this.$set(this.threadGroups[i], "threadType", this.threadGroups[i].threadType || 'DURATION');
|
||||
this.$set(this.threadGroups[i], "iterateNum", this.threadGroups[i].iterateNum || 1);
|
||||
this.$set(this.threadGroups[i], "iterateRampUp", this.threadGroups[i].iterateRampUp || 10);
|
||||
})
|
||||
this.calculateChart(this.threadGroups[i]);
|
||||
} else {
|
||||
switch (d.key) {
|
||||
d.forEach(item => {
|
||||
switch (item.key) {
|
||||
case TARGET_LEVEL:
|
||||
this.threadGroups[0].threadNumber = d.value;
|
||||
this.threadGroups[i].threadNumber = item.value;
|
||||
break;
|
||||
case RAMP_UP:
|
||||
this.threadGroups[0].rampUpTime = d.value;
|
||||
this.threadGroups[i].rampUpTime = item.value;
|
||||
break;
|
||||
case ITERATE_RAMP_UP:
|
||||
this.threadGroups[0].iterateRampUp = d.value;
|
||||
this.threadGroups[i].iterateRampUp = item.value;
|
||||
break;
|
||||
case DURATION:
|
||||
if (d.unit) {
|
||||
this.threadGroups[0].duration = d.value;
|
||||
if (item.unit) {
|
||||
this.threadGroups[i].duration = item.value;
|
||||
} else {
|
||||
this.threadGroups[0].duration = d.value * 60;
|
||||
this.threadGroups[i].duration = item.value * 60;
|
||||
}
|
||||
break;
|
||||
case STEPS:
|
||||
this.threadGroups[0].step = d.value;
|
||||
this.threadGroups[i].step = item.value;
|
||||
break;
|
||||
case RPS_LIMIT:
|
||||
this.threadGroups[0].rpsLimit = d.value;
|
||||
this.threadGroups[i].rpsLimit = item.value;
|
||||
break;
|
||||
case RPS_LIMIT_ENABLE:
|
||||
this.threadGroups[0].rpsLimitEnable = d.value;
|
||||
this.threadGroups[i].rpsLimitEnable = item.value;
|
||||
break;
|
||||
case THREAD_TYPE:
|
||||
this.threadGroups[0].threadType = d.value;
|
||||
this.threadGroups[i].threadType = item.value;
|
||||
break;
|
||||
case ITERATE_NUM:
|
||||
this.threadGroups[0].iterateNum = d.value;
|
||||
this.threadGroups[i].iterateNum = item.value;
|
||||
break;
|
||||
case ENABLED:
|
||||
this.threadGroups[i].enabled = item.value;
|
||||
break;
|
||||
case DELETED:
|
||||
this.threadGroups[i].deleted = item.value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this.$set(this.threadGroups[0], "threadType", this.threadGroups[0].threadType || 'DURATION');
|
||||
this.$set(this.threadGroups[0], "iterateNum", this.threadGroups[0].iterateNum || 1);
|
||||
this.$set(this.threadGroups[0], "iterateRampUp", this.threadGroups[0].iterateRampUp || 10);
|
||||
this.calculateChart(this.threadGroups[0]);
|
||||
}
|
||||
//
|
||||
this.$set(this.threadGroups[i], "threadType", this.threadGroups[i].threadType || 'DURATION');
|
||||
this.$set(this.threadGroups[i], "iterateNum", this.threadGroups[i].iterateNum || 1);
|
||||
this.$set(this.threadGroups[i], "iterateRampUp", this.threadGroups[i].iterateRampUp || 10);
|
||||
this.$set(this.threadGroups[i], "enabled", this.threadGroups[i].enabled || 'true');
|
||||
this.$set(this.threadGroups[i], "deleted", this.threadGroups[i].deleted || 'false');
|
||||
})
|
||||
this.calculateChart(this.threadGroups[i]);
|
||||
|
||||
}
|
||||
this.calculateTotalChart();
|
||||
}
|
||||
|
@ -324,6 +293,7 @@ export default {
|
|||
this.threadGroups.forEach(tg => {
|
||||
tg.options = {};
|
||||
});
|
||||
this.$emit('fileChange', this.threadGroups);
|
||||
this.getLoadConfig();
|
||||
}
|
||||
});
|
||||
|
@ -355,6 +325,9 @@ export default {
|
|||
};
|
||||
|
||||
for (let i = 0; i < handler.threadGroups.length; i++) {
|
||||
if (handler.threadGroups[i].enabled === 'false' || handler.threadGroups[i].deleted === 'true') {
|
||||
continue;
|
||||
}
|
||||
let seriesData = {
|
||||
name: handler.threadGroups[i].attributes.testname,
|
||||
data: [],
|
||||
|
@ -560,6 +533,8 @@ export default {
|
|||
{key: THREAD_TYPE, value: this.threadGroups[i].threadType},
|
||||
{key: ITERATE_NUM, value: this.threadGroups[i].iterateNum},
|
||||
{key: ITERATE_RAMP_UP, value: this.threadGroups[i].iterateRampUp},
|
||||
{key: ENABLED, value: this.threadGroups[i].enabled},
|
||||
{key: DELETED, value: this.threadGroups[i].deleted},
|
||||
]);
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -5,8 +5,17 @@ let travel = function (elements, threadGroups) {
|
|||
return;
|
||||
}
|
||||
for (let element of elements) {
|
||||
if (element.name === 'ThreadGroup') {
|
||||
threadGroups.push(element);
|
||||
switch (element.name) {
|
||||
case "ThreadGroup":
|
||||
case "kg.apc.jmeter.threads.UltimateThreadGroup":
|
||||
case "com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup":
|
||||
case "com.blazemeter.jmeter.threads.arrivals.FreeFormArrivalsThreadGroup":
|
||||
case "com.blazemeter.jmeter.threads.arrivals.ArrivalsThreadGroup":
|
||||
case "com.octoperf.jmeter.OctoPerfThreadGroup":
|
||||
threadGroups.push(element);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
travel(element.elements, threadGroups)
|
||||
}
|
||||
|
@ -16,6 +25,10 @@ export function findThreadGroup(jmxContent) {
|
|||
let jmxJson = JSON.parse(xml2json(jmxContent));
|
||||
let threadGroups = [];
|
||||
travel(jmxJson.elements, threadGroups);
|
||||
threadGroups.forEach(tg => {
|
||||
tg.deleted = 'false';
|
||||
tg.enabled = tg.attributes.enabled;
|
||||
})
|
||||
return threadGroups;
|
||||
}
|
||||
|
||||
|
|
|
@ -420,6 +420,8 @@ export default {
|
|||
delete_batch_confirm: 'Confirm batch delete report',
|
||||
},
|
||||
load_test: {
|
||||
test: 'Test',
|
||||
name: 'Test Name',
|
||||
same_project_test: 'Only tests within the same project can be run',
|
||||
run: 'One click operation',
|
||||
operating: 'Operating',
|
||||
|
@ -488,7 +490,18 @@ export default {
|
|||
user_name: 'Creator',
|
||||
special_characters_are_not_supported: 'Test name does not support special characters',
|
||||
pressure_config_params_is_empty: 'Pressure configuration parameters cannot be empty!',
|
||||
schedule_tip: 'The interval must not be less than the pressure measuring time'
|
||||
schedule_tip: 'The interval must not be less than the pressure measuring time',
|
||||
delete_threadgroup_confirm: 'Confirm delete scenario: ',
|
||||
scenario_list: 'Scenario List',
|
||||
scenario_name: 'Scenario Name',
|
||||
upload_jmx: 'Upload JMX',
|
||||
exist_jmx: 'Existed Files',
|
||||
other_resource: 'Other Files',
|
||||
upload_file: 'Upload Files',
|
||||
load_exist_file: 'Load Project Files',
|
||||
load_exist_jmx: 'Load Project JMX',
|
||||
threadgroup_at_least_one: 'At least one ThreadGroup is enabled',
|
||||
load_api_automation_jmx: 'Import API automation scenario',
|
||||
},
|
||||
api_test: {
|
||||
creator: "Creator",
|
||||
|
@ -605,7 +618,7 @@ export default {
|
|||
edit_time_Reverse_order: "From back to front by update time",
|
||||
request_method: "Request method",
|
||||
request_interface: "Request interface",
|
||||
search_by_api_name : "Search by api name",
|
||||
search_by_api_name: "Search by api name",
|
||||
request_info: "Request info",
|
||||
request_head: "Request head",
|
||||
request_param: "Param",
|
||||
|
@ -615,7 +628,7 @@ export default {
|
|||
response_head: "Response head",
|
||||
response_body: "Response body",
|
||||
response_code: "Response code",
|
||||
table_coloum:{
|
||||
table_coloum: {
|
||||
name: "name",
|
||||
value: "value",
|
||||
is_required: "Is it required",
|
||||
|
|
|
@ -418,6 +418,8 @@ export default {
|
|||
delete_batch_confirm: '确认批量删除报告',
|
||||
},
|
||||
load_test: {
|
||||
test: '测试',
|
||||
name: '测试名称',
|
||||
same_project_test: '只能运行同一项目内的测试',
|
||||
already_exists: '测试名称不能重复',
|
||||
operating: '操作',
|
||||
|
@ -487,7 +489,18 @@ export default {
|
|||
user_name: '创建人',
|
||||
special_characters_are_not_supported: '测试名称不支持特殊字符',
|
||||
pressure_config_params_is_empty: '压力配置参数不能为空!',
|
||||
schedule_tip: '间隔时间不能小于压测时长'
|
||||
schedule_tip: '间隔时间不能小于压测时长',
|
||||
delete_threadgroup_confirm: '确认删除场景',
|
||||
scenario_list: '场景列表',
|
||||
scenario_name: "场景名称",
|
||||
upload_jmx: '上传 JMX 文件',
|
||||
exist_jmx: '已存在的文件',
|
||||
other_resource: '其他资源',
|
||||
upload_file: '上传新文件',
|
||||
load_exist_file: '加载已有文件',
|
||||
load_exist_jmx: '加载已有 JMX 文件',
|
||||
threadgroup_at_least_one: '至少启用一个线程组',
|
||||
load_api_automation_jmx: '引用接口自动化场景',
|
||||
},
|
||||
api_test: {
|
||||
creator: "创建人",
|
||||
|
@ -606,7 +619,7 @@ export default {
|
|||
edit_time_Reverse_order: "按更新时间从后到前",
|
||||
request_method: "请求方式",
|
||||
request_interface: "请求接口",
|
||||
search_by_api_name : "名称搜索",
|
||||
search_by_api_name: "名称搜索",
|
||||
request_info: "请求信息",
|
||||
request_head: "请求头",
|
||||
request_param: "参数",
|
||||
|
@ -616,7 +629,7 @@ export default {
|
|||
response_head: "响应头",
|
||||
response_body: "响应体",
|
||||
response_code: "响应码",
|
||||
table_coloum:{
|
||||
table_coloum: {
|
||||
name: "名称",
|
||||
value: "值",
|
||||
is_required: "是否必填",
|
||||
|
|
|
@ -418,6 +418,8 @@ export default {
|
|||
delete_batch_confirm: '確認批量刪除報告',
|
||||
},
|
||||
load_test: {
|
||||
test: '測試',
|
||||
name: '測試名稱',
|
||||
same_project_test: '只能運行同壹項目內的測試',
|
||||
already_exists: '測試名稱不能重復',
|
||||
operating: '操作',
|
||||
|
@ -487,7 +489,18 @@ export default {
|
|||
user_name: '創建人',
|
||||
special_characters_are_not_supported: '測試名稱不支持特殊字符',
|
||||
pressure_config_params_is_empty: '壓力配置參數不能為空!',
|
||||
schedule_tip: '間隔時間不能小於壓測時長'
|
||||
schedule_tip: '間隔時間不能小於壓測時長',
|
||||
delete_threadgroup_confirm: '確認刪除場景: ',
|
||||
scenario_list: '場景列表',
|
||||
scenario_name: '場景名稱',
|
||||
upload_jmx: '上傳 JMX文件',
|
||||
exist_jmx: '已存在的文件',
|
||||
other_resource: '其他資源',
|
||||
upload_file: '上傳新文件',
|
||||
load_exist_file: '加載已有文件',
|
||||
load_exist_jmx: '加載已有 JMX 文件',
|
||||
threadgroup_at_least_one: '至少啟用一個線程組',
|
||||
load_api_automation_jmx: '引用接口自動化場景',
|
||||
},
|
||||
api_test: {
|
||||
creator: "創建人",
|
||||
|
@ -605,7 +618,7 @@ export default {
|
|||
edit_time_Reverse_order: "按更新時間從後到前",
|
||||
request_method: "請求方式",
|
||||
request_interface: "請求接口e",
|
||||
search_by_api_name : "API名稱搜索",
|
||||
search_by_api_name: "API名稱搜索",
|
||||
request_info: "請求信息",
|
||||
request_head: "請求頭",
|
||||
request_param: "參數",
|
||||
|
@ -615,7 +628,7 @@ export default {
|
|||
response_head: "響應頭",
|
||||
response_body: "響應體",
|
||||
response_code: "響應碼",
|
||||
table_coloum:{
|
||||
table_coloum: {
|
||||
name: "名稱",
|
||||
value: "值",
|
||||
is_required: "是否必填",
|
||||
|
|
Loading…
Reference in New Issue