diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiBatchRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiBatchRequest.java index b30a290eeb..8c28f72bbc 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/ApiBatchRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiBatchRequest.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.Setter; import java.util.List; +import java.util.Map; @Getter @Setter @@ -24,7 +25,7 @@ public class ApiBatchRequest extends ApiDefinitionWithBLOBs { */ private boolean isSelectAllDate; - private List filters; + private Map> filters; private String name; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionBatchProcessingRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionBatchProcessingRequest.java index 834a98831a..44a17ab1c6 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionBatchProcessingRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionBatchProcessingRequest.java @@ -4,6 +4,7 @@ import lombok.Getter; import lombok.Setter; import java.util.List; +import java.util.Map; /** * 接口定义模块-批量处理请求类 @@ -24,7 +25,7 @@ public class ApiDefinitionBatchProcessingRequest { */ private boolean isSelectAllDate; - private List filters; + private Map> filters; private String name; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionRequest.java index 3073ed8300..0f40f3cc78 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/ApiDefinitionRequest.java @@ -23,7 +23,7 @@ public class ApiDefinitionRequest { private String planId; private boolean recent = false; private List orders; - private List filters; + private Map> filters; private Map combine; private List ids; private boolean isSelectThisWeedData = false; diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java index ef017c8bd2..0dad6ff49f 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiDefinitionRequest.java @@ -6,7 +6,6 @@ import io.metersphere.base.domain.Schedule; import lombok.Getter; import lombok.Setter; -import java.util.ArrayList; import java.util.List; @Setter @@ -49,5 +48,5 @@ public class SaveApiDefinitionRequest { private List bodyUploadIds; - private List tags = new ArrayList<>(); + private String tags; } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiTestCaseRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiTestCaseRequest.java index a04a8bd01e..34815ceee7 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiTestCaseRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/SaveApiTestCaseRequest.java @@ -4,7 +4,6 @@ import io.metersphere.api.dto.definition.request.MsTestElement; import lombok.Getter; import lombok.Setter; -import java.util.ArrayList; import java.util.List; @Setter @@ -37,5 +36,5 @@ public class SaveApiTestCaseRequest { private List bodyUploadIds; - private List tags = new ArrayList<>(); + private String tags; } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 09bd985273..48934297bc 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -365,8 +365,8 @@ public class ApiDefinitionService { scenario.setHashTree(elements); } if (StringUtils.isNotEmpty(element.getString("variables"))) { - LinkedList variables = mapper.readValue(element.getString("variables"), - new TypeReference>() {}); + LinkedList variables = mapper.readValue(element.getString("variables"), + new TypeReference>() {}); scenario.setVariables(variables); } group.setEnableCookieShare(scenario.isEnableCookieShare()); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java index 8f03769d17..945fb26e11 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiTestCaseService.java @@ -222,7 +222,7 @@ public class ApiTestCaseService { test.setPriority(request.getPriority()); test.setUpdateTime(System.currentTimeMillis()); test.setDescription(request.getDescription()); - test.setTags(JSON.toJSONString(new HashSet<>(request.getTags()))); + test.setTags(request.getTags()); apiTestCaseMapper.updateByPrimaryKeySelective(test); return test; } @@ -244,7 +244,7 @@ public class ApiTestCaseService { test.setUpdateTime(System.currentTimeMillis()); test.setDescription(request.getDescription()); test.setNum(getNextNum(request.getApiDefinitionId())); - test.setTags(JSON.toJSONString(new HashSet<>(request.getTags()))); + test.setTags(request.getTags()); apiTestCaseMapper.insert(test); return test; } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml index ee3a5eaa1f..0fe7d1398b 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionMapper.xml @@ -243,9 +243,17 @@ - and api_definition.status in - - #{value} + + + + + and api_definition.status in + + #{value} + + + + diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.java index 590e9605d7..29bf5bc474 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.java @@ -8,5 +8,5 @@ import java.util.List; public interface ExtTestPlanLoadCaseMapper { List selectIdsNotInPlan(@Param("projectId") String projectId, @Param("planId") String planId); - List selectTestPlanLoadCaseList(@Param("planId") String planId); + List selectTestPlanLoadCaseList(@Param("planId") String planId, @Param("projectId") String projectId); } diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml index e99558c432..7b2f666cbf 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtTestPlanLoadCaseMapper.xml @@ -11,11 +11,25 @@ ) \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/track/dto/TestPlanLoadCaseDTO.java b/backend/src/main/java/io/metersphere/track/dto/TestPlanLoadCaseDTO.java index 3e08fed138..572532baf0 100644 --- a/backend/src/main/java/io/metersphere/track/dto/TestPlanLoadCaseDTO.java +++ b/backend/src/main/java/io/metersphere/track/dto/TestPlanLoadCaseDTO.java @@ -9,4 +9,5 @@ import lombok.Setter; public class TestPlanLoadCaseDTO extends TestPlanLoadCase { private String userName; private String caseName; + private String projectName; } diff --git a/backend/src/main/java/io/metersphere/track/request/testcase/EditTestCaseRequest.java b/backend/src/main/java/io/metersphere/track/request/testcase/EditTestCaseRequest.java index 37d58ec62e..3da65becc6 100644 --- a/backend/src/main/java/io/metersphere/track/request/testcase/EditTestCaseRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testcase/EditTestCaseRequest.java @@ -16,5 +16,4 @@ public class EditTestCaseRequest extends TestCaseWithBLOBs { * 复制测试用例后,要进行复制的文件Id list */ private List fileIds = new ArrayList<>(); - private List caseTags = new ArrayList<>(); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index cd263edad7..9237735195 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -585,7 +585,6 @@ public class TestCaseService { throw new IllegalArgumentException(Translator.get("file_cannot_be_null")); } - request.setTags(JSON.toJSONString(new HashSet<>(request.getCaseTags()))); final TestCaseWithBLOBs testCaseWithBLOBs = addTestCase(request); // 复制用例时传入文件ID进行复制 @@ -642,7 +641,6 @@ public class TestCaseService { }); } - request.setTags(JSON.toJSONString(new HashSet<>(request.getCaseTags()))); editTestCase(request); return request.getId(); } diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanLoadCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanLoadCaseService.java index caa4ce1e6e..28fbb8df29 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanLoadCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanLoadCaseService.java @@ -1,6 +1,7 @@ package io.metersphere.track.service; import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.LoadTestMapper; import io.metersphere.base.mapper.LoadTestReportMapper; import io.metersphere.base.mapper.TestPlanLoadCaseMapper; import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper; @@ -20,6 +21,7 @@ import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.stream.Collectors; @Service @Transactional(rollbackFor = Exception.class) @@ -35,6 +37,8 @@ public class TestPlanLoadCaseService { private SqlSessionFactory sqlSessionFactory; @Resource private LoadTestReportMapper loadTestReportMapper; + @Resource + private LoadTestMapper loadTestMapper; public List relevanceList(LoadCaseRequest request) { List ids = extTestPlanLoadCaseMapper.selectIdsNotInPlan(request.getProjectId(), request.getTestPlanId()); @@ -45,7 +49,7 @@ public class TestPlanLoadCaseService { } public List list(LoadCaseRequest request) { - return extTestPlanLoadCaseMapper.selectTestPlanLoadCaseList(request.getTestPlanId()); + return extTestPlanLoadCaseMapper.selectTestPlanLoadCaseList(request.getTestPlanId(), request.getProjectId()); } public void relevanceCase(LoadCaseRequest request) { @@ -96,4 +100,17 @@ public class TestPlanLoadCaseService { } return true; } + + public void deleteByRelevanceProjectIds(String id, List relevanceProjectIds) { + LoadTestExample loadTestExample = new LoadTestExample(); + loadTestExample.createCriteria().andProjectIdIn(relevanceProjectIds); + List loadTests = loadTestMapper.selectByExample(loadTestExample); + TestPlanLoadCaseExample testPlanLoadCaseExample = new TestPlanLoadCaseExample(); + TestPlanLoadCaseExample.Criteria criteria = testPlanLoadCaseExample.createCriteria().andTestPlanIdEqualTo(id); + if (!CollectionUtils.isEmpty(loadTests)) { + List ids = loadTests.stream().map(LoadTest::getId).collect(Collectors.toList()); + criteria.andLoadCaseIdNotIn(ids); + } + testPlanLoadCaseMapper.deleteByExample(testPlanLoadCaseExample); + } } 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 36aa71e46b..437c7407e7 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -87,6 +87,8 @@ public class TestPlanService { private TestPlanApiCaseService testPlanApiCaseService; @Resource private TestPlanScenarioCaseService testPlanScenarioCaseService; + @Resource + private TestPlanLoadCaseService testPlanLoadCaseService; public synchronized void addTestPlan(AddTestPlanRequest testPlan) { if (getTestPlanByName(testPlan.getName()).size() > 0) { @@ -234,6 +236,7 @@ public class TestPlanService { } testPlanApiCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds); testPlanScenarioCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds); + testPlanLoadCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds); } } diff --git a/backend/src/main/java/io/metersphere/xpack b/backend/src/main/java/io/metersphere/xpack index 068127ce59..9f4a9bbf46 160000 --- a/backend/src/main/java/io/metersphere/xpack +++ b/backend/src/main/java/io/metersphere/xpack @@ -1 +1 @@ -Subproject commit 068127ce59ea8b016434ed52a9de4a7a4b13bdb4 +Subproject commit 9f4a9bbf46fc1333dbcccea21f83e27e3ec10b1f diff --git a/frontend/package.json b/frontend/package.json index 7d7d671066..a0f2d44465 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -45,7 +45,8 @@ "vuedraggable": "^2.23.2", "vuex": "^3.1.2", "xml-js": "^1.6.11", - "yan-progress": "^1.0.3" + "yan-progress": "^1.0.3", + "vue-jsonpath-picker": "^1.1.5" }, "devDependencies": { "@vue/cli-plugin-babel": "^4.1.0", 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 322b2d647e..556a826cc6 100644 --- a/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue +++ b/frontend/src/business/components/api/definition/components/assertion/ApiAssertions.vue @@ -48,11 +48,16 @@ + + + + @@ -67,11 +72,15 @@ import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList"; import MsApiAssertionXPath2 from "./ApiAssertionXPath2"; import {getUUID} from "@/common/js/utils"; + import ApiJsonPathSuggestButton from "./ApiJsonPathSuggestButton"; + import MsApiJsonpathSuggest from "./ApiJsonpathSuggest"; export default { name: "MsApiAssertions", components: { + MsApiJsonpathSuggest, + ApiJsonPathSuggestButton, MsApiAssertionXPath2, MsApiAssertionJsr223, MsApiJsonpathSuggestList, @@ -83,6 +92,7 @@ assertions: {}, node: {}, request: {}, + response: {}, customizeStyle: { type: String, default: "margin-top: 10px" @@ -101,6 +111,7 @@ type: "", loading: false, reloadData: "", + suggestData: {} } }, @@ -114,11 +125,17 @@ this.$emit('copyRow', this.assertions, this.node); }, suggestJsonOpen() { - if (!this.request.debugRequestResult) { + if (!this.response || !this.response.responseResult || !this.response.responseResult.body) { this.$message(this.$t('api_test.request.assertions.debug_first')); return; } - this.$refs.jsonpathSuggestList.open(); + try { + this.suggestData = JSON.parse(this.response.responseResult.body); + } catch (e) { + this.$error(this.$t('api_test.request.assertions.json_path_err')); + return; + } + this.$refs.jsonpathSuggest.open(); }, reload() { this.loading = true @@ -133,14 +150,12 @@ remove() { this.$emit('remove', this.assertions, this.node); }, - addJsonpathSuggest(jsonPathList) { - jsonPathList.forEach(jsonPath => { - let jsonItem = new JSONPath(); - jsonItem.expression = jsonPath.json_path; - jsonItem.expect = jsonPath.json_value; - jsonItem.setJSONPathDescription(); - this.assertions.jsonPath.push(jsonItem); - }); + addJsonPathSuggest(data) { + let jsonItem = new JSONPath(); + jsonItem.expression = data.path; + jsonItem.expect = data.value; + jsonItem.setJSONPathDescription(); + this.assertions.jsonPath.push(jsonItem); }, clearJson() { this.assertions.jsonPath = []; @@ -159,6 +174,7 @@ padding: 10px; margin: 5px 0; border-radius: 5px; + border: #DCDFE6 solid 1px; } .icon.is-active { @@ -168,4 +184,5 @@ /deep/ .el-card__body { padding: 15px; } + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiJsonPathSuggestButton.vue b/frontend/src/business/components/api/definition/components/assertion/ApiJsonPathSuggestButton.vue new file mode 100644 index 0000000000..f708459ab3 --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiJsonPathSuggestButton.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggest.vue b/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggest.vue new file mode 100644 index 0000000000..1ca9c8f22a --- /dev/null +++ b/frontend/src/business/components/api/definition/components/assertion/ApiJsonpathSuggest.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue index 24c5ac8f2d..4387c8a728 100644 --- a/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue +++ b/frontend/src/business/components/api/definition/components/case/ApiCaseItem.vue @@ -24,26 +24,7 @@ - - {{ tag }} - - - - + +
@@ -57,15 +38,15 @@
- - - - - - + + + + + + @@ -112,10 +93,12 @@ import MsSqlBasisParameters from "../request/database/BasisParameters"; import MsTcpBasisParameters from "../request/tcp/BasisParameters"; import MsDubboBasisParameters from "../request/dubbo/BasisParameters"; import MsApiExtendBtns from "../reference/ApiExtendBtns"; +import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag"; export default { name: "ApiCaseItem", components: { + MsInputTag, MsTag, MsTipButton, MsApiRequestForm, @@ -139,8 +122,6 @@ export default { visible: false, condition: {}, isShowInput: false, - inputVisible: false, - inputValue: '' } }, props: { @@ -213,21 +194,25 @@ export default { } }, saveTestCase(row) { + let tmp = JSON.parse(JSON.stringify(row)); this.isShowInput = false; - if (this.validate(row)) { + if (this.validate(tmp)) { return; } - let bodyFiles = this.getBodyUploadFiles(row); - row.projectId = getCurrentProjectID(); - row.active = true; - row.request.path = this.api.path; - row.request.method = this.api.method; - row.apiDefinitionId = row.apiDefinitionId || this.api.id; + let bodyFiles = this.getBodyUploadFiles(tmp); + tmp.projectId = getCurrentProjectID(); + tmp.active = true; + tmp.request.path = this.api.path; + tmp.request.method = this.api.method; + tmp.apiDefinitionId = tmp.apiDefinitionId || this.api.id; let url = "/api/testcase/create"; - if (row.id) { + if (tmp.id) { url = "/api/testcase/update"; } - this.$fileUpload(url, null, bodyFiles, row, () => { + if (tmp.tags instanceof Array) { + tmp.tags = JSON.stringify(tmp.tags); + } + this.$fileUpload(url, null, bodyFiles, tmp, () => { this.$success(this.$t('commons.save_success')); this.$emit('refresh'); }); @@ -293,28 +278,6 @@ export default { } return bodyUploadFiles; }, - handleClose(tag) { - this.apiCase.tags.splice(this.apiCase.tags.indexOf(tag), 1); - this.saveTestCase(this.apiCase) - }, - - showTagInput() { - this.inputVisible = true; - this.$nextTick(_ => { - this.$refs.saveTagInput.$refs.input.focus(); - }); - }, - - handleInputConfirm() { - let inputValue = this.inputValue; - if (inputValue) { - this.apiCase.tags.push(inputValue); - this.saveTestCase(this.apiCase) - } - this.inputVisible = false; - this.inputValue = ''; - } - } } @@ -356,22 +319,4 @@ export default { .is-selected { background: #EFF7FF; } - -.el-tag + .el-tag { - margin-left: 10px; -} - -.button-new-tag { - margin-left: 10px; - height: 20px; - /*line-height: 30px;*/ - padding-top: 0; - padding-bottom: 0; -} - -.input-new-tag { - width: 90px; - margin-left: 10px; - vertical-align: bottom; -} diff --git a/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue b/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue index 973b4b923a..6b94fda754 100644 --- a/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue +++ b/frontend/src/business/components/api/definition/components/case/ApiCaseList.vue @@ -177,9 +177,7 @@ export default { this.addCase(); } this.apiCaseList.forEach(apiCase => { - if (!apiCase.tags) { - apiCase.tags = []; - } else { + if (apiCase.tags && apiCase.tags.length > 0) { apiCase.tags = JSON.parse(apiCase.tags); } }) diff --git a/frontend/src/business/components/api/definition/components/complete/BasisApi.vue b/frontend/src/business/components/api/definition/components/complete/BasisApi.vue index 439bb55c58..8dc7951e7e 100644 --- a/frontend/src/business/components/api/definition/components/complete/BasisApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/BasisApi.vue @@ -17,8 +17,9 @@
- {{$t('api_test.definition.select_comp.no_data')}}, - {{$t('api_test.definition.select_comp.add_data')}} + {{ $t('api_test.definition.select_comp.no_data') }}, + + {{ $t('api_test.definition.select_comp.add_data') }}
@@ -54,27 +55,7 @@ - - {{ tag }} - - - - + - + @@ -94,111 +75,69 @@ diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue index fb8a41a3ce..88d9af53d7 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteDubboApi.vue @@ -6,87 +6,91 @@
- {{$t('commons.save')}} - {{$t('commons.test')}} + {{ $t('commons.save') }} + {{ $t('commons.test') }}
-

{{$t('test_track.plan_view.base_info')}}

+

{{ $t('test_track.plan_view.base_info') }}


- + -

{{$t('api_test.definition.request.req_param')}}

+

{{ $t('api_test.definition.request.req_param') }}

diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue index 5b39e8d319..85e7a65921 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue @@ -77,27 +77,7 @@ - - {{ tag }} - - - - + - + @@ -134,10 +114,11 @@ import {WORKSPACE_ID} from '../../../../../../common/js/constants'; import {API_STATUS, REQ_METHOD} from "../../model/JsonData"; import MsJsr233Processor from "../processor/Jsr233Processor"; import {KeyValue} from "../../model/ApiTestModel"; +import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag"; export default { name: "MsAddCompleteHttpApi", - components: {MsResponseText, MsApiRequestForm, MsJsr233Processor}, + components: {MsResponseText, MsApiRequestForm, MsJsr233Processor, MsInputTag}, data() { let validateURL = (rule, value, callback) => { if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) { @@ -165,8 +146,6 @@ export default { currentModule: {}, reqOptions: REQ_METHOD, options: API_STATUS, - inputVisible: false, - inputValue: '' } }, props: {moduleOptions: {}, request: {}, response: {}, basisData: {}}, @@ -192,6 +171,9 @@ export default { this.request.path = this.httpForm.path; this.request.method = this.httpForm.method; this.httpForm.request.useEnvironment = undefined; + if (this.httpForm.tags instanceof Array) { + this.httpForm.tags = JSON.stringify(this.httpForm.tags); + } }, saveApi() { this.$refs['httpForm'].validate((valid) => { @@ -243,26 +225,6 @@ export default { this.$error(this.$t('api_test.request.url_invalid'), 2000); } }, - - handleClose(tag) { - this.httpForm.tags.splice(this.httpForm.tags.indexOf(tag), 1); - }, - - showInput() { - this.inputVisible = true; - this.$nextTick(_ => { - this.$refs.saveTagInput.$refs.input.focus(); - }); - }, - - handleInputConfirm() { - let inputValue = this.inputValue; - if (inputValue) { - this.httpForm.tags.push(inputValue); - } - this.inputVisible = false; - this.inputValue = ''; - } }, created() { @@ -270,11 +232,6 @@ export default { if (!this.basisData.environmentId) { this.basisData.environmentId = ""; } - if (!this.basisData.tags) { - this.basisData.tags = []; - } else { - this.basisData.tags = JSON.parse(this.basisData.tags); - } this.httpForm = JSON.parse(JSON.stringify(this.basisData)); } @@ -314,22 +271,4 @@ export default { .ms-left-buttion { margin: 6px 0px 8px 30px; } - -.el-tag + .el-tag { - margin-left: 10px; -} - -.button-new-tag { - margin-left: 10px; - height: 20px; - /*line-height: 30px;*/ - padding-top: 0; - padding-bottom: 0; -} - -.input-new-tag { - width: 90px; - margin-left: 10px; - vertical-align: bottom; -} diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue index 4ca16a2d0b..94e434e7b4 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteSQLApi.vue @@ -5,84 +5,88 @@
- {{$t('commons.save')}} - {{$t('commons.test')}} + {{ $t('commons.save') }} + {{ $t('commons.test') }}
-

{{$t('test_track.plan_view.base_info')}}

+

{{ $t('test_track.plan_view.base_info') }}


- + -

{{$t('api_test.definition.request.req_param')}}

+

{{ $t('api_test.definition.request.req_param') }}

diff --git a/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue index 6d26a4da03..4148d999e2 100644 --- a/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue +++ b/frontend/src/business/components/api/definition/components/complete/EditCompleteTCPApi.vue @@ -5,22 +5,23 @@
- {{$t('commons.save')}} - {{$t('commons.test')}} + {{ $t('commons.save') }} + {{ $t('commons.test') }}
-

{{$t('test_track.plan_view.base_info')}}

+

{{ $t('test_track.plan_view.base_info') }}


- + -

{{$t('api_test.definition.request.req_param')}}

+

{{ $t('api_test.definition.request.req_param') }}

@@ -28,62 +29,65 @@ diff --git a/frontend/src/business/components/api/definition/components/debug/DebugHttpPage.vue b/frontend/src/business/components/api/definition/components/debug/DebugHttpPage.vue index bcb2bbb98b..280bfe04cb 100644 --- a/frontend/src/business/components/api/definition/components/debug/DebugHttpPage.vue +++ b/frontend/src/business/components/api/definition/components/debug/DebugHttpPage.vue @@ -26,7 +26,7 @@

{{$t('api_test.definition.request.req_param')}}

- + diff --git a/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue b/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue index 620fcd5218..d29ca874e3 100644 --- a/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue +++ b/frontend/src/business/components/api/definition/components/extract/ApiExtract.vue @@ -12,6 +12,7 @@ +
@@ -36,10 +37,17 @@ Add
+ + +
+ + + @@ -49,17 +57,23 @@ import MsApiExtractEdit from "./ApiExtractEdit"; import MsApiExtractCommon from "./ApiExtractCommon"; import {getUUID} from "@/common/js/utils"; + import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton"; + import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest"; + import {ExtractJSONPath} from "../../../test/model/ScenarioModel"; export default { name: "MsApiExtract", components: { + MsApiJsonpathSuggest, + ApiJsonPathSuggestButton, MsApiExtractCommon, MsApiExtractEdit, }, props: { extract: {}, + response: {}, node: {}, customizeStyle: { type: String, @@ -77,6 +91,7 @@ type: "", reloadData: "", loading: false, + suggestData: {} } }, @@ -101,6 +116,28 @@ item.active = !item.active; this.reload(); }, + suggestJsonOpen() { + if (!this.response || !this.response.responseResult || !this.response.responseResult.body) { + this.$message(this.$t('api_test.request.assertions.debug_first')); + return; + } + try { + this.suggestData = JSON.parse(this.response.responseResult.body); + } catch (e) { + this.$error(this.$t('api_test.request.assertions.json_path_err')); + return; + } + this.$refs.jsonpathSuggest.open(); + }, + addJsonPathSuggest(data) { + let option = {}; + option.variable = data.key; + option.expression = data.path; + this.extract.json.push(new ExtractJSONPath(option)); + }, + clearJson() { + this.extract.json = []; + } }, computed: { list() { diff --git a/frontend/src/business/components/api/definition/components/list/ApiList.vue b/frontend/src/business/components/api/definition/components/list/ApiList.vue index 183fca236f..281c299d10 100644 --- a/frontend/src/business/components/api/definition/components/list/ApiList.vue +++ b/frontend/src/business/components/api/definition/components/list/ApiList.vue @@ -10,6 +10,8 @@ @@ -27,18 +29,21 @@ - - + + + column-key="status" + sortable="custom" + :filters="statusFilters" + :label="$t('api_test.definition.api_status')"> @@ -66,10 +71,9 @@ @@ -128,14 +132,16 @@ import MsBottomContainer from "../BottomContainer"; import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn"; import MsBatchEdit from "../basis/BatchEdit"; import {API_METHOD_COLOUR, API_STATUS, REQ_METHOD} from "../../model/JsonData"; -import {getCurrentProjectID} from "@/common/js/utils"; +import {_filter, _sort, getCurrentProjectID} from "@/common/js/utils"; import {WORKSPACE_ID} from '@/common/js/constants'; import ApiListContainer from "./ApiListContainer"; import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll"; +import ApiStatus from "@/business/components/api/definition/components/list/ApiStatus"; export default { name: "ApiList", components: { + ApiStatus, MsTableSelectAll, ApiListContainer, MsTableButton, @@ -168,6 +174,12 @@ export default { {id: 'method', name: this.$t('api_test.definition.api_type')}, {id: 'userId', name: this.$t('api_test.definition.api_principal')}, ], + statusFilters: [ + {text: this.$t('test_track.plan.plan_status_prepare'), value: 'Prepare'}, + {text: this.$t('test_track.plan.plan_status_running'), value: 'Underway'}, + {text: this.$t('test_track.plan.plan_status_completed'), value: 'Completed'}, + {text: this.$t('test_track.plan.plan_status_trash'), value: 'Trash'}, + ], valueArr: { status: API_STATUS, method: REQ_METHOD, @@ -208,6 +220,7 @@ export default { }, }, created: function () { + this.condition.filters = {status: ["Prepare", "Underway", "Completed"]}; this.initTable(); this.getMaintainerOptions(); }, @@ -220,8 +233,12 @@ export default { }, trashEnable() { if (this.trashEnable) { - this.initTable(); + this.condition.filters = {status: ["Trash"]}; + this.condition.moduleIds = []; + } else { + this.condition.filters = {status: ["Prepare", "Underway", "Completed"]}; } + this.initTable(); } }, methods: { @@ -235,14 +252,8 @@ export default { this.unSelection = []; this.selectDataCounts = 0; - this.condition.filters = ["Prepare", "Underway", "Completed"]; this.condition.moduleIds = this.selectNodeIds; - if (this.trashEnable) { - this.condition.filters = ["Trash"]; - this.condition.moduleIds = []; - } - this.condition.projectId = getCurrentProjectID(); if (this.currentProtocol != null) { this.condition.protocol = this.currentProtocol; @@ -256,15 +267,6 @@ export default { case 'thisWeekCount': this.condition.selectThisWeedData = true; break; - case 'Prepare': - this.condition.filters = [this.selectDataRange]; - break; - case 'Completed': - this.condition.filters = [this.selectDataRange]; - break; - case 'Underway': - this.condition.filters = [this.selectDataRange]; - break; case 'uncoverage': this.condition.apiCaseCoverage = 'uncoverage'; break; @@ -278,8 +280,10 @@ export default { this.tableData = response.data.listObject; this.unSelection = response.data.listObject.map(s => s.id); - this.tableData.forEach(row => { - row.showTags = JSON.parse(row.tags); + this.tableData.forEach(item => { + if (item.tags && item.tags.length > 0) { + item.tags = JSON.parse(item.tags); + } }) }); } @@ -342,9 +346,13 @@ export default { this.$emit('editApi', row); }, reductionApi(row) { - row.request = null; - row.response = null; - let rows = [row]; + let tmp = JSON.parse(JSON.stringify(row)); + tmp.request = null; + tmp.response = null; + if (tmp.tags instanceof Array) { + tmp.tags = JSON.stringify(tmp.tags); + } + let rows = [tmp]; this.$post('/api/definition/reduction/', rows, () => { this.$success(this.$t('commons.save_success')); this.search(); @@ -495,7 +503,19 @@ export default { let rowArray = Array.from(rowSets) let ids = rowArray.map(s => s.id); return ids; - } + }, + sort(column) { + // 每次只对一个字段排序 + if (this.condition.orders) { + this.condition.orders = []; + } + _sort(column, this.condition); + this.initTable(); + }, + filter(filters) { + _filter(filters, this.condition); + this.initTable(); + }, }, } diff --git a/frontend/src/business/components/api/definition/components/list/ApiStatus.vue b/frontend/src/business/components/api/definition/components/list/ApiStatus.vue new file mode 100644 index 0000000000..5b5cfe132d --- /dev/null +++ b/frontend/src/business/components/api/definition/components/list/ApiStatus.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue b/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue index ef886a900d..b4605968f6 100644 --- a/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue +++ b/frontend/src/business/components/api/definition/components/request/http/ApiHttpRequestForm.vue @@ -76,12 +76,10 @@ - + - + - -
@@ -131,6 +129,7 @@ }, props: { request: {}, + response: {}, showScript: Boolean, headers: { type: Array, diff --git a/frontend/src/business/components/api/definition/components/request/http/ApiRequestForm.vue b/frontend/src/business/components/api/definition/components/request/http/ApiRequestForm.vue index 0f2274df8a..53613fb7b5 100644 --- a/frontend/src/business/components/api/definition/components/request/http/ApiRequestForm.vue +++ b/frontend/src/business/components/api/definition/components/request/http/ApiRequestForm.vue @@ -1,6 +1,6 @@ @@ -12,6 +12,7 @@ components: {MsApiHttpRequestForm}, props: { request: {}, + response: {}, headers: Array, isShowEnable: { type: Boolean, diff --git a/frontend/src/business/components/api/test/components/ApiScenarioConfig.vue b/frontend/src/business/components/api/test/components/ApiScenarioConfig.vue index f0f68b5f9b..bb98da5dd1 100644 --- a/frontend/src/business/components/api/test/components/ApiScenarioConfig.vue +++ b/frontend/src/business/components/api/test/components/ApiScenarioConfig.vue @@ -77,7 +77,7 @@ import {Request, Scenario} from "../model/ScenarioModel"; import draggable from 'vuedraggable'; import MsApiScenarioSelect from "@/business/components/api/test/components/ApiScenarioSelect"; import {parseEnvironment} from "../model/EnvironmentModel"; -import MsHorizontalDragBar from "../../../common/components/MsHorizontalDragBar"; +import MsHorizontalDragBar from "../../../common/components/dragbar/MsLeft2RightDragBar"; export default { name: "MsApiScenarioConfig", diff --git a/frontend/src/business/components/common/components/MsAsideContainer.vue b/frontend/src/business/components/common/components/MsAsideContainer.vue index 29b0b83f07..1a3a365511 100644 --- a/frontend/src/business/components/common/components/MsAsideContainer.vue +++ b/frontend/src/business/components/common/components/MsAsideContainer.vue @@ -11,7 +11,7 @@ - - diff --git a/frontend/src/business/components/common/components/MsDrawer.vue b/frontend/src/business/components/common/components/MsDrawer.vue index dd1ae608e3..1fc8251ce0 100644 --- a/frontend/src/business/components/common/components/MsDrawer.vue +++ b/frontend/src/business/components/common/components/MsDrawer.vue @@ -1,21 +1,29 @@ + + diff --git a/frontend/src/business/components/common/components/MsHorizontalDragBar.vue b/frontend/src/business/components/common/components/dragbar/MsLeft2RightDragBar.vue similarity index 79% rename from frontend/src/business/components/common/components/MsHorizontalDragBar.vue rename to frontend/src/business/components/common/components/dragbar/MsLeft2RightDragBar.vue index 9abe466d3a..63dd57a64f 100644 --- a/frontend/src/business/components/common/components/MsHorizontalDragBar.vue +++ b/frontend/src/business/components/common/components/dragbar/MsLeft2RightDragBar.vue @@ -1,10 +1,10 @@ diff --git a/frontend/src/business/components/common/components/dragbar/MsRight2LeftDragBar.vue b/frontend/src/business/components/common/components/dragbar/MsRight2LeftDragBar.vue new file mode 100644 index 0000000000..1fad325007 --- /dev/null +++ b/frontend/src/business/components/common/components/dragbar/MsRight2LeftDragBar.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/frontend/src/business/components/track/case/components/TestCaseEdit.vue b/frontend/src/business/components/track/case/components/TestCaseEdit.vue index 68bc2fbb8c..ff9667f901 100644 --- a/frontend/src/business/components/track/case/components/TestCaseEdit.vue +++ b/frontend/src/business/components/track/case/components/TestCaseEdit.vue @@ -69,27 +69,7 @@ - - {{ tag }} - - - - + - + @@ -298,10 +278,11 @@ import TestCaseAttachment from "@/business/components/track/case/components/Test import {getCurrentProjectID} from "../../../../../common/js/utils"; import {buildNodePath} from "../../../api/definition/model/NodeTree"; import CaseComment from "@/business/components/track/case/components/CaseComment"; +import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag"; export default { name: "TestCaseEdit", - components: {CaseComment, MsDialogFooter, TestCaseAttachment}, + components: {MsInputTag, CaseComment, MsDialogFooter, TestCaseAttachment}, data() { return { result: {}, @@ -323,7 +304,6 @@ export default { result: '' }], remark: '', - caseTags: [] }, moduleOptions: [], maintainerOptions: [], @@ -355,8 +335,6 @@ export default { {value: 'manual', label: this.$t('test_track.case.manual')} ], testCase: {}, - inputVisible: false, - inputValue: '' }; }, props: { @@ -387,7 +365,7 @@ export default { open(testCase) { this.testCase = {}; if (testCase) { - testCase.caseTags = JSON.parse(testCase.tags); + testCase.tags = JSON.parse(testCase.tags); // 复制 不查询评论 this.testCase = testCase.isCopy ? {} : testCase; } @@ -425,7 +403,6 @@ export default { this.form.type = 'functional'; this.form.method = 'manual'; this.form.maintainer = user.id; - this.form.caseTags = []; } this.getSelectOptions(); @@ -538,6 +515,10 @@ export default { if (param.method != 'auto') { param.testId = null; } + if (this.form.tags instanceof Array) { + this.form.tags = JSON.stringify(this.form.tags); + } + param.tags = this.form.tags; return param; }, getOption(param) { @@ -641,7 +622,6 @@ export default { desc: '', result: '' }]; - this.caseTags = []; this.uploadList = []; this.fileList = []; this.tableData = []; @@ -729,26 +709,6 @@ export default { /// todo: 是否需要对文件内容和大小做限制 return file.size > 0; }, - - handleClose(tag) { - this.form.caseTags.splice(this.form.caseTags.indexOf(tag), 1); - }, - - showInput() { - this.inputVisible = true; - this.$nextTick(_ => { - this.$refs.saveTagInput.$refs.input.focus(); - }); - }, - - handleInputConfirm() { - let inputValue = this.inputValue; - if (inputValue) { - this.form.caseTags.push(inputValue); - } - this.inputVisible = false; - this.inputValue = ''; - } } } @@ -788,21 +748,4 @@ export default { height: calc(100vh - 120px); } -.el-tag + .el-tag { - margin-left: 10px; -} - -.button-new-tag { - margin-left: 10px; - height: 20px; - /*line-height: 30px;*/ - padding-top: 0; - padding-bottom: 0; -} - -.input-new-tag { - width: 90px; - margin-left: 10px; - vertical-align: bottom; -} diff --git a/frontend/src/business/components/track/case/components/TestCaseList.vue b/frontend/src/business/components/track/case/components/TestCaseList.vue index d22ec68d2b..42e4bcd735 100644 --- a/frontend/src/business/components/track/case/components/TestCaseList.vue +++ b/frontend/src/business/components/track/case/components/TestCaseList.vue @@ -113,10 +113,9 @@ @@ -319,8 +318,10 @@ export default { this.tableData = data.listObject; // this.selectIds.clear(); this.selectRows.clear(); - this.tableData.forEach(row => { - row.showTags = JSON.parse(row.tags); + this.tableData.forEach(item => { + if (item.tags && item.tags.length > 0) { + item.tags = JSON.parse(item.tags); + } }) }); } diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue index 2355476d20..5e9dde2718 100644 --- a/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestPlanApiCaseList.vue @@ -62,10 +62,9 @@ @@ -265,8 +264,10 @@ export default { this.result = this.$post('/test/plan/api/case/list/' + this.currentPage + "/" + this.pageSize, this.condition, response => { this.total = response.data.itemCount; this.tableData = response.data.listObject; - this.tableData.forEach(row => { - row.showTags = JSON.parse(row.tags); + this.tableData.forEach(item => { + if (item.tags && item.tags.length > 0) { + item.tags = JSON.parse(item.tags); + } }) }); }, @@ -479,7 +480,4 @@ export default { margin-right: 20px; } -.el-tag { - margin-left: 10px; -} diff --git a/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoad.vue b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoad.vue index fd76aa1724..433feddc02 100644 --- a/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoad.vue +++ b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoad.vue @@ -12,7 +12,7 @@ class="table-list" @refresh="refresh" :plan-id="planId" - :select-node-ids="selectNodeIds" + :select-project-id="selectProjectId" :select-parent-nodes="selectParentNodes" @relevanceCase="openTestCaseRelevanceDialog" ref="testPlanLoadCaseList"/> @@ -46,6 +46,7 @@ export default { result: {}, selectNodeIds: [], selectParentNodes: [], + selectProjectId: "", treeNodes: [], } }, @@ -74,8 +75,7 @@ export default { this.$refs.testCaseLoadRelevance.open(); }, nodeChange(node, nodeIds, pNodes) { - this.selectNodeIds = nodeIds; - this.selectParentNodes = pNodes; + this.selectProjectId = node.key; // 切换node后,重置分页数 this.$refs.testPlanLoadCaseList.currentPage = 1; this.$refs.testPlanLoadCaseList.pageSize = 10; @@ -84,6 +84,8 @@ export default { if (this.planId) { this.result = this.$get("/case/node/list/plan/" + this.planId, response => { this.treeNodes = response.data; + // 性能测试与模块无关,过滤项目下模块 + this.treeNodes.map(node => node.children = null); }); } }, diff --git a/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList.vue b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList.vue index 7c2adc1752..b2d52f5de1 100644 --- a/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList.vue +++ b/frontend/src/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList.vue @@ -30,12 +30,11 @@ :label="$t('commons.name')" show-overflow-tooltip> - - - - - - + + { + this.selectRows = new Set(); + let param = {}; + param.testPlanId = this.planId; + if (this.selectProjectId && this.selectProjectId !== 'root') { + param.projectId = this.selectProjectId; + } + this.$post("/test/plan/load/case/list/" + this.currentPage + "/" + this.pageSize, param, response => { let data = response.data; this.total = data.itemCount; this.tableData = data.listObject; diff --git a/frontend/src/business/components/xpack b/frontend/src/business/components/xpack index 7d43154a7c..010ad7a5f0 160000 --- a/frontend/src/business/components/xpack +++ b/frontend/src/business/components/xpack @@ -1 +1 @@ -Subproject commit 7d43154a7c19732407a8e9ace8a7d1ea13c91f36 +Subproject commit 010ad7a5f072a5e9d368c756a2473bbd20781433 diff --git a/frontend/src/business/main.js b/frontend/src/business/main.js index c722e34cc7..87b3862555 100644 --- a/frontend/src/business/main.js +++ b/frontend/src/business/main.js @@ -18,8 +18,10 @@ import '../common/css/menu-header.css'; import '../common/css/main.css'; import CKEditor from '@ckeditor/ckeditor5-vue'; import VueFab from 'vue-float-action-button' -import {horizontalDrag} from "../common/js/directive"; +import {left2RightDrag, bottom2TopDrag, right2LeftDrag} from "../common/js/directive"; import JsonSchemaEditor from './components/common/json-schema/schema/index'; +import JSONPathPicker from 'vue-jsonpath-picker'; + Vue.use(JsonSchemaEditor); import VuePapaParse from 'vue-papa-parse' Vue.use(VuePapaParse) @@ -37,6 +39,7 @@ Vue.use(message); Vue.use(CKEditor); Vue.use(YanProgress); Vue.use(VueFab); +Vue.use(JSONPathPicker); // v-permission Vue.directive('permission', permission); @@ -49,7 +52,9 @@ Vue.directive('xpack', xpack); Vue.directive('tester', tester); //支持左右拖拽 -Vue.directive('horizontal-drag', horizontalDrag); +Vue.directive('left-to-right-drag', left2RightDrag); +Vue.directive('right-to-left-drag', right2LeftDrag); +Vue.directive('bottom-to-top-drag', bottom2TopDrag); new Vue({ el: '#app', diff --git a/frontend/src/common/css/main.css b/frontend/src/common/css/main.css index c14a241985..4b3a23ab1e 100644 --- a/frontend/src/common/css/main.css +++ b/frontend/src/common/css/main.css @@ -161,7 +161,7 @@ html,body { overflow: visible; } -.ms-select-all .el-icon-arrow-down { +.ms-select-all th:nth-child(2) .el-icon-arrow-down { position: absolute; display: inline-block; top: -7px; diff --git a/frontend/src/common/js/directive.js b/frontend/src/common/js/directive.js index b7d78b4d0a..60854f51d4 100644 --- a/frontend/src/common/js/directive.js +++ b/frontend/src/common/js/directive.js @@ -1,4 +1,4 @@ -export const horizontalDrag = { +export const left2RightDrag = { inserted(el, binding) { el.onmousedown = function (e) { const init = e.clientX; @@ -7,7 +7,49 @@ export const horizontalDrag = { document.onmousemove = function (e) { const end = e.clientX; const newWidth = end - init + initWidth; - parent.style.width = newWidth + "px"; + if (newWidth < document.body.clientWidth - 10 && newWidth > 10) { + parent.style.width = newWidth + "px"; + } + }; + document.onmouseup = function () { + document.onmousemove = document.onmouseup = null; + }; + }; + } +}; + +export const right2LeftDrag = { + inserted(el, binding) { + el.onmousedown = function (e) { + const init = e.clientX; + const parent = el.parentNode; + const initWidth = parent.offsetWidth; + document.onmousemove = function (e) { + const end = e.clientX; + const newWidth = initWidth - (end - init); + if (newWidth < document.body.clientWidth - 10 && newWidth > 10) { + parent.style.width = newWidth + "px"; + } + }; + document.onmouseup = function () { + document.onmousemove = document.onmouseup = null; + }; + }; + } +}; + +export const bottom2TopDrag = { + inserted(el, binding) { + el.onmousedown = function (e) { + const init = e.clientY; + const parent = el.parentNode; + const initHeight = parent.offsetHeight; + document.onmousemove = function (e) { + const end = e.clientY; + const newHeight = initHeight - (end - init); + if (newHeight < document.body.clientHeight - 10 && newHeight > 10) { + parent.style.height = newHeight + "px"; + } }; document.onmouseup = function () { document.onmousemove = document.onmouseup = null; diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 48e63e73cf..15294bee00 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -732,6 +732,7 @@ export default { json_path_suggest: "JSONPath Assertion Suggest", json_path_clear: "Clear JSONPath Assertion", debug_first: "First, debug to get the response", + suggest_tip: "Click the note to add the JSONPath assertion", }, extract: { label: "Extract from response", @@ -742,6 +743,9 @@ export default { regex_expression: "Regular expression", json_path_expression: "JSONPath expression", xpath_expression: "XPath expression", + suggest_tip: "Click the note to add the JSONPath extraction", + json_path_suggest: "JSONPath Extraction Suggest", + json_path_clear: "Clear JSONPath Extraction", }, processor: { pre_exec_script: "PreProcessor", @@ -1085,6 +1089,7 @@ export default { plan_status_prepare: "Not started", plan_status_running: "Starting", plan_status_completed: "Completed", + plan_status_trash: "Trashed", planned_start_time: "Scheduled Start Time", planned_end_time: "Scheduled End Time", actual_start_time: "Actual Start Time", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index 720cc13d63..d0e6662eec 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -732,6 +732,7 @@ export default { variable_name: "变量名称", set_failure_status: "设置失败状态", set_failure_msg: "设置失败消息", + suggest_tip: "点击便签添加JSONPath断言", }, extract: { label: "提取", @@ -742,6 +743,9 @@ export default { regex_expression: "Perl型正则表达式", json_path_expression: "JSONPath表达式", xpath_expression: "XPath表达式", + suggest_tip: "点击便签添加JSONPath提取", + json_path_suggest: "推荐JSONPath提取", + json_path_clear: "清空JSONPath提取", }, processor: { pre_exec_script: "预执行脚本", @@ -1086,6 +1090,7 @@ export default { plan_status_prepare: "未开始", plan_status_running: "进行中", plan_status_completed: "已完成", + plan_status_trash: "废弃", planned_start_time: "计划开始", planned_end_time: "计划结束", actual_start_time: "实际开始", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 7c150fdfdf..0967c2e45e 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -65,7 +65,7 @@ export default { refresh: '刷新', remark: '備註', delete: '刪除', - reduction: '恢复', + reduction: '恢復', not_filled: '未填寫', please_select: '請選擇', search_by_name: '根據名稱搜索', @@ -250,9 +250,9 @@ export default { mail: '郵件', nail_robot: '釘釘機器人', enterprise_wechat_robot: '企業微信機器人', - notes: '1.事件,接收方式,接收人為必填項;\n' + - ' 2.接收方式除郵件外webhook為必填;\n' + - ' 3.機器人選擇為群機器人,安全驗證選擇“自定義關鍵詞” :"任務通知"', + notes: '1.釘釘和企業群裏新建壹個自定義機器人,然後復制 webhook 地址在我們平臺上;\n' + + ' 2.機器人選擇為群機器人,安全驗證選擇“自定義關鍵詞” :"任務通知";\n' + + ' 3.選擇接收人時必須是妳所建的群裏包含的人,接收人手機號為必填項且為釘釘企業所使用的手機號,', message: '事件,接收人,接收方式為必填項', message_webhook: '接收方式為釘釘和企業機器人時,webhook為必填項', template: "模版" @@ -307,7 +307,7 @@ export default { manager: '項目管理', no_data: '無數據', select: '選擇項目', - repeatable: '接口定义URL可重复' + repeatable: '接口定義URL可重復' }, member: { create: '添加成員', @@ -377,9 +377,9 @@ export default { test_stop_now: '立即停止', test_stop_now_confirm: '確定要立即停止當前測試嗎?', test_rerun_confirm: '確定要再次執行當前測試嗎?', - downloadJtl: '下載JTL', test_stop_success: '停止成功', test_execute_again: '再次執行', + downloadJtl: '下載JTL', export: '導出', compare: '比較', generation_error: '報告生成錯誤, 無法查看, 請檢查日誌詳情!', @@ -436,9 +436,9 @@ export default { ramp_up_time_within: '在', ramp_up_time_minutes: '秒內,分', ramp_up_time_seconds: '秒內增加並發用戶', - iterate_num: '迭代次數 (次): ', - by_iteration: '按迭代次數', - by_duration: '按壓測時長', + iterate_num: '叠代次數 (次): ', + by_iteration: '按叠代次數', + by_duration: '按持續時間', ramp_up_time_times: '次增加並發用戶', advanced_config_error: '高級配置校驗失敗', domain_bind: '域名綁定', @@ -514,18 +514,18 @@ export default { add_data: "去添加" }, request: { - grade_info: "按等級筛选", + grade_info: "按等級篩選", run_env: "運行環境", select_case: "搜索用例", case: "用例", - responsible: "责任人", - title: "创建接口", + responsible: "責任人", + title: "創建接口", path_info: "請輸入接口的URL,如/api/demo/#{id},其中id為路徑參數", path_all_info: "請輸入完整測試地址", fast_debug: "快捷調試", close_all_label: "關閉所有標簽", save_as: "另存為新接口", - load_case: "加载用例", + load_case: "加載用例", save_as_case: "另存為新用例", update_api: "更新接口", body_form_data: "form-data", @@ -541,10 +541,10 @@ export default { verified: "認證", encryption: "加密", req_param: "請求參數", - res_param: "響應内容", - batch_delete: "批量删除", + res_param: "響應內容", + batch_delete: "批量刪除", delete_confirm: "確認刪除接口", - delete_confirm_step: "確認刪除步骤", + delete_confirm_step: "確認刪除步驟", assertions_rule: "斷言規則", response_header: "響應頭", response_body: "響應體", @@ -557,12 +557,12 @@ export default { post_script: "後置腳本", extract_param: "提取參數", add_module: "創建模塊", - edit_api: "编辑接口", + edit_api: "編輯接口", test_plan_select: "請選擇測試計劃", create_info: '創建', update_info: '更新', batch_edit: "批量編輯", - path_valid_info: "請求路径无效", + path_valid_info: "請求路徑無效", } }, automation: { @@ -575,15 +575,15 @@ export default { external_import: "外部導入", wait_controller: "等待控制器", if_controller: "條件控制器", - loop_controller: "循环控制器", + loop_controller: "循環控制器", scenario_import: "場景導入", - customize_script: "自定義脚本", + customize_script: "自定義腳本", customize_req: "自定義請求", reference_info: "請選擇接口或用例", scenario_test: "場景", - add_scenario: "创建場景", - scenario_name: "場景名称", - case_level: "用例等级", + add_scenario: "創建場景", + scenario_name: "場景名稱", + case_level: "用例等級", tag: "標簽", creator: "創建人", update_time: "最後更新時間", @@ -597,14 +597,14 @@ export default { edit: "編輯", execute: "執行", copy: "復制", - remove: "删除", + remove: "刪除", view_ref: "查看引用", case_ref: "用例引用", schedule: "定時任務", - scenario_ref: "场景引用", - plan_ref: "测试计划引用", - batch_add_plan: "添加到测试计划", - batch_execute: "批量执行", + scenario_ref: "場景引用", + plan_ref: "測試計劃引用", + batch_add_plan: "添加到測試計劃", + batch_execute: "批量執行", scenario: { principal: "責任人", select_principal: "請選擇責任人", @@ -614,7 +614,7 @@ export default { }, report_name_info: '請輸入報告名稱', save_case_info: '請先保存用例', - reference_deleted: '引用已删除', + reference_deleted: '引用已刪除', }, environment: { name: "環境名稱", @@ -707,6 +707,7 @@ export default { text: "文本", regex: "正則", response_time: "響應時間", + jsr223: "腳本", select_type: "請選擇類型", select_subject: "請選擇對象", select_condition: "請選擇條件", @@ -731,6 +732,7 @@ export default { variable_name: "變量名稱", set_failure_status: "設置失敗狀態", set_failure_msg: "設置失敗消息", + suggest_tip: "點擊便簽添加JSONPath斷言", }, extract: { label: "提取", @@ -741,6 +743,9 @@ export default { regex_expression: "Perl型正則表達式", json_path_expression: "JSONPath表達式", xpath_expression: "XPath表達式", + suggest_tip: "點擊便簽添加JSONPath提取", + json_path_suggest: "推薦JSONPath提取", + json_path_clear: "清空JSONPath提取", }, processor: { pre_exec_script: "預執行腳本", @@ -791,7 +796,7 @@ export default { close_connection: "關閉連接", so_linger: "SO LINGER", eol_byte: "行尾(EOL)字節值", - request: "要發送的文本", + request: "發送文本", username: "用戶名", password: "密碼", login: "登錄設置", @@ -808,7 +813,7 @@ export default { ms_tip: "支持 Metersphere json 格式", ms_export_tip: "通過 Metersphere 接口測試頁面或者瀏覽器插件導出 json 格式文件", postman_tip: "只支持 Postman Collection v2.1 格式的 json 文件", - swagger_tip: "支持 Swagger 2.0 與 3.0版本的 json 文件", + swagger_tip: "支持 Swagger 2.0 與 3.0 版本的 json 文件", post_export_tip: "通過 Postman 導出測試集合", swagger_export_tip: "通過 Swagger 頁面導出", suffixFormatErr: "文件格式不符合要求", @@ -850,42 +855,42 @@ export default { }, api_details_card: { title: "接口", - this_week_add: "本週新增:", + this_week_add: "本周新增:", }, test_case_details_card: { title: "用例", - this_week_add: "本週新增:", - this_week_execute: "本週執行: {0}次", + this_week_add: "本周新增:", + this_week_execute: "本周執行: {0}次", executed: "歷史總執行: {0}次", - this_week_add_sm: "本週新增:", - this_week_execute_sm: "本週執行:
{0}次", + this_week_add_sm: "本周新增:", + this_week_execute_sm: "本周執行:
{0}次", executed_sm: "歷史總執行:
{0}次", }, test_scene_details_card: { title: "場景", - this_week_add: "本週新增:", - this_week_execute: "本週執行: {0}次", + this_week_add: "本周新增:", + this_week_execute: "本周執行: {0}次", executed: "歷史總執行: {0}次", - this_week_add_sm: "本週新增:", - this_week_execute_sm: "本週執行:
{0}次", + this_week_add_sm: "本周新增:", + this_week_execute_sm: "本周執行:
{0}次", executed_sm: "歷史總執行:
{0}次", }, schedule_task_details_card: { title: "定時任務", - this_week_add: "本週新增: {0}个", - this_week_execute: "本週執行: {0}次", + this_week_add: "本周新增: {0}個", + this_week_execute: "本周執行: {0}次", executed: "歷史總執行: {0}次", - this_week_add_sm: "本週新增:
{0}个", - this_week_execute_sm: "本週執行:
{0}次", + this_week_add_sm: "本周新增:
{0}個", + this_week_execute_sm: "本周執行:
{0}次", executed_sm: "歷史總執行:
{0}次", }, failed_case_list: { - title: "過去7天測試計畫失敗用例TOP 10", + title: "過去7天測試計劃失敗用例TOP 10", table_coloum: { index: "排名", case_name: "用例名稱", case_type: "用例類型", - test_plan: "所屬測試計畫", + test_plan: "所屬測試計劃", failure_times: "失敗次數", }, table_value: { @@ -949,11 +954,11 @@ export default { not_exist: "測試報告不存在", }, api_monitor: { - to: "到", - start_time: "開始時間", - end_time: "結束時間", - today: "今天", - this_week: "本週", + to: "至", + start_time: "開始日期", + end_time: "結束日期", + today: "今日", + this_week: "本周", this_mouth: "本月", please_search: "請搜索", date: "日期" @@ -1085,6 +1090,7 @@ export default { plan_status_prepare: "未開始", plan_status_running: "進行中", plan_status_completed: "已完成", + plan_status_trash: "廢棄", planned_start_time: "計劃開始", planned_end_time: "計劃結束", actual_start_time: "實際開始", @@ -1124,8 +1130,8 @@ export default { send: "發送", description_is_null: "評論內容不能為空!", send_success: "評論成功!", - cannot_edit: "無法編輯此評論!", - cannot_delete: "無法刪除此評論!", + cannot_edit: "無法編輯此評論!", + cannot_delete: "無法刪除此評論!", }, review_view: { review: "評審", @@ -1363,7 +1369,7 @@ export default { performance: "性能測試數量", resource_pool: "可用測試資源池", max_threads: "最大並發數", - duration: "壓測時長(分鐘)", + duration: "壓測時長(秒)", use_default: "使用默認配額", yes: "是", no: "否", @@ -1376,55 +1382,54 @@ export default { clean: "清空" }, schema: { - title: "标题", - import_json: "导入 json", - base_setting: "基础设置", - all_setting: "编辑源码", - default: "默认值", + title: "標題", + import_json: "導入 json", + base_setting: "基礎設置", + all_setting: "編輯源碼", + default: "默認值", description: "描述", - adv_setting: "高级设置", - add_child_node: "添加子节点", - add_sibling_node: "添加兄弟节点", - add_node: "添加兄弟/子节点", - remove_node: "删除节点", - child_node: "子节点", - sibling_node: "兄弟节点", - ok: "确定", + adv_setting: "高級設置", + add_child_node: "添加子節點", + add_sibling_node: "添加兄弟節點", + add_node: "添加兄弟/子節點", + remove_node: "刪除節點", + child_node: "子節點", + sibling_node: "兄弟節點", + ok: "確定", cancel: "取消", - minLength: "最小长度", - maxLength: "最大长度", - pattern: "用正则表达式约束字符串", - exclusiveMinimum: "开启后,数据必须大于最小值", - exclusiveMaximum: "开启后,数据必须小于最大值", + minLength: "最小長度", + maxLength: "最大長度", + pattern: "用正則表達式約束字符串", + exclusiveMinimum: "開啟後,數據必須大於最小值", + exclusiveMaximum: "開啟後,數據必須小於最大值", minimum: "最小值", maximum: "最大值", - uniqueItems: "开启后,每个元素都不相同", - minItems: "最小元素个数", - maxItems: "最大元素个数", - minProperties: "最小元素个数", - maxProperties: "最大元素个数", - checked_all: "全选", + uniqueItems: "開啟後,每個元素都不相同", + minItems: "最小元素個數", + maxItems: "最大元素個數", + minProperties: "最小元素個數", + maxProperties: "最大元素個數", + checked_all: "全選", valid_json: "不是合法的json字符串", - enum: "枚举", - enum_msg: "每行只能写一个值", - enum_desc: "备注", - enum_desc_msg: "备注描述信息", - required: "是否必须", + enum: "枚舉", + enum_msg: "每行只能寫壹個值", + enum_desc: "備註", + enum_desc_msg: "備註描述信息", + required: "是否必須", mock: "mock", - mockLink: "查看文档", + mockLink: "查看文檔", format: "格式化", - nothing: "无", - preview: "预览", - add_custom: "添加自定义属性" + nothing: "無", + preview: "預覽", + add_custom: "添加自定義屬性" }, loop: { loops_title: "次數循環", foreach: "ForEach 循環", while: "While 循環", - loops: "循環次数", + loops: "循環次數", interval: "循環間隔", proceed: "成功後繼續循環", timeout: "循環超時時間", } - };