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 207c960234..f4fad53752 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 @@ -9,6 +9,7 @@ public class AssertionType { 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/scenario/assertions/AssertionXPath2.java b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionXPath2.java new file mode 100644 index 0000000000..4aa838bc41 --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/dto/scenario/assertions/AssertionXPath2.java @@ -0,0 +1,13 @@ +package io.metersphere.api.dto.scenario.assertions; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +@EqualsAndHashCode(callSuper = true) +@Data +public class AssertionXPath2 extends AssertionType { + private String expression; + public AssertionXPath2() { + setType(AssertionType.XPATH2); + } +} 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 ecbe07467b..5a7cac35a8 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 @@ -9,5 +9,6 @@ public class Assertions { private List regex; private List jsonPath; private List jsr223; + private List xPath2; private AssertionDuration duration; } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java index cc3ad10c74..d12ed264c4 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APIBackendListenerClient.java @@ -305,9 +305,11 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl private ResponseAssertionResult getResponseAssertionResult(AssertionResult assertionResult) { ResponseAssertionResult responseAssertionResult = new ResponseAssertionResult(); - responseAssertionResult.setMessage(assertionResult.getFailureMessage()); responseAssertionResult.setName(assertionResult.getName()); responseAssertionResult.setPass(!assertionResult.isFailure() && !assertionResult.isError()); + if (!responseAssertionResult.isPass()) { + responseAssertionResult.setMessage(assertionResult.getFailureMessage()); + } return responseAssertionResult; } diff --git a/backend/src/main/java/io/metersphere/notice/service/MailService.java b/backend/src/main/java/io/metersphere/notice/service/MailService.java index d750768e89..b39564c1e7 100644 --- a/backend/src/main/java/io/metersphere/notice/service/MailService.java +++ b/backend/src/main/java/io/metersphere/notice/service/MailService.java @@ -1,9 +1,7 @@ package io.metersphere.notice.service; -import io.metersphere.base.domain.ApiTestReport; -import io.metersphere.base.domain.LoadTestReportWithBLOBs; -import io.metersphere.base.domain.SystemParameter; -import io.metersphere.base.domain.TestCaseWithBLOBs; +import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.UserMapper; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.NoticeConstants; import io.metersphere.commons.constants.ParamConstants; @@ -46,6 +44,8 @@ public class MailService { private UserService userService; @Resource private SystemParameterService systemParameterService; + @Resource + private UserMapper userMapper; //接口和性能测试 public void sendLoadNotification(MessageDetail messageDetail, LoadTestReportWithBLOBs loadTestReport, String eventType) { @@ -297,7 +297,8 @@ public class MailService { Map context = new HashMap<>(); BaseSystemConfigDTO baseSystemConfigDTO = systemParameterService.getBaseInfo(); context.put("url", baseSystemConfigDTO.getUrl()); - context.put("creator", reviewRequest.getCreator()); + User user = userMapper.selectByPrimaryKey(reviewRequest.getCreator()); + context.put("creator", user.getName()); context.put("reviewName", reviewRequest.getName()); context.put("start", start); context.put("end", end); @@ -328,6 +329,8 @@ public class MailService { context.put("start", start); context.put("end", end); context.put("id", testPlan.getId()); + User user = userMapper.selectByPrimaryKey(testPlan.getCreator()); + context.put("creator", user.getName()); return context; } diff --git a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java index 96b1123c33..d342ec32c6 100644 --- a/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java +++ b/backend/src/main/java/io/metersphere/performance/service/PerformanceTestService.java @@ -229,7 +229,6 @@ public class PerformanceTestService { startEngine(loadTest, engine, request.getTriggerMode()); LoadTestReportWithBLOBs loadTestReport = loadTestReportMapper.selectByPrimaryKey(engine.getReportId()); - loadTestReport.setTriggerMode("API"); if (StringUtils.equals(NoticeConstants.API, loadTestReport.getTriggerMode()) || StringUtils.equals(NoticeConstants.SCHEDULE, loadTestReport.getTriggerMode())) { performanceNoticeTask.registerNoticeTask(loadTestReport); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java index a08e4de8f8..8a76212498 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseReviewService.java @@ -561,7 +561,9 @@ public class TestCaseReviewService { } /*编辑,新建,完成,删除通知内容*/ - private static String getReviewContext(SaveTestCaseReviewRequest reviewRequest, String type) { + private String getReviewContext(SaveTestCaseReviewRequest reviewRequest, String type) { + + User user = userMapper.selectByPrimaryKey(reviewRequest.getCreator()); Long startTime = reviewRequest.getCreateTime(); Long endTime = reviewRequest.getEndTime(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -577,11 +579,11 @@ public class TestCaseReviewService { } String context = ""; if (StringUtils.equals(NoticeConstants.CREATE, type)) { - context = "测试评审任务通知:" + reviewRequest.getCreator() + "发起的" + "'" + reviewRequest.getName() + "'" + "待开始,计划开始时间是" + start + "计划结束时间为" + end + "请跟进"; + context = "测试评审任务通知:" + user.getName() + "发起的" + "'" + reviewRequest.getName() + "'" + "待开始,计划开始时间是" + start + "计划结束时间为" + end + "请跟进"; } else if (StringUtils.equals(NoticeConstants.UPDATE, type)) { - context = "测试评审任务通知:" + reviewRequest.getCreator() + "发起的" + "'" + reviewRequest.getName() + "'" + "已完成,计划开始时间是" + start + "计划结束时间为" + end + "已完成"; + context = "测试评审任务通知:" + user.getName() + "发起的" + "'" + reviewRequest.getName() + "'" + "已完成,计划开始时间是" + start + "计划结束时间为" + end + "已完成"; } else if (StringUtils.equals(NoticeConstants.DELETE, type)) { - context = "测试评审任务通知:" + reviewRequest.getCreator() + "发起的" + "'" + reviewRequest.getName() + "'" + "计划开始时间是" + start + "计划结束时间为" + end + "已删除"; + context = "测试评审任务通知:" + user.getName() + "发起的" + "'" + reviewRequest.getName() + "'" + "计划开始时间是" + start + "计划结束时间为" + end + "已删除"; } return context; diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 08e37ea2f4..c8157e472d 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -93,6 +93,8 @@ public class TestPlanService { DingTaskService dingTaskService; @Resource WxChatTaskService wxChatTaskService; + @Resource + UserMapper userMapper; public void addTestPlan(AddTestPlanRequest testPlan) { if (getTestPlanByName(testPlan.getName()).size() > 0) { @@ -534,7 +536,8 @@ public class TestPlanService { return projectName; } - private static String getTestPlanContext(AddTestPlanRequest testPlan, String type) { + private String getTestPlanContext(AddTestPlanRequest testPlan, String type) { + User user = userMapper.selectByPrimaryKey(testPlan.getCreator()); Long startTime = testPlan.getPlannedStartTime(); Long endTime = testPlan.getPlannedEndTime(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @@ -554,11 +557,11 @@ public class TestPlanService { } String context = ""; if (StringUtils.equals(NoticeConstants.CREATE, type)) { - context = "测试计划任务通知:" + testPlan.getCreator() + "创建的" + "'" + testPlan.getName() + "'" + "待开始,计划开始时间是" + start + "计划结束时间为" + end + "请跟进"; + context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "待开始,计划开始时间是" + start + "计划结束时间为" + end + "请跟进"; } else if (StringUtils.equals(NoticeConstants.UPDATE, type)) { - context = "测试计划任务通知:" + testPlan.getCreator() + "创建的" + "'" + testPlan.getName() + "'" + "已完成,计划开始时间是" + start + "计划结束时间为" + end + "已完成"; + context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "已完成,计划开始时间是" + start + "计划结束时间为" + end + "已完成"; } else if (StringUtils.equals(NoticeConstants.DELETE, type)) { - context = "测试计划任务通知:" + testPlan.getCreator() + "创建的" + "'" + testPlan.getName() + "'" + "计划开始时间是" + start + "计划结束时间为" + end + "已删除"; + context = "测试计划任务通知:" + user.getName() + "创建的" + "'" + testPlan.getName() + "'" + "计划开始时间是" + start + "计划结束时间为" + end + "已删除"; } return context; } diff --git a/frontend/src/business/components/api/test/components/assertion/ApiAssertionXPath2.vue b/frontend/src/business/components/api/test/components/assertion/ApiAssertionXPath2.vue new file mode 100644 index 0000000000..c88444def3 --- /dev/null +++ b/frontend/src/business/components/api/test/components/assertion/ApiAssertionXPath2.vue @@ -0,0 +1,72 @@ + + + + + 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 75293cf38f..30d3042f66 100644 --- a/frontend/src/business/components/api/test/components/assertion/ApiAssertions.vue +++ b/frontend/src/business/components/api/test/components/assertion/ApiAssertions.vue @@ -8,6 +8,7 @@ + @@ -16,6 +17,7 @@ + @@ -52,11 +54,13 @@ import MsApiAssertionJsonPath from "./ApiAssertionJsonPath"; import MsApiAssertionJsr223 from "@/business/components/api/test/components/assertion/ApiAssertionJsr223"; import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList"; + import MsApiAssertionXPath2 from "./ApiAssertionXPath2"; export default { name: "MsApiAssertions", components: { + MsApiAssertionXPath2, MsApiAssertionJsr223, MsApiJsonpathSuggestList, MsApiAssertionJsonPath, 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 0b880a76e1..e46c3d2904 100644 --- a/frontend/src/business/components/api/test/components/assertion/ApiAssertionsEdit.vue +++ b/frontend/src/business/components/api/test/components/assertion/ApiAssertionsEdit.vue @@ -20,6 +20,16 @@ +
+
+ {{ 'XPath' }} +
+
+ +
+
+
{{ $t("api_test.request.assertions.script") }} @@ -47,11 +57,14 @@ import MsApiAssertionDuration from "./ApiAssertionDuration"; import {Assertions} from "../../model/ScenarioModel"; import MsApiAssertionJsonPath from "./ApiAssertionJsonPath"; import MsApiAssertionJsr223 from "@/business/components/api/test/components/assertion/ApiAssertionJsr223"; +import MsApiAssertionXPath2 from "./ApiAssertionXPath2"; export default { name: "MsApiAssertionsEdit", - components: {MsApiAssertionJsr223, MsApiAssertionJsonPath, MsApiAssertionDuration, MsApiAssertionRegex}, + components: { + MsApiAssertionXPath2, + MsApiAssertionJsr223, MsApiAssertionJsonPath, MsApiAssertionDuration, MsApiAssertionRegex}, props: { assertions: Assertions, @@ -92,6 +105,10 @@ export default { border-left: 2px solid #1FDD02; } +.assertion-item-editing.x_path { + border-left: 2px solid #fca130; +} + .regex-item { margin-top: 10px; } diff --git a/frontend/src/business/components/api/test/model/JMX.js b/frontend/src/business/components/api/test/model/JMX.js index 1540aad191..3368c376de 100644 --- a/frontend/src/business/components/api/test/model/JMX.js +++ b/frontend/src/business/components/api/test/model/JMX.js @@ -439,6 +439,16 @@ export class JSONPathAssertion extends DefaultTestElement { } } +export class XPath2Assertion extends DefaultTestElement { + constructor(testName, xPath) { + super('XPath2Assertion', 'XPath2AssertionGui', 'XPath2Assertion', testName); + this.xPath = xPath || {}; + this.stringProp('XPath.xpath', this.xPath.expression); + this.stringProp('XPath.namespace'); + this.boolProp('XPath.negate', false); + } +} + export class ResponseCodeAssertion extends ResponseAssertion { constructor(testName, type, value, assumeSuccess, 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 f638e962ac..0ca23c0749 100644 --- a/frontend/src/business/components/api/test/model/ScenarioModel.js +++ b/frontend/src/business/components/api/test/model/ScenarioModel.js @@ -25,7 +25,7 @@ import { ThreadGroup, XPath2Extractor, IfController as JMXIfController, - ConstantTimer as JMXConstantTimer, TCPSampler, JSR223Assertion, + ConstantTimer as JMXConstantTimer, TCPSampler, JSR223Assertion, XPath2Assertion, } from "./JMX"; import Mock from "mockjs"; import {funcFilters} from "@/common/js/func-filter"; @@ -96,6 +96,7 @@ export const ASSERTION_TYPE = { JSON_PATH: "JSON", DURATION: "Duration", JSR223: "JSR223", + XPATH2: "XPath2", } export const ASSERTION_REGEX_SUBJECT = { @@ -741,10 +742,11 @@ export class Assertions extends BaseConfig { this.regex = []; this.jsonPath = []; this.jsr223 = []; + this.xPath2 = []; this.duration = undefined; this.set(options); - this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223}, options); + this.sets({text: Text, regex: Regex, jsonPath: JSONPath, jsr223: AssertionJSR223, xPath2: XPath2}, options); } initOptions(options) { @@ -826,6 +828,23 @@ 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); + } + + // setJSONPathDescription() { + // this.description = this.expression + " expect: " + (this.expect ? this.expect : ''); + // } + + isValid() { + return !!this.expression; + } +} + export class Duration extends AssertionType { constructor(options) { super(ASSERTION_TYPE.DURATION); @@ -1001,7 +1020,8 @@ class JMXHttpRequest { this.domain = environment.config.httpConfig.domain; this.port = environment.config.httpConfig.port; this.protocol = environment.config.httpConfig.protocol; - let envPath = environment.config.httpConfig.protocol + "://" + environment.config.httpConfig.socket; + let url = new URL(environment.config.httpConfig.protocol + "://" + environment.config.httpConfig.socket); + let envPath = url.pathname === '/' ? '' : url.pathname; this.path = this.getPostQueryParameters(request, decodeURIComponent(envPath + (request.path ? request.path : ''))); } this.connectTimeout = request.connectTimeout; @@ -1397,11 +1417,11 @@ class JMXGenerator { body = this.filterKV(request.body.kvs); this.addRequestBodyFile(httpSamplerProxy, request, testId); } else { - httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true); body.push({name: '', value: request.body.raw, encode: false, enable: true}); } if (request.method !== 'GET') { + httpSamplerProxy.boolProp('HTTPSampler.postBodyRaw', true); httpSamplerProxy.add(new HTTPSamplerArguments(body)); } } @@ -1437,6 +1457,12 @@ class JMXGenerator { }) } + if (assertions.xPath2.length > 0) { + assertions.xPath2.filter(this.filter).forEach(item => { + httpSamplerProxy.put(this.getXpathAssertion(item)); + }) + } + if (assertions.jsr223.length > 0) { assertions.jsr223.filter(this.filter).forEach(item => { httpSamplerProxy.put(this.getJSR223Assertion(item)); @@ -1459,6 +1485,11 @@ class JMXGenerator { return new JSR223Assertion(name, item); } + getXpathAssertion(item) { + let name = item.expression; + return new XPath2Assertion(name, item); + } + getResponseAssertion(regex) { let name = regex.description; let type = JMX_ASSERTION_CONDITION.CONTAINS; // 固定用Match,自己写正则 diff --git a/frontend/src/business/components/track/plan/view/comonents/TestCaseRelevance.vue b/frontend/src/business/components/track/plan/view/comonents/TestCaseRelevance.vue index 387dd66c6f..5d979498d4 100644 --- a/frontend/src/business/components/track/plan/view/comonents/TestCaseRelevance.vue +++ b/frontend/src/business/components/track/plan/view/comonents/TestCaseRelevance.vue @@ -286,12 +286,13 @@ }, getProject() { if (this.planId) { - this.$post("/test/plan/project/", {planId: this.planId}, res => { + this.result = this.$post("/test/plan/project/", {planId: this.planId}, res => { let data = res.data; if (data) { this.projects = data; this.projectId = data[0].id; this.projectName = data[0].name; + this.search(); } }) }