diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java new file mode 100644 index 0000000000..c17a893559 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionJsonPath.java @@ -0,0 +1,16 @@ +package io.metersphere.api.dto.scenario.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class AssertionJsonPath extends AssertionType { + private String expect; + private String expression; + private String description; + + public AssertionJsonPath() { + setType(AssertionType.JSON_PATH); + } +} diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionType.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionType.java index 5ae8414478..443ea969a9 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionType.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionType.java @@ -6,6 +6,7 @@ import lombok.Data; public class AssertionType { public final static String REGEX = "Regex"; public final static String DURATION = "Duration"; + public final static String JSON_PATH = "JSONPath"; public final static String TEXT = "Text"; private String type; diff --git a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/Assertions.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/Assertions.java index 678fcdc0ce..d910c0af13 100644 --- a/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/Assertions.java +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/Assertions.java @@ -7,5 +7,6 @@ import java.util.List; @Data public class Assertions { private List regex; + private List jsonPath; private AssertionDuration duration; } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java index 74f4e898d4..e0f731b47b 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/JMeterService.java @@ -1,6 +1,7 @@ package io.metersphere.api.jmeter; import io.metersphere.commons.exception.MSException; +import io.metersphere.commons.utils.LogUtil; import io.metersphere.config.JmeterProperties; import io.metersphere.i18n.Translator; import org.apache.jmeter.config.Arguments; @@ -33,6 +34,7 @@ public class JMeterService { LocalRunner runner = new LocalRunner(testPlan); runner.run(); } catch (Exception e) { + LogUtil.error(e.getMessage(), e); MSException.throwException(Translator.get("api_load_script_error")); } } diff --git a/frontend/src/business/components/api/test/components/assertion/ApiAssertionJsonPath.vue b/frontend/src/business/components/api/test/components/assertion/ApiAssertionJsonPath.vue new file mode 100644 index 0000000000..820710aede --- /dev/null +++ b/frontend/src/business/components/api/test/components/assertion/ApiAssertionJsonPath.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/frontend/src/business/components/api/test/components/assertion/ApiAssertions.vue b/frontend/src/business/components/api/test/components/assertion/ApiAssertions.vue index daa939dd36..de285e519c 100644 --- a/frontend/src/business/components/api/test/components/assertion/ApiAssertions.vue +++ b/frontend/src/business/components/api/test/components/assertion/ApiAssertions.vue @@ -7,12 +7,14 @@ size="small"> + + Add @@ -30,11 +32,14 @@ import MsApiAssertionDuration from "./ApiAssertionDuration"; import {ASSERTION_TYPE, Assertions} from "../../model/ScenarioModel"; import MsApiAssertionsEdit from "./ApiAssertionsEdit"; + import MsApiAssertionJsonPath from "./ApiAssertionJsonPath"; export default { name: "MsApiAssertions", - components: {MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText}, + components: { + MsApiAssertionJsonPath, + MsApiAssertionsEdit, MsApiAssertionDuration, MsApiAssertionRegex, MsApiAssertionText}, props: { assertions: Assertions, diff --git a/frontend/src/business/components/api/test/components/assertion/ApiAssertionsEdit.vue b/frontend/src/business/components/api/test/components/assertion/ApiAssertionsEdit.vue index 914c8555f2..722aae8eb4 100644 --- a/frontend/src/business/components/api/test/components/assertion/ApiAssertionsEdit.vue +++ b/frontend/src/business/components/api/test/components/assertion/ApiAssertionsEdit.vue @@ -9,6 +9,15 @@ +
+
+ {{'JSONPath'}} +
+
+ +
+
+
{{$t("api_test.request.assertions.response_time")}} @@ -23,11 +32,12 @@ import MsApiAssertionRegex from "./ApiAssertionRegex"; import MsApiAssertionDuration from "./ApiAssertionDuration"; import {Assertions} from "../../model/ScenarioModel"; + import MsApiAssertionJsonPath from "./ApiAssertionJsonPath"; export default { name: "MsApiAssertionsEdit", - components: {MsApiAssertionDuration, MsApiAssertionRegex}, + components: {MsApiAssertionJsonPath, MsApiAssertionDuration, MsApiAssertionRegex}, props: { assertions: Assertions, @@ -56,6 +66,10 @@ border-left: 2px solid #7B0274; } + .assertion-item-editing.json_path { + border-left: 2px solid #44B3D2; + } + .assertion-item-editing.response-time { border-left: 2px solid #DD0240; } diff --git a/frontend/src/business/components/api/test/model/JMX.js b/frontend/src/business/components/api/test/model/JMX.js index 155e054cad..7e0839883a 100644 --- a/frontend/src/business/components/api/test/model/JMX.js +++ b/frontend/src/business/components/api/test/model/JMX.js @@ -352,6 +352,20 @@ export class ResponseAssertion extends DefaultTestElement { } } +export class JSONPathAssertion extends DefaultTestElement { + constructor(testName, jsonPath) { + super('JSONPathAssertion', 'JSONPathAssertionGui', 'JSONPathAssertion', testName); + this.jsonPath = jsonPath || {}; + + this.stringProp('JSON_PATH', this.jsonPath.expression); + this.stringProp('EXPECTED_VALUE', this.jsonPath.expect); + this.boolProp('JSONVALIDATION', true); + this.boolProp('EXPECT_NULL', false); + this.boolProp('INVERT', false); + this.boolProp('ISREGEX', true); + } +} + export class ResponseCodeAssertion extends ResponseAssertion { constructor(testName, type, value, message) { let assertion = { diff --git a/frontend/src/business/components/api/test/model/ScenarioModel.js b/frontend/src/business/components/api/test/model/ScenarioModel.js index 5d18b5b69b..16d7088096 100644 --- a/frontend/src/business/components/api/test/model/ScenarioModel.js +++ b/frontend/src/business/components/api/test/model/ScenarioModel.js @@ -12,7 +12,7 @@ import { ResponseCodeAssertion, ResponseDataAssertion, ResponseHeadersAssertion, - RegexExtractor, JSONPostProcessor, XPath2Extractor, DubboSample, + RegexExtractor, JSONPostProcessor, XPath2Extractor, DubboSample, JSONPathAssertion, } from "./JMX"; export const uuid = function () { @@ -47,6 +47,7 @@ export const BODY_FORMAT = { export const ASSERTION_TYPE = { TEXT: "Text", REGEX: "Regex", + JSON_PATH: "JSON", DURATION: "Duration" } @@ -514,10 +515,11 @@ export class Assertions extends BaseConfig { super(); this.text = []; this.regex = []; + this.jsonPath = []; this.duration = undefined; this.set(options); - this.sets({text: Text, regex: Regex}, options); + this.sets({text: Text, regex: Regex, jsonPath: JSONPath}, options); } initOptions(options) { @@ -560,6 +562,21 @@ export class Regex extends AssertionType { } } +export class JSONPath extends AssertionType { + constructor(options) { + super(ASSERTION_TYPE.JSON_PATH); + this.expression = undefined; + this.expect = undefined; + this.description = undefined; + + this.set(options); + } + + isValid() { + return !!this.expression; + } +} + export class Duration extends AssertionType { constructor(options) { super(ASSERTION_TYPE.DURATION); @@ -886,7 +903,13 @@ class JMXGenerator { let assertions = request.assertions; if (assertions.regex.length > 0) { assertions.regex.filter(this.filter).forEach(regex => { - httpSamplerProxy.put(this.getAssertion(regex)); + httpSamplerProxy.put(this.getResponseAssertion(regex)); + }) + } + + if (assertions.jsonPath.length > 0) { + assertions.jsonPath.filter(this.filter).forEach(item => { + httpSamplerProxy.put(this.getJSONPathAssertion(item)); }) } @@ -896,7 +919,12 @@ class JMXGenerator { } } - getAssertion(regex) { + getJSONPathAssertion(jsonPath) { + let name = jsonPath.description; + return new JSONPathAssertion(name, jsonPath); + } + + getResponseAssertion(regex) { let name = regex.description; let type = JMX_ASSERTION_CONDITION.CONTAINS; // 固定用Match,自己写正则 let value = regex.expression; diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index fea7530c11..9a6a16ebab 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -404,6 +404,7 @@ export default { start_with: "Start with", end_with: "End With", value: "Value", + expect: "Expect Value", expression: "Expression", response_in_time: "Response in time", }, diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 08ee6d5d81..cace501b08 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -402,6 +402,7 @@ export default { start_with: "以...开始", end_with: "以...结束", value: "值", + expect: "期望值", expression: "Perl型正则表达式", response_in_time: "响应时间在...毫秒以内", }, diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 92ecd463e3..2edc62f4c0 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -403,6 +403,7 @@ export default { start_with: "以…開始", end_with: "以…結束", value: "值", + expect: "期望值", expression: "Perl型規則運算式", response_in_time: "回應時間在…毫秒以內", },