Merge remote-tracking branch 'origin/v1.8' into v1.8

# Conflicts:
#	frontend/src/business/components/api/definition/components/list/ApiCaseSimpleList.vue
This commit is contained in:
song.tianyang 2021-03-26 18:02:08 +08:00
commit 64263c2cdb
18 changed files with 166 additions and 59 deletions

View File

@ -75,6 +75,8 @@ import org.apache.jorphan.collections.HashTree;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
@ -132,12 +134,66 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
return (HashTree) field.get(scriptWrapper);
}
public boolean isProtocolDefaultPort(HTTPSamplerProxy source) {
String portAsString = source.getPropertyAsString("HTTPSampler.port");
if (portAsString != null && !portAsString.isEmpty()) {
return false;
} else {
return true;
}
}
public String url(String protocol, String host, String port, String file) {
protocol = protocol.toLowerCase();
if (StringUtils.isNotEmpty(file) && !file.startsWith("/")) {
file += "/";
}
return protocol + "://" + host + ":" + port + file;
}
public String getUrl(HTTPSamplerProxy source) throws MalformedURLException {
String path = source.getPath();
if (!path.startsWith("http://") && !path.startsWith("https://")) {
String domain = source.getDomain();
String protocol = source.getProtocol();
String method = source.getMethod();
StringBuilder pathAndQuery = new StringBuilder(100);
if ("file".equalsIgnoreCase(protocol)) {
domain = null;
} else if (!path.startsWith("/")) {
pathAndQuery.append('/');
}
pathAndQuery.append(path);
if ("GET".equals(method) || "DELETE".equals(method) || "OPTIONS".equals(method)) {
String queryString = source.getQueryString(source.getContentEncoding());
if (queryString.length() > 0) {
if (path.contains("?")) {
pathAndQuery.append("&");
} else {
pathAndQuery.append("?");
}
pathAndQuery.append(queryString);
}
}
String portAsString = source.getPropertyAsString("HTTPSampler.port");
return this.isProtocolDefaultPort(source) ? new URL(protocol, domain, pathAndQuery.toString()).toExternalForm() : this.url(protocol, domain, portAsString, pathAndQuery.toString());
} else {
return new URL(path).toExternalForm();
}
}
private void convertHttpSampler(MsHTTPSamplerProxy samplerProxy, Object key) {
try {
HTTPSamplerProxy source = (HTTPSamplerProxy) key;
BeanUtils.copyBean(samplerProxy, source);
samplerProxy.setRest(new ArrayList<KeyValue>(){{this.add(new KeyValue());}});
samplerProxy.setArguments(new ArrayList<KeyValue>(){{this.add(new KeyValue());}});
samplerProxy.setRest(new ArrayList<KeyValue>() {{
this.add(new KeyValue());
}});
samplerProxy.setArguments(new ArrayList<KeyValue>() {{
this.add(new KeyValue());
}});
if (source != null && source.getHTTPFiles().length > 0) {
samplerProxy.getBody().initBinary();
samplerProxy.getBody().setType(Body.FORM_DATA);
@ -158,7 +214,8 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
samplerProxy.getBody().setKvs(keyValues);
}
samplerProxy.setProtocol(RequestType.HTTP);
samplerProxy.setPort(source.getPort() + "");
samplerProxy.setPort(source.getPropertyAsString("HTTPSampler.port"));
samplerProxy.setDomain(source.getDomain());
if (source.getArguments() != null) {
if (source.getPostBodyRaw()) {
samplerProxy.getBody().setType(Body.RAW);
@ -178,10 +235,10 @@ public class MsJmeterParser extends ApiImportAbstractParser<ScenarioImport> {
}
samplerProxy.getBody().initBinary();
}
samplerProxy.setPath("");
// samplerProxy.setPath(source.getPath());
samplerProxy.setMethod(source.getMethod());
if (source.getUrl() != null) {
samplerProxy.setUrl(source.getUrl().toString());
if (this.getUrl(source) != null) {
samplerProxy.setUrl(this.getUrl(source));
}
samplerProxy.setId(UUID.randomUUID().toString());
samplerProxy.setType("HTTPSamplerProxy");

View File

@ -213,7 +213,7 @@ public abstract class MsTestElement {
csvDataSet.setName(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName());
csvDataSet.setProperty("fileEncoding", StringUtils.isEmpty(item.getEncoding()) ? "UTF-8" : item.getEncoding());
if (CollectionUtils.isNotEmpty(item.getFiles())) {
if (!config.isOperating() && new File(BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()).exists()) {
if (!config.isOperating() && !new File(BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName()).exists()) {
MSException.throwException(StringUtils.isEmpty(item.getName()) ? "CSVDataSet" : item.getName() + "[ CSV文件不存在 ]");
}
csvDataSet.setProperty("filename", BODY_FILE_DIR + "/" + item.getFiles().get(0).getId() + "_" + item.getFiles().get(0).getName());

View File

@ -91,6 +91,9 @@ public class MsHTTPSamplerProxy extends MsTestElement {
@JSONField(ordinal = 36)
private MsAuthManager authManager;
@JSONField(ordinal = 37)
private boolean urlOrPath;
@Override
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
// 非导出操作且不是启用状态则跳过执行
@ -140,23 +143,26 @@ public class MsHTTPSamplerProxy extends MsTestElement {
url = this.getUrl();
isUrl = true;
}
URL urlObject = new URL(url);
if (isUrl) {
if (StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
url.replaceAll(this.getPort(), "10990");
}
URL urlObject = new URL(url);
sampler.setDomain(URLDecoder.decode(urlObject.getHost(), "UTF-8"));
if (urlObject.getPort() > 0) {
if (urlObject.getPort() > 0 && urlObject.getPort() != 10990 && StringUtils.isNotEmpty(this.getPort()) && this.getPort().startsWith("${")) {
sampler.setPort(urlObject.getPort());
} else {
sampler.setProperty("HTTPSampler.port", this.getPort());
}
sampler.setProtocol(urlObject.getProtocol());
sampler.setPath(urlObject.getPath());
} else {
sampler.setDomain(config.getConfig().get(this.getProjectId()).getHttpConfig().getDomain());
sampler.setPort(config.getConfig().get(this.getProjectId()).getHttpConfig().getPort());
sampler.setProtocol(config.getConfig().get(this.getProjectId()).getHttpConfig().getProtocol());
sampler.setPath(this.getPath());
}
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
if (StringUtils.isNotBlank(this.getPath()) && !isUrl) {
envPath += this.getPath();
sampler.setPath(envPath);
}
String envPath = sampler.getPath();
if (CollectionUtils.isNotEmpty(this.getRest()) && this.isRest()) {
envPath = getRestParameters(URLDecoder.decode(envPath, "UTF-8"));
sampler.setPath(envPath);
@ -327,10 +333,16 @@ public class MsHTTPSamplerProxy extends MsTestElement {
}
public boolean isURL(String str) {
//转换为小写
try {
new URL(str);
return true;
String regex = "^((https|http|ftp|rtsp|mms)?://)"
+ "?(([0-9a-z_!~*'().&=+$%-]+: )?[0-9a-z_!~*'().&=+$%-]+@)?"
+ "(([0-9]{1,3}\\.){3}[0-9]{1,3}" + "|" + "([0-9a-z_!~*'()-]+\\.)*"
+ "([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\\."
+ "[a-z]{2,6})"
+ "(:[0-9]{1,5})?"
+ "((/?)|"
+ "(/[0-9a-z_!~*'().;?:@&=+$,%#-]+)+/?)$";
return str.matches(regex) || (str.matches("^(http|https|ftp)://.*$") && str.matches(".*://\\$\\{.*$"));
} catch (Exception e) {
return false;
}
@ -339,5 +351,5 @@ public class MsHTTPSamplerProxy extends MsTestElement {
private boolean isRest() {
return this.getRest().stream().filter(KeyValue::isEnable).filter(KeyValue::isValid).toArray().length > 0;
}
}

View File

@ -274,7 +274,7 @@
#{value}
</foreach>
</when>
<when test="key=='status'">
<when test="key=='reviewStatus'">
and test_case.review_status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}

View File

@ -443,7 +443,7 @@
});
this.result.loading = false;
if (this.$refs.scenarioTable) {
setTimeout(this.$refs.scenarioTable.doLayout,500)
setTimeout(this.$refs.scenarioTable.doLayout, 200)
}
if(!this.condition.selectAll){
@ -505,6 +505,7 @@
moveSave(param) {
this.buildBatchParam(param);
param.apiScenarioModuleId = param.nodeId;
param.modulePath = param.nodePath;
this.$post('/api/automation/batch/edit', param, () => {
this.$success(this.$t('commons.save_success'));
this.$refs.testBatchMove.close();

View File

@ -577,7 +577,7 @@
recursiveSorting(arr, scenarioProjectId) {
for (let i in arr) {
arr[i].index = Number(i) + 1;
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) {
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].loopType === "LOOP_COUNT" && arr[i].hashTree && arr[i].hashTree.length > 1) {
arr[i].countController.proceed = true;
}
if (!arr[i].projectId) {
@ -926,7 +926,7 @@
return bodyUploadFiles;
},
editScenario() {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
document.getElementById("inputDelay").focus(); // input
this.$refs['currentScenario'].validate((valid) => {
if (valid) {

View File

@ -5,12 +5,12 @@
<el-option v-for="(environment, index) in pe.envs" :key="index"
:label="environment.name + (environment.config.httpConfig.socket ? (': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '')"
:value="environment.id"/>
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
{{ $t('api_test.environment.environment_config') }}
</el-button>
<template v-slot:empty>
<div class="empty-environment">
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id)">
<el-button class="ms-scenario-button" size="mini" type="primary" @click="openEnvironmentConfig(pe.id, pe['selectEnv'])">
{{ $t('api_test.environment.environment_config') }}
</el-button>
</div>
@ -78,12 +78,12 @@ export default {
const project = this.projectList.find(p => p.id === id);
return project ? project.name : "";
},
openEnvironmentConfig(projectId) {
openEnvironmentConfig(projectId, envId) {
if (!projectId) {
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(projectId);
this.$refs.environmentConfig.open(projectId, envId);
},
handleConfirm() {
let map = new Map();

View File

@ -45,7 +45,7 @@
this.$emit('remove');
break;
case "scenarioVar":
this.$refs.scenarioParameters.open(this.data.variables, this.data.headers, true);
this.$refs.scenarioParameters.open(this.data.variables, this.data.headers, this.data.referenced === 'REF');
break;
case "openScenario":
this.getScenario();

View File

@ -493,7 +493,7 @@
recursiveSorting(arr, scenarioProjectId) {
for (let i in arr) {
arr[i].index = Number(i) + 1;
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].hashTree && arr[i].hashTree.length > 1) {
if (arr[i].type === ELEMENT_TYPE.LoopController && arr[i].loopType === "LOOP_COUNT" && arr[i].hashTree && arr[i].hashTree.length > 1) {
arr[i].countController.proceed = true;
}
if (!arr[i].projectId) {

View File

@ -77,7 +77,7 @@
this.$error(this.$t('api_test.select_project'));
return;
}
this.$refs.environmentConfig.open(this.projectId);
this.$refs.environmentConfig.open(this.projectId, this.environmentId);
},
environmentChange(value) {
for (let i in this.environments) {

View File

@ -46,13 +46,15 @@
icon: 'el-icon-delete',
func: this.deleteEnvironment
}
]
],
selectEnvironmentId: ''
}
},
methods: {
open: function (projectId) {
open: function (projectId, envId) {
this.visible = true;
this.projectId = projectId;
this.selectEnvironmentId = envId;
this.getEnvironments();
listenGoBack(this.close);
},
@ -114,7 +116,16 @@
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
this.environments = response.data;
if (this.environments.length > 0) {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
if (this.selectEnvironmentId) {
const index = this.environments.findIndex(e => e.id === this.selectEnvironmentId);
if (index !== -1) {
this.$refs.environmentItems.itemSelected(index, this.environments[index]);
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else {
let item = new Environment({
projectId: this.projectId

View File

@ -357,7 +357,7 @@ export default {
}
})
if (this.$refs.caseTable) {
setTimeout(this.$refs.caseTable.doLayout, 500)
setTimeout(this.$refs.caseTable.doLayout, 200)
}
this.$nextTick(function(){
this.checkTableRowIsSelect();

View File

@ -467,7 +467,7 @@
}
})
if (this.$refs.apiDefinitionTable) {
setTimeout(this.$refs.apiDefinitionTable.doLayout, 500)
setTimeout(this.$refs.apiDefinitionTable.doLayout, 200)
}
// nexttick:
this.$nextTick(function(){

View File

@ -46,13 +46,15 @@
icon: 'el-icon-delete',
func: this.deleteEnvironment
}
]
],
selectEnvironmentId: ''
}
},
methods: {
open: function (projectId) {
open: function (projectId, envId) {
this.visible = true;
this.projectId = projectId;
this.selectEnvironmentId = envId;
this.getEnvironments();
listenGoBack(this.close);
},
@ -114,7 +116,16 @@
this.result = this.$get('/api/environment/list/' + this.projectId, response => {
this.environments = response.data;
if (this.environments.length > 0) {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
if (this.selectEnvironmentId) {
const index = this.environments.findIndex(e => e.id === this.selectEnvironmentId);
if (index !== -1) {
this.$refs.environmentItems.itemSelected(index, this.environments[index]);
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else {
this.$refs.environmentItems.itemSelected(0, this.environments[0]);
}
} else {
let item = new Environment({
projectId: this.projectId

View File

@ -4,7 +4,7 @@
<el-col>
<el-form :inline="true">
<el-form-item :label="$t('load_test.select_resource_pool')">
<el-select v-model="resourcePool" :disabled="isReadOnly" size="mini">
<el-select v-model="resourcePool" :disabled="isReadOnly" size="mini" @change="resourcePoolChange">
<el-option
v-for="item in resourcePools"
:key="item.id"
@ -32,7 +32,7 @@
</el-tag>
<el-tag type="primary" size="mini" v-if="threadGroup.threadType === 'ITERATION'">
{{ $t('load_test.thread_num') }} {{ threadGroup.threadNumber }},
{{$t('load_test.iterate_num')}} {{threadGroup.iterateNum}}
{{ $t('load_test.iterate_num') }} {{ threadGroup.iterateNum }}
</el-tag>
</template>
<el-form :inline="true">
@ -42,6 +42,7 @@
v-model="threadGroup.threadNumber"
@change="calculateTotalChart(threadGroup)"
:min="resourcePoolResourceLength"
:max="maxThreadNumbers"
size="mini"/>
</el-form-item>
<br>
@ -219,7 +220,8 @@ export default {
resourcePools: [],
activeNames: ["0"],
threadGroups: [],
resourcePoolResourceLength: 1
resourcePoolResourceLength: 1,
maxThreadNumbers: 5000,
}
},
mounted() {
@ -336,6 +338,22 @@ export default {
});
}
},
resourcePoolChange() {
let result = this.resourcePools.filter(p => p.id === this.resourcePool);
if (result.length === 1) {
let threadNumber = 0;
result[0].resources.forEach(resource => {
threadNumber += JSON.parse(resource.configuration).maxConcurrency;
})
this.maxThreadNumbers = threadNumber;
this.threadGroups.forEach(tg => {
if (tg.threadNumber > threadNumber) {
this.$set(tg, "threadNumber", threadNumber);
}
})
this.calculateTotalChart();
}
},
calculateTotalChart() {
let handler = this;
if (handler.duration < handler.rampUpTime) {
@ -344,6 +362,11 @@ export default {
if (handler.rampUpTime < handler.step) {
handler.step = handler.rampUpTime;
}
// 线
let resourcePool = this.resourcePools.filter(v => v.id === this.resourcePool)[0];
if (resourcePool) {
this.resourcePoolResourceLength = resourcePool.resources.length;
}
let color = ['#60acfc', '#32d3eb', '#5bc49f', '#feb64d', '#ff7c7c', '#9287e7', '#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'];
handler.options = {
color: color,
@ -649,7 +672,7 @@ export default {
border-bottom: 1px solid #DCDFE6;
}
/deep/ .el-collapse-item__content{
/deep/ .el-collapse-item__content {
padding-left: 10px;
padding-bottom: 5px;
border-left-width: 8px;

View File

@ -303,20 +303,12 @@ export default {
},
created: function () {
this.$emit('setCondition', this.condition);
if (this.trashEnable) {
this.condition.filters = {status: ["Trash"]};
} else {
this.condition.filters = {status: ["Prepare", "Pass", "UnPass"]};
}
this.condition.filters = {reviewStatus: ["Prepare", "Pass", "UnPass"]};
this.initTableData();
getSystemLabel(this, this.type)
},
activated() {
if (this.trashEnable) {
this.condition.filters = {status: ["Trash"]};
} else {
this.condition.filters = {status: ["Prepare", "Pass", "UnPass"]};
}
this.condition.filters = {reviewStatus: ["Prepare", "Pass", "UnPass"]};
this.initTableData();
},
watch: {
@ -374,15 +366,15 @@ export default {
case 'coverage':
this.condition.caseCoverage = 'coverage';
break;
/* case 'Prepare':
this.condition.filters.status = [this.selectDataRange];
case 'Prepare':
this.condition.filters.reviewStatus = [this.selectDataRange];
break;
case 'Pass':
this.condition.filters.status = [this.selectDataRange];
this.condition.filters.reviewStatus = [this.selectDataRange];
break;
case 'UnPass':
this.condition.filters.status = [this.selectDataRange];
break;*/
this.condition.filters.reviewStatus = [this.selectDataRange];
break;
}
if (this.projectId) {
this.condition.projectId = this.projectId;
@ -401,7 +393,7 @@ export default {
item.tags = JSON.parse(item.tags);
})
if (this.$refs.table) {
setTimeout(this.$refs.table.doLayout,500)
setTimeout(this.$refs.table.doLayout, 200)
}
this.$nextTick(function(){

View File

@ -477,7 +477,7 @@ export default {
}
this.selectRows.clear();
if (this.$refs.table) {
setTimeout(this.$refs.table.doLayout,500)
setTimeout(this.$refs.table.doLayout, 200)
}
});
}

@ -1 +1 @@
Subproject commit 07951ba17aef6f29e50cfd68e40de3266f9a60cd
Subproject commit 2115bd28a90854d2b6276a90878934715498c584