From 06e8d48344e6ec97e782d94a5898fdd003f6ea6c Mon Sep 17 00:00:00 2001 From: fit2-zhao Date: Mon, 23 Nov 2020 11:36:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E5=AE=9A=E4=B9=89):=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/definition/request/MsTestElement.java | 4 +- .../assertions/MsAssertionDuration.java | 18 ++ .../request/assertions/MsAssertionJSR223.java | 25 ++ .../assertions/MsAssertionJsonPath.java | 21 ++ .../request/assertions/MsAssertionRegex.java | 22 ++ .../request/assertions/MsAssertionType.java | 15 + .../request/assertions/MsAssertionXPath2.java | 19 ++ .../request/assertions/MsAssertions.java | 31 +++ .../request/sampler/MsHTTPSamplerProxy.java | 5 +- .../assertion/ApiAssertionJsr223.vue | 261 ++++++++++++++++++ .../assertion/ApiAssertionXPath2.vue | 72 +++++ .../components/assertion/ApiAssertions.vue | 73 ++--- .../definition/components/body/ApiBody.vue | 6 +- .../components/sampler/http-sampler/index.js | 6 +- .../components/request/ApiHttpRequestForm.vue | 35 ++- .../components/response/ResponseText.vue | 7 +- .../api/definition/model/ApiTestModel.js | 69 ++++- 17 files changed, 613 insertions(+), 76 deletions(-) create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionDuration.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJSR223.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJsonPath.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionRegex.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionType.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionXPath2.java create mode 100644 backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java create mode 100644 frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsr223.vue create mode 100644 frontend/src/business/components/api/definition/components/assertion/ApiAssertionXPath2.vue 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 2eccd4bee9..0eb4815826 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 @@ -4,6 +4,7 @@ import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONType; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import io.metersphere.api.dto.definition.request.assertions.MsAssertions; import io.metersphere.api.dto.definition.request.auth.MsAuthManager; import io.metersphere.api.dto.definition.request.configurations.MsHeaderManager; import io.metersphere.api.dto.definition.request.processors.post.MsJSR223PostProcessor; @@ -29,9 +30,10 @@ import java.util.List; @JsonSubTypes.Type(value = MsTestPlan.class, name = "TestPlan"), @JsonSubTypes.Type(value = MsThreadGroup.class, name = "ThreadGroup"), @JsonSubTypes.Type(value = MsAuthManager.class, name = "AuthManager"), + @JsonSubTypes.Type(value = MsAssertions.class, name = "Assertions"), }) -@JSONType(seeAlso = {MsHTTPSamplerProxy.class, MsHeaderManager.class, MsJSR223PostProcessor.class, MsJSR223PreProcessor.class,MsTestPlan.class,MsThreadGroup.class, AuthManager.class}, typeKey = "type") +@JSONType(seeAlso = {MsHTTPSamplerProxy.class, MsHeaderManager.class, MsJSR223PostProcessor.class, MsJSR223PreProcessor.class, MsTestPlan.class, MsThreadGroup.class, AuthManager.class, MsAssertions.class}, typeKey = "type") @Data public abstract class MsTestElement { private String type; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionDuration.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionDuration.java new file mode 100644 index 0000000000..5a84a0cbea --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionDuration.java @@ -0,0 +1,18 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionDuration extends MsAssertionType { + private long value; + + public MsAssertionDuration() { + setType(MsAssertionType.DURATION); + } + + public boolean isValid() { + return value > 0; + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJSR223.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJSR223.java new file mode 100644 index 0000000000..110741291c --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJSR223.java @@ -0,0 +1,25 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionJSR223 extends MsAssertionType { + private String variable; + private String operator; + private String value; + private String desc; + private String name; + private String script; + private String language; + + public MsAssertionJSR223() { + setType(MsAssertionType.JSR223); + } + + public boolean isValid() { + return StringUtils.isNotBlank(script) && StringUtils.isNotBlank(language); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJsonPath.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJsonPath.java new file mode 100644 index 0000000000..a86686a369 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionJsonPath.java @@ -0,0 +1,21 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionJsonPath extends MsAssertionType { + private String expect; + private String expression; + private String description; + + public MsAssertionJsonPath() { + setType(MsAssertionType.JSON_PATH); + } + + public boolean isValid() { + return StringUtils.isNotBlank(expression); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionRegex.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionRegex.java new file mode 100644 index 0000000000..bbd1f465b3 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionRegex.java @@ -0,0 +1,22 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionRegex extends MsAssertionType { + private String subject; + private String expression; + private String description; + private boolean assumeSuccess; + + public MsAssertionRegex() { + setType(MsAssertionType.REGEX); + } + + public boolean isValid() { + return StringUtils.isNotBlank(subject) && StringUtils.isNotBlank(expression); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionType.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionType.java new file mode 100644 index 0000000000..b77df8f7e8 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionType.java @@ -0,0 +1,15 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; + +@Data +public class MsAssertionType { + public final static String REGEX = "Regex"; + public final static String DURATION = "Duration"; + public final static String JSON_PATH = "JSONPath"; + public final static String JSR223 = "JSR223"; + public final static String TEXT = "Text"; + public final static String XPATH2 = "XPath2"; + + private String type; +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionXPath2.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionXPath2.java new file mode 100644 index 0000000000..6ed27c3287 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertionXPath2.java @@ -0,0 +1,19 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +@EqualsAndHashCode(callSuper = true) +@Data +public class MsAssertionXPath2 extends MsAssertionType { + private String expression; + + public MsAssertionXPath2() { + setType(MsAssertionType.XPATH2); + } + + public boolean isValid() { + return StringUtils.isNotBlank(expression); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java new file mode 100644 index 0000000000..d9573adcb5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/assertions/MsAssertions.java @@ -0,0 +1,31 @@ +package io.metersphere.api.dto.definition.request.assertions; + +import com.alibaba.fastjson.annotation.JSONType; +import io.metersphere.api.dto.definition.request.MsTestElement; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.jorphan.collections.HashTree; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +@JSONType(typeName = "Assertions") +public class MsAssertions extends MsTestElement { + private List regex; + private List jsonPath; + private List jsr223; + private List xpath2; + private MsAssertionDuration duration; + private String type = "Assertions"; + + public void toHashTree(HashTree tree, List hashTree) { +// final HashTree testPlanTree = tree.add(getPlan()); +// if (CollectionUtils.isNotEmpty(hashTree)) { +// hashTree.forEach(el -> { +// el.toHashTree(testPlanTree, el.getHashTree()); +// }); +// } + } + +} 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 eeb071fc0e..7262c38393 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 @@ -8,7 +8,6 @@ import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.definition.request.dns.MsDNSCacheManager; import io.metersphere.api.dto.definition.request.prop.BoolProp; import io.metersphere.api.dto.definition.request.prop.StringProp; -import io.metersphere.api.dto.scenario.AuthConfig; import io.metersphere.api.dto.scenario.Body; import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.environment.EnvironmentConfig; @@ -72,7 +71,7 @@ public class MsHTTPSamplerProxy extends MsTestElement { private List rest; @JSONField(ordinal = 20) - private AuthConfig authConfig; + private String url; @JSONField(ordinal = 21) private BoolProp followRedirects; @@ -83,8 +82,6 @@ public class MsHTTPSamplerProxy extends MsTestElement { @JSONField(ordinal = 23) private String useEnvironment; - @JSONField(ordinal = 24) - private String url; public void toHashTree(HashTree tree, List hashTree) { HTTPSamplerProxy sampler = new HTTPSamplerProxy(); diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsr223.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsr223.vue new file mode 100644 index 0000000000..729a529474 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionJsr223.vue @@ -0,0 +1,261 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertionXPath2.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionXPath2.vue new file mode 100644 index 0000000000..ab2302436a --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertionXPath2.vue @@ -0,0 +1,72 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue b/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue index 2b4308ee91..2d1825f601 100644 --- a/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue @@ -1,47 +1,56 @@ @@ -49,23 +58,28 @@ import MsApiAssertionText from "./ApiAssertionText"; import MsApiAssertionRegex from "./ApiAssertionRegex"; import MsApiAssertionDuration from "./ApiAssertionDuration"; - import {ASSERTION_TYPE, Assertions, HttpRequest, JSONPath} from "../../model/ApiTestModel"; + import {ASSERTION_TYPE, Assertions, JSONPath, Scenario} from "../../model/ApiTestModel"; import MsApiAssertionsEdit from "./ApiAssertionsEdit"; import MsApiAssertionJsonPath from "./ApiAssertionJsonPath"; + import MsApiAssertionJsr223 from "./ApiAssertionJsr223"; import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList"; + import MsApiAssertionXPath2 from "./ApiAssertionXPath2"; export default { name: "MsApiAssertions", components: { + MsApiAssertionXPath2, + MsApiAssertionJsr223, MsApiJsonpathSuggestList, MsApiAssertionJsonPath, MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText }, props: { - assertions: {}, + assertions: Assertions, request: {}, + scenario: Scenario, isReadOnly: { type: Boolean, default: false @@ -115,7 +129,6 @@ .assertion-add { padding: 10px; - border: #DCDFE6 solid 1px; margin: 5px 0; border-radius: 5px; } @@ -143,10 +156,8 @@ } .json-path-suggest-button { - text-align: left; - margin-left: 20px; margin-top: 20px; - margin-bottom: 20px; + margin-left: 20px; } diff --git a/frontend/src/business/components/api/definition/components/body/ApiBody.vue b/frontend/src/business/components/api/definition/components/body/ApiBody.vue index 23d271cc1f..ce8d599ae8 100644 --- a/frontend/src/business/components/api/definition/components/body/ApiBody.vue +++ b/frontend/src/business/components/api/definition/components/body/ApiBody.vue @@ -25,10 +25,6 @@ {{ $t('api_test.definition.request.body_binary') }} - - import MsApiKeyValue from "../ApiKeyValue"; - import {Body, BODY_FORMAT, BODY_TYPE, KeyValue, Scenario} from "../../model/ApiTestModel"; + import {BODY_FORMAT, BODY_TYPE, KeyValue} from "../../model/ApiTestModel"; import MsCodeEdit from "../../../../common/components/MsCodeEdit"; import MsJsonCodeEdit from "../../../../common/components/MsJsonCodeEdit"; diff --git a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/http-sampler/index.js b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/http-sampler/index.js index 8be049ba7c..b317ab95fb 100644 --- a/frontend/src/business/components/api/definition/components/jmeter/components/sampler/http-sampler/index.js +++ b/frontend/src/business/components/api/definition/components/jmeter/components/sampler/http-sampler/index.js @@ -1,7 +1,6 @@ import {boolProp, elementProp, stringProp} from "../../../props"; import Sampler from "../sampler"; -import {BaseConfig, BODY_TYPE, KeyValue, Body} from "../../../../../model/ApiTestModel"; - +import {Body} from "../../../../../model/ApiTestModel"; const DEFAULT_OPTIONS = { options: { attributes: { @@ -32,8 +31,7 @@ export default class HTTPSamplerProxy extends Sampler { this.embeddedUrlRe = this.initStringProp('HTTPSampler.embedded_url_re'); this.connectTimeout = this.initStringProp('HTTPSampler.connect_timeout'); this.responseTimeout = this.initStringProp('HTTPSampler.response_timeout'); - // 初始化认证对象 和主体对象 - this.authConfig = {}; + // 初始化主体对象 this.body = new Body(); this.arguments = []; diff --git a/frontend/src/business/components/api/definition/components/request/ApiHttpRequestForm.vue b/frontend/src/business/components/api/definition/components/request/ApiHttpRequestForm.vue index ccf5b642c7..75ff78998b 100644 --- a/frontend/src/business/components/api/definition/components/request/ApiHttpRequestForm.vue +++ b/frontend/src/business/components/api/definition/components/request/ApiHttpRequestForm.vue @@ -3,7 +3,7 @@
- + @@ -46,6 +46,7 @@ + @@ -65,11 +66,13 @@ - - - - +
+ + +
@@ -77,9 +80,9 @@
+后置脚本
- - - + +断言规则 +
+ +提取参数
@@ -96,6 +99,7 @@ import {createComponent} from "../jmeter/components"; import MsApiAssertions from "../assertion/ApiAssertions"; import MsApiExtract from "../extract/ApiExtract"; + import {Assertions} from "../../model/ApiTestModel"; export default { name: "MsApiHttpRequestForm", @@ -139,9 +143,17 @@ }, headerSuggestions: REQUEST_HEADERS, isReloadData: false, + assertions: [], } }, - + created() { + this.request.hashTree.forEach(row => { + if (row.type === "Assertions") { + row = new Assertions({text: row.text, regex: row.regex, jsonPath: row.jsonPath, jsr223: row.jsr223, xpath2: row.xpath2, duration: row.duration}); + } + }) + console.log(this.assertions) + }, methods: { addPre() { let jsr223PreProcessor = createComponent("JSR223PreProcessor"); @@ -154,8 +166,9 @@ this.reload(); }, addAssertions() { - let jsonPathAssertion = createComponent("JSONPathAssertion"); - this.request.hashTree.push(jsonPathAssertion); + //let jsonPathAssertion = createComponent("JSONPathAssertion"); + let assertions = new Assertions(); + this.request.hashTree.push(assertions); this.reload(); }, addExtract() { diff --git a/frontend/src/business/components/api/definition/components/response/ResponseText.vue b/frontend/src/business/components/api/definition/components/response/ResponseText.vue index 0267b29d93..a0125cf9d6 100644 --- a/frontend/src/business/components/api/definition/components/response/ResponseText.vue +++ b/frontend/src/business/components/api/definition/components/response/ResponseText.vue @@ -5,13 +5,10 @@ - + - + diff --git a/frontend/src/business/components/api/definition/model/ApiTestModel.js b/frontend/src/business/components/api/definition/model/ApiTestModel.js index ae689a681f..1b4a9856d7 100644 --- a/frontend/src/business/components/api/definition/model/ApiTestModel.js +++ b/frontend/src/business/components/api/definition/model/ApiTestModel.js @@ -98,7 +98,9 @@ export const ASSERTION_TYPE = { TEXT: "Text", REGEX: "Regex", JSON_PATH: "JSON", - DURATION: "Duration" + DURATION: "Duration", + JSR223: "JSR223", + XPATH2: "XPath2", } export const ASSERTION_REGEX_SUBJECT = { @@ -434,7 +436,7 @@ export class HttpResponse extends Response { this.headers = []; this.body = new Body(options.body); this.statusCode = []; - this.sets({statusCode: KeyValue,headers: KeyValue}, options); + this.sets({statusCode: KeyValue, headers: KeyValue}, options); } } @@ -713,7 +715,7 @@ export class Body extends BaseConfig { this.xml = undefined; this.json = undefined; this.set(options); - this.sets({kvs: KeyValue},{fromUrlencoded: KeyValue},{binary: KeyValue}, options); + this.sets({kvs: KeyValue}, {fromUrlencoded: KeyValue}, {binary: KeyValue}, options); } isValid() { @@ -759,13 +761,16 @@ export class KeyValue extends BaseConfig { export class Assertions extends BaseConfig { constructor(options) { super(); + this.type = "Assertions"; this.text = []; this.regex = []; this.jsonPath = []; + this.jsr223 = []; + this.xpath2 = []; this.duration = undefined; this.set(options); - this.sets({text: Text, regex: Regex, jsonPath: JSONPath}, options); + this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xpath2: XPath2}, options); } initOptions(options) { @@ -782,6 +787,37 @@ export class AssertionType extends BaseConfig { } } +export class AssertionJSR223 extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.JSR223); + this.variable = undefined; + this.operator = undefined; + this.value = undefined; + this.desc = undefined; + + this.name = undefined; + this.script = undefined; + this.language = "beanshell"; + this.set(options); + } + + isValid() { + return !!this.script && !!this.language; + } +} + +export class Text extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.TEXT); + this.subject = undefined; + this.condition = undefined; + this.value = undefined; + + this.set(options); + } +} + + export class BeanShellProcessor extends BaseConfig { constructor(options) { super(); @@ -800,17 +836,6 @@ export class JSR223Processor extends BaseConfig { } } -export class Text extends AssertionType { - constructor(options) { - super(ASSERTION_TYPE.TEXT); - this.subject = undefined; - this.condition = undefined; - this.value = undefined; - - this.set(options); - } -} - export class Regex extends AssertionType { constructor(options) { super(ASSERTION_TYPE.REGEX); @@ -846,6 +871,20 @@ export class JSONPath extends AssertionType { } } +export class XPath2 extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.XPATH2); + this.expression = undefined; + this.description = undefined; + this.set(options); + } + + isValid() { + return !!this.expression; + } +} + + export class Duration extends AssertionType { constructor(options) { super(ASSERTION_TYPE.DURATION);