diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java index 7cb52c524c..852b3b9755 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/parse/MsJmeterParser.java @@ -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 { @@ -132,12 +134,66 @@ public class MsJmeterParser extends ApiImportAbstractParser { 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(){{this.add(new KeyValue());}}); - samplerProxy.setArguments(new ArrayList(){{this.add(new KeyValue());}}); + samplerProxy.setRest(new ArrayList() {{ + this.add(new KeyValue()); + }}); + samplerProxy.setArguments(new ArrayList() {{ + 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 { 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 { } 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"); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java index 5fa9333809..a7563bf58a 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/MsTestElement.java @@ -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()); diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java index 907e0170fb..ecf3e2b261 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/sampler/MsHTTPSamplerProxy.java @@ -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 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; } - } + diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml index b23321eba5..6930ce6025 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestCaseMapper.xml @@ -274,7 +274,7 @@ #{value} - + and test_case.review_status in #{value} diff --git a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue index a0cd7ae4c9..e749ffde14 100644 --- a/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue +++ b/frontend/src/business/components/api/automation/scenario/ApiScenarioList.vue @@ -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(); diff --git a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue index d5c143a998..4ebeeea4c4 100644 --- a/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue +++ b/frontend/src/business/components/api/automation/scenario/EditApiScenario.vue @@ -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) { diff --git a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue index c9eda69790..87c62495f0 100644 --- a/frontend/src/business/components/api/automation/scenario/EnvSelect.vue +++ b/frontend/src/business/components/api/automation/scenario/EnvSelect.vue @@ -5,12 +5,12 @@ - + {{ $t('api_test.environment.environment_config') }} @@ -42,6 +42,7 @@ v-model="threadGroup.threadNumber" @change="calculateTotalChart(threadGroup)" :min="resourcePoolResourceLength" + :max="maxThreadNumbers" size="mini"/>
@@ -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; diff --git a/frontend/src/business/components/track/case/components/TestCaseList.vue b/frontend/src/business/components/track/case/components/TestCaseList.vue index 24d745cc63..0609f36f90 100644 --- a/frontend/src/business/components/track/case/components/TestCaseList.vue +++ b/frontend/src/business/components/track/case/components/TestCaseList.vue @@ -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(){ diff --git a/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseList.vue b/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseList.vue index b3682c0801..5117b039af 100644 --- a/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseList.vue +++ b/frontend/src/business/components/track/plan/view/comonents/functional/FunctionalTestCaseList.vue @@ -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) } }); } diff --git a/frontend/src/business/components/xpack b/frontend/src/business/components/xpack index 07951ba17a..2115bd28a9 160000 --- a/frontend/src/business/components/xpack +++ b/frontend/src/business/components/xpack @@ -1 +1 @@ -Subproject commit 07951ba17aef6f29e50cfd68e40de3266f9a60cd +Subproject commit 2115bd28a90854d2b6276a90878934715498c584