fix(接口定义): 冲突合并

This commit is contained in:
fit2-zhao 2021-01-08 13:49:58 +08:00
commit 7c3b718863
52 changed files with 897 additions and 847 deletions

View File

@ -6,6 +6,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.List; import java.util.List;
import java.util.Map;
@Getter @Getter
@Setter @Setter
@ -24,7 +25,7 @@ public class ApiBatchRequest extends ApiDefinitionWithBLOBs {
*/ */
private boolean isSelectAllDate; private boolean isSelectAllDate;
private List<String> filters; private Map<String, List<String>> filters;
private String name; private String name;

View File

@ -4,6 +4,7 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* 接口定义模块-批量处理请求类 * 接口定义模块-批量处理请求类
@ -24,7 +25,7 @@ public class ApiDefinitionBatchProcessingRequest {
*/ */
private boolean isSelectAllDate; private boolean isSelectAllDate;
private List<String> filters; private Map<String, List<String>> filters;
private String name; private String name;

View File

@ -23,7 +23,7 @@ public class ApiDefinitionRequest {
private String planId; private String planId;
private boolean recent = false; private boolean recent = false;
private List<OrderRequest> orders; private List<OrderRequest> orders;
private List<String> filters; private Map<String, List<String>> filters;
private Map<String, Object> combine; private Map<String, Object> combine;
private List<String> ids; private List<String> ids;
private boolean isSelectThisWeedData = false; private boolean isSelectThisWeedData = false;

View File

@ -6,7 +6,6 @@ import io.metersphere.base.domain.Schedule;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Setter @Setter
@ -49,5 +48,5 @@ public class SaveApiDefinitionRequest {
private List<String> bodyUploadIds; private List<String> bodyUploadIds;
private List<String> tags = new ArrayList<>(); private String tags;
} }

View File

@ -4,7 +4,6 @@ import io.metersphere.api.dto.definition.request.MsTestElement;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Setter @Setter
@ -37,5 +36,5 @@ public class SaveApiTestCaseRequest {
private List<String> bodyUploadIds; private List<String> bodyUploadIds;
private List<String> tags = new ArrayList<>(); private String tags;
} }

View File

@ -365,8 +365,8 @@ public class ApiDefinitionService {
scenario.setHashTree(elements); scenario.setHashTree(elements);
} }
if (StringUtils.isNotEmpty(element.getString("variables"))) { if (StringUtils.isNotEmpty(element.getString("variables"))) {
LinkedList<ScenarioVariable> variables = mapper.readValue(element.getString("variables"), LinkedList<KeyValue> variables = mapper.readValue(element.getString("variables"),
new TypeReference<LinkedList<ScenarioVariable>>() {}); new TypeReference<LinkedList<KeyValue>>() {});
scenario.setVariables(variables); scenario.setVariables(variables);
} }
group.setEnableCookieShare(scenario.isEnableCookieShare()); group.setEnableCookieShare(scenario.isEnableCookieShare());

View File

@ -222,7 +222,7 @@ public class ApiTestCaseService {
test.setPriority(request.getPriority()); test.setPriority(request.getPriority());
test.setUpdateTime(System.currentTimeMillis()); test.setUpdateTime(System.currentTimeMillis());
test.setDescription(request.getDescription()); test.setDescription(request.getDescription());
test.setTags(JSON.toJSONString(new HashSet<>(request.getTags()))); test.setTags(request.getTags());
apiTestCaseMapper.updateByPrimaryKeySelective(test); apiTestCaseMapper.updateByPrimaryKeySelective(test);
return test; return test;
} }
@ -244,7 +244,7 @@ public class ApiTestCaseService {
test.setUpdateTime(System.currentTimeMillis()); test.setUpdateTime(System.currentTimeMillis());
test.setDescription(request.getDescription()); test.setDescription(request.getDescription());
test.setNum(getNextNum(request.getApiDefinitionId())); test.setNum(getNextNum(request.getApiDefinitionId()));
test.setTags(JSON.toJSONString(new HashSet<>(request.getTags()))); test.setTags(request.getTags());
apiTestCaseMapper.insert(test); apiTestCaseMapper.insert(test);
return test; return test;
} }

View File

@ -243,9 +243,17 @@
</foreach> </foreach>
</if> </if>
<if test="request.filters != null and request.filters.size() > 0"> <if test="request.filters != null and request.filters.size() > 0">
and api_definition.status in <foreach collection="request.filters.entrySet()" index="key" item="values">
<foreach collection="request.filters" item="value" separator="," open="(" close=")"> <if test="values != null and values.size() > 0">
#{value} <choose>
<when test="key=='status'">
and api_definition.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach> </foreach>
</if> </if>
<if test="request.apiCaseCoverage == 'uncoverage' "> <if test="request.apiCaseCoverage == 'uncoverage' ">

View File

@ -8,5 +8,5 @@ import java.util.List;
public interface ExtTestPlanLoadCaseMapper { public interface ExtTestPlanLoadCaseMapper {
List<String> selectIdsNotInPlan(@Param("projectId") String projectId, @Param("planId") String planId); List<String> selectIdsNotInPlan(@Param("projectId") String projectId, @Param("planId") String planId);
List<TestPlanLoadCaseDTO> selectTestPlanLoadCaseList(@Param("planId") String planId); List<TestPlanLoadCaseDTO> selectTestPlanLoadCaseList(@Param("planId") String planId, @Param("projectId") String projectId);
} }

View File

@ -11,11 +11,25 @@
) )
</select> </select>
<select id="selectTestPlanLoadCaseList" resultType="io.metersphere.track.dto.TestPlanLoadCaseDTO"> <select id="selectTestPlanLoadCaseList" resultType="io.metersphere.track.dto.TestPlanLoadCaseDTO">
select tplc.id, u.name as userName, tplc.create_time, tplc.update_time, tplc.test_plan_id, tplc.load_case_id, select tplc.id,
lt.status, lt.name as caseName, tplc.load_report_id u.name as userName,
tplc.create_time,
tplc.update_time,
tplc.test_plan_id,
tplc.load_case_id,
lt.status,
lt.name as caseName,
tplc.load_report_id,
p.name as projectName
from test_plan_load_case tplc from test_plan_load_case tplc
inner join load_test lt on tplc.load_case_id = lt.id inner join load_test lt on tplc.load_case_id = lt.id
inner join user u on lt.user_id = u.id inner join user u on lt.user_id = u.id
where tplc.test_plan_id = #{planId} inner join project p on lt.project_id = p.id
<where>
tplc.test_plan_id = #{planId}
<if test="projectId != null and projectId != ''">
and lt.project_id = #{projectId}
</if>
</where>
</select> </select>
</mapper> </mapper>

View File

@ -9,4 +9,5 @@ import lombok.Setter;
public class TestPlanLoadCaseDTO extends TestPlanLoadCase { public class TestPlanLoadCaseDTO extends TestPlanLoadCase {
private String userName; private String userName;
private String caseName; private String caseName;
private String projectName;
} }

View File

@ -16,5 +16,4 @@ public class EditTestCaseRequest extends TestCaseWithBLOBs {
* 复制测试用例后要进行复制的文件Id list * 复制测试用例后要进行复制的文件Id list
*/ */
private List<String> fileIds = new ArrayList<>(); private List<String> fileIds = new ArrayList<>();
private List<String> caseTags = new ArrayList<>();
} }

View File

@ -585,7 +585,6 @@ public class TestCaseService {
throw new IllegalArgumentException(Translator.get("file_cannot_be_null")); throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
} }
request.setTags(JSON.toJSONString(new HashSet<>(request.getCaseTags())));
final TestCaseWithBLOBs testCaseWithBLOBs = addTestCase(request); final TestCaseWithBLOBs testCaseWithBLOBs = addTestCase(request);
// 复制用例时传入文件ID进行复制 // 复制用例时传入文件ID进行复制
@ -642,7 +641,6 @@ public class TestCaseService {
}); });
} }
request.setTags(JSON.toJSONString(new HashSet<>(request.getCaseTags())));
editTestCase(request); editTestCase(request);
return request.getId(); return request.getId();
} }

View File

@ -1,6 +1,7 @@
package io.metersphere.track.service; package io.metersphere.track.service;
import io.metersphere.base.domain.*; import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.LoadTestMapper;
import io.metersphere.base.mapper.LoadTestReportMapper; import io.metersphere.base.mapper.LoadTestReportMapper;
import io.metersphere.base.mapper.TestPlanLoadCaseMapper; import io.metersphere.base.mapper.TestPlanLoadCaseMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper; import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper;
@ -20,6 +21,7 @@ import javax.annotation.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -35,6 +37,8 @@ public class TestPlanLoadCaseService {
private SqlSessionFactory sqlSessionFactory; private SqlSessionFactory sqlSessionFactory;
@Resource @Resource
private LoadTestReportMapper loadTestReportMapper; private LoadTestReportMapper loadTestReportMapper;
@Resource
private LoadTestMapper loadTestMapper;
public List<LoadTest> relevanceList(LoadCaseRequest request) { public List<LoadTest> relevanceList(LoadCaseRequest request) {
List<String> ids = extTestPlanLoadCaseMapper.selectIdsNotInPlan(request.getProjectId(), request.getTestPlanId()); List<String> ids = extTestPlanLoadCaseMapper.selectIdsNotInPlan(request.getProjectId(), request.getTestPlanId());
@ -45,7 +49,7 @@ public class TestPlanLoadCaseService {
} }
public List<TestPlanLoadCaseDTO> list(LoadCaseRequest request) { public List<TestPlanLoadCaseDTO> list(LoadCaseRequest request) {
return extTestPlanLoadCaseMapper.selectTestPlanLoadCaseList(request.getTestPlanId()); return extTestPlanLoadCaseMapper.selectTestPlanLoadCaseList(request.getTestPlanId(), request.getProjectId());
} }
public void relevanceCase(LoadCaseRequest request) { public void relevanceCase(LoadCaseRequest request) {
@ -96,4 +100,17 @@ public class TestPlanLoadCaseService {
} }
return true; return true;
} }
public void deleteByRelevanceProjectIds(String id, List<String> relevanceProjectIds) {
LoadTestExample loadTestExample = new LoadTestExample();
loadTestExample.createCriteria().andProjectIdIn(relevanceProjectIds);
List<LoadTest> loadTests = loadTestMapper.selectByExample(loadTestExample);
TestPlanLoadCaseExample testPlanLoadCaseExample = new TestPlanLoadCaseExample();
TestPlanLoadCaseExample.Criteria criteria = testPlanLoadCaseExample.createCriteria().andTestPlanIdEqualTo(id);
if (!CollectionUtils.isEmpty(loadTests)) {
List<String> ids = loadTests.stream().map(LoadTest::getId).collect(Collectors.toList());
criteria.andLoadCaseIdNotIn(ids);
}
testPlanLoadCaseMapper.deleteByExample(testPlanLoadCaseExample);
}
} }

View File

@ -87,6 +87,8 @@ public class TestPlanService {
private TestPlanApiCaseService testPlanApiCaseService; private TestPlanApiCaseService testPlanApiCaseService;
@Resource @Resource
private TestPlanScenarioCaseService testPlanScenarioCaseService; private TestPlanScenarioCaseService testPlanScenarioCaseService;
@Resource
private TestPlanLoadCaseService testPlanLoadCaseService;
public synchronized void addTestPlan(AddTestPlanRequest testPlan) { public synchronized void addTestPlan(AddTestPlanRequest testPlan) {
if (getTestPlanByName(testPlan.getName()).size() > 0) { if (getTestPlanByName(testPlan.getName()).size() > 0) {
@ -234,6 +236,7 @@ public class TestPlanService {
} }
testPlanApiCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds); testPlanApiCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
testPlanScenarioCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds); testPlanScenarioCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
testPlanLoadCaseService.deleteByRelevanceProjectIds(testPlan.getId(), relevanceProjectIds);
} }
} }

@ -1 +1 @@
Subproject commit 068127ce59ea8b016434ed52a9de4a7a4b13bdb4 Subproject commit 9f4a9bbf46fc1333dbcccea21f83e27e3ec10b1f

View File

@ -45,7 +45,8 @@
"vuedraggable": "^2.23.2", "vuedraggable": "^2.23.2",
"vuex": "^3.1.2", "vuex": "^3.1.2",
"xml-js": "^1.6.11", "xml-js": "^1.6.11",
"yan-progress": "^1.0.3" "yan-progress": "^1.0.3",
"vue-jsonpath-picker": "^1.1.5"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "^4.1.0", "@vue/cli-plugin-babel": "^4.1.0",

View File

@ -48,11 +48,16 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<api-json-path-suggest-button :open-tip="$t('api_test.request.assertions.json_path_suggest')"
:clear-tip="$t('api_test.request.assertions.json_path_clear')" @open="suggestJsonOpen" @clear="clearJson"/>
<ms-api-assertions-edit :is-read-only="isReadOnly" :assertions="assertions" :reloadData="reloadData" style="margin-bottom: 20px"/> <ms-api-assertions-edit :is-read-only="isReadOnly" :assertions="assertions" :reloadData="reloadData" style="margin-bottom: 20px"/>
</div> </div>
</el-collapse-transition> </el-collapse-transition>
</el-card> </el-card>
<ms-api-jsonpath-suggest :tip="$t('api_test.request.extract.suggest_tip')" @addSuggest="addJsonPathSuggest" :data="suggestData" ref="jsonpathSuggest"/>
</div> </div>
</template> </template>
@ -67,11 +72,15 @@
import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList"; import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList";
import MsApiAssertionXPath2 from "./ApiAssertionXPath2"; import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
import {getUUID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import ApiJsonPathSuggestButton from "./ApiJsonPathSuggestButton";
import MsApiJsonpathSuggest from "./ApiJsonpathSuggest";
export default { export default {
name: "MsApiAssertions", name: "MsApiAssertions",
components: { components: {
MsApiJsonpathSuggest,
ApiJsonPathSuggestButton,
MsApiAssertionXPath2, MsApiAssertionXPath2,
MsApiAssertionJsr223, MsApiAssertionJsr223,
MsApiJsonpathSuggestList, MsApiJsonpathSuggestList,
@ -83,6 +92,7 @@
assertions: {}, assertions: {},
node: {}, node: {},
request: {}, request: {},
response: {},
customizeStyle: { customizeStyle: {
type: String, type: String,
default: "margin-top: 10px" default: "margin-top: 10px"
@ -101,6 +111,7 @@
type: "", type: "",
loading: false, loading: false,
reloadData: "", reloadData: "",
suggestData: {}
} }
}, },
@ -114,11 +125,17 @@
this.$emit('copyRow', this.assertions, this.node); this.$emit('copyRow', this.assertions, this.node);
}, },
suggestJsonOpen() { 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')); this.$message(this.$t('api_test.request.assertions.debug_first'));
return; 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() { reload() {
this.loading = true this.loading = true
@ -133,14 +150,12 @@
remove() { remove() {
this.$emit('remove', this.assertions, this.node); this.$emit('remove', this.assertions, this.node);
}, },
addJsonpathSuggest(jsonPathList) { addJsonPathSuggest(data) {
jsonPathList.forEach(jsonPath => { let jsonItem = new JSONPath();
let jsonItem = new JSONPath(); jsonItem.expression = data.path;
jsonItem.expression = jsonPath.json_path; jsonItem.expect = data.value;
jsonItem.expect = jsonPath.json_value; jsonItem.setJSONPathDescription();
jsonItem.setJSONPathDescription(); this.assertions.jsonPath.push(jsonItem);
this.assertions.jsonPath.push(jsonItem);
});
}, },
clearJson() { clearJson() {
this.assertions.jsonPath = []; this.assertions.jsonPath = [];
@ -159,6 +174,7 @@
padding: 10px; padding: 10px;
margin: 5px 0; margin: 5px 0;
border-radius: 5px; border-radius: 5px;
border: #DCDFE6 solid 1px;
} }
.icon.is-active { .icon.is-active {
@ -168,4 +184,5 @@
/deep/ .el-card__body { /deep/ .el-card__body {
padding: 15px; padding: 15px;
} }
</style> </style>

View File

@ -0,0 +1,25 @@
<template>
<el-row :gutter="10" class="json-path-suggest-button">
<el-button size="small" type="primary" @click="$emit('open')">
{{openTip}}
</el-button>
<el-button size="small" type="danger" @click="$emit('clear')">
{{clearTip}}
</el-button>
</el-row>
</template>
<script>
export default {
name: "ApiJsonPathSuggestButton",
props: ['openTip', 'clearTip']
}
</script>
<style scoped>
.json-path-suggest-button {
text-align: right;
}
</style>

View File

@ -0,0 +1,93 @@
<template>
<ms-drawer class="json-path-picker" :visible="visible" :size="30" @close="close" direction="right">
<template v-slot:header>
<ms-instructions-icon :content="tip"/>
{{tip}}
</template>
<jsonpath-picker :code="data" v-on:path="pathChangeHandler" ref="jsonpathPicker"/>
</ms-drawer>
</template>
<script>
import MsDrawer from "../../../../common/components/MsDrawer";
import MsInstructionsIcon from "../../../../common/components/MsInstructionsIcon";
export default {
name: "MsApiJsonpathSuggest",
components: {MsInstructionsIcon, MsDrawer},
data() {
return {
visible: false,
isCheckAll: false,
};
},
props: {
data: {},
tip: {
type: String,
default() {
return ""
}
}
},
methods: {
close() {
this.visible = false;
},
open() {
this.visible = true;
},
pathChangeHandler(data) {
let paramNames = data.split('.');
let result = this.getParamValue(this.data, 0, paramNames);
result.path = '$.' + data;
this.$emit('addSuggest', result);
},
getParamValue(obj, index, params) {
if (params.length < 1) {
return "";
}
let param = params[index];
let childObj;
let reg = /\[\d\]$/;
let regIndex = param.search(reg);
if (regIndex > -1) {
let paramName = param.substring(0, regIndex);
let paramIndex = param.substring(regIndex + 1, param.length - 1);
param = paramIndex;
childObj = obj[paramName][paramIndex];
} else {
childObj = obj[params[index]];
}
if (index === params.length - 1) {
if (childObj instanceof Object) {
childObj = JSON.stringify(childObj);
} else if (childObj == null) {
childObj = "null";
}
return {
key: param,
value: childObj
};
}
index++;
return this.getParamValue(childObj, index, params);
}
}
}
</script>
<style scoped>
.json-path-picker {
padding: 10px 13px;
}
.json-path-picker >>> .json-tree {
margin-top: 0px;
margin-left: 6px;
}
</style>

View File

@ -24,26 +24,7 @@
<label class="ms-api-label" style="padding-left: 20px; padding-right: 20px;">{{ $t('commons.tag') }}</label> <label class="ms-api-label" style="padding-left: 20px; padding-right: 20px;">{{ $t('commons.tag') }}</label>
<el-tag <ms-input-tag :currentScenario="apiCase" ref="tag" style="float: right;margin-right: 215px;margin-top: -3px;"/>
:key="apiCase.id + '_' + index"
v-for="(tag, index) in apiCase.tags"
closable
size="mini"
:disable-transitions="false"
@close="handleClose(tag)">
{{ tag }}
</el-tag>
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
size="mini"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
>
</el-input>
<el-button v-else class="button-new-tag" size="mini" @click="showTagInput">+</el-button>
<div v-if="apiCase.id" style="color: #999999;font-size: 12px"> <div v-if="apiCase.id" style="color: #999999;font-size: 12px">
<span> <span>
@ -57,15 +38,15 @@
</div> </div>
</el-col> </el-col>
<el-col :span="4"> <el-col :span="4">
<ms-tip-button @click="singleRun(apiCase)" :tip="$t('api_test.run')" icon="el-icon-video-play" <ms-tip-button @click="singleRun(apiCase)" :tip="$t('api_test.run')" icon="el-icon-video-play"
style="background-color: #409EFF;color: white" size="mini" :disabled="!apiCase.id" circle v-tester/> style="background-color: #409EFF;color: white" size="mini" :disabled="!apiCase.id" circle v-tester/>
<ms-tip-button @click="copyCase(apiCase)" :tip="$t('commons.copy')" icon="el-icon-document-copy" <ms-tip-button @click="copyCase(apiCase)" :tip="$t('commons.copy')" icon="el-icon-document-copy"
size="mini" :disabled="!apiCase.id || isCaseEdit" circle v-tester/> size="mini" :disabled="!apiCase.id || isCaseEdit" circle v-tester/>
<ms-tip-button @click="deleteCase(index,apiCase)" :tip="$t('commons.delete')" icon="el-icon-delete" <ms-tip-button @click="deleteCase(index,apiCase)" :tip="$t('commons.delete')" icon="el-icon-delete"
size="mini" :disabled="!apiCase.id || isCaseEdit" circle v-tester/> size="mini" :disabled="!apiCase.id || isCaseEdit" circle v-tester/>
<ms-api-extend-btns :is-case-edit="isCaseEdit" :environment="environment" :row="apiCase" v-tester/> <ms-api-extend-btns :is-case-edit="isCaseEdit" :environment="environment" :row="apiCase" v-tester/>
</el-col> </el-col>
<el-col :span="3"> <el-col :span="3">
<el-link type="danger" v-if="apiCase.execResult && apiCase.execResult==='error'" @click="showExecResult(apiCase)"> <el-link type="danger" v-if="apiCase.execResult && apiCase.execResult==='error'" @click="showExecResult(apiCase)">
@ -112,10 +93,12 @@ import MsSqlBasisParameters from "../request/database/BasisParameters";
import MsTcpBasisParameters from "../request/tcp/BasisParameters"; import MsTcpBasisParameters from "../request/tcp/BasisParameters";
import MsDubboBasisParameters from "../request/dubbo/BasisParameters"; import MsDubboBasisParameters from "../request/dubbo/BasisParameters";
import MsApiExtendBtns from "../reference/ApiExtendBtns"; import MsApiExtendBtns from "../reference/ApiExtendBtns";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
export default { export default {
name: "ApiCaseItem", name: "ApiCaseItem",
components: { components: {
MsInputTag,
MsTag, MsTag,
MsTipButton, MsTipButton,
MsApiRequestForm, MsApiRequestForm,
@ -139,8 +122,6 @@ export default {
visible: false, visible: false,
condition: {}, condition: {},
isShowInput: false, isShowInput: false,
inputVisible: false,
inputValue: ''
} }
}, },
props: { props: {
@ -213,21 +194,25 @@ export default {
} }
}, },
saveTestCase(row) { saveTestCase(row) {
let tmp = JSON.parse(JSON.stringify(row));
this.isShowInput = false; this.isShowInput = false;
if (this.validate(row)) { if (this.validate(tmp)) {
return; return;
} }
let bodyFiles = this.getBodyUploadFiles(row); let bodyFiles = this.getBodyUploadFiles(tmp);
row.projectId = getCurrentProjectID(); tmp.projectId = getCurrentProjectID();
row.active = true; tmp.active = true;
row.request.path = this.api.path; tmp.request.path = this.api.path;
row.request.method = this.api.method; tmp.request.method = this.api.method;
row.apiDefinitionId = row.apiDefinitionId || this.api.id; tmp.apiDefinitionId = tmp.apiDefinitionId || this.api.id;
let url = "/api/testcase/create"; let url = "/api/testcase/create";
if (row.id) { if (tmp.id) {
url = "/api/testcase/update"; 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.$success(this.$t('commons.save_success'));
this.$emit('refresh'); this.$emit('refresh');
}); });
@ -293,28 +278,6 @@ export default {
} }
return bodyUploadFiles; 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 = '';
}
} }
} }
</script> </script>
@ -356,22 +319,4 @@ export default {
.is-selected { .is-selected {
background: #EFF7FF; 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;
}
</style> </style>

View File

@ -177,9 +177,7 @@ export default {
this.addCase(); this.addCase();
} }
this.apiCaseList.forEach(apiCase => { this.apiCaseList.forEach(apiCase => {
if (!apiCase.tags) { if (apiCase.tags && apiCase.tags.length > 0) {
apiCase.tags = [];
} else {
apiCase.tags = JSON.parse(apiCase.tags); apiCase.tags = JSON.parse(apiCase.tags);
} }
}) })

View File

@ -17,8 +17,9 @@
<div v-else> <div v-else>
<el-option :key="0" :value="''"> <el-option :key="0" :value="''">
<div style="margin-left: 40px"> <div style="margin-left: 40px">
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{$t('api_test.definition.select_comp.no_data')}}, <span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},
</span><el-link type="primary" @click="createModules">{{$t('api_test.definition.select_comp.add_data')}}</el-link> </span>
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>
</div> </div>
</el-option> </el-option>
</div> </div>
@ -54,27 +55,7 @@
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item :label="$t('commons.tag')" prop="tag"> <el-form-item :label="$t('commons.tag')" prop="tag">
<el-tag <ms-input-tag :currentScenario="basicForm" ref="tag"/>
:key="basicForm + '_' + index"
v-for="(tag, index) in basicForm.tags"
closable
size="mini"
:disable-transitions="false"
@close="handleClose(tag)">
{{ tag }}
</el-tag>
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
size="mini"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
>
</el-input>
<el-button v-else class="button-new-tag" size="mini" @click="showInput">+</el-button>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16">
@ -94,111 +75,69 @@
<script> <script>
import {API_STATUS} from "../../model/JsonData"; import {API_STATUS} from "../../model/JsonData";
import {WORKSPACE_ID} from '../../../../../../common/js/constants'; import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
export default { export default {
name: "MsBasisApi", name: "MsBasisApi",
components: {}, components: {MsInputTag},
props: { props: {
currentProtocol: { currentProtocol: {
type: String, type: String,
default: "HTTP" default: "HTTP"
},
moduleOptions: Array,
basisData: {},
}, },
created() { moduleOptions: Array,
this.getMaintainerOptions(); basisData: {},
if (!this.basisData.tags) { },
this.basisData.tags = []; created() {
} else { this.getMaintainerOptions();
this.basisData.tags = JSON.parse(this.basisData.tags); this.basicForm = this.basisData;
} },
this.basicForm = this.basisData; data() {
}, return {
data() { basicForm: {},
return { httpVisible: false,
basicForm: {}, currentModule: {},
httpVisible: false, maintainerOptions: [],
currentModule: {}, loading: false,
maintainerOptions: [], rule: {
loading: false, name: [
rule: { {required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
name: [ {max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'}, ],
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'} userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
], moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}], status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
},
value: API_STATUS[0].id,
options: API_STATUS,
inputVisible: false,
inputValue: ''
}
},
methods: {
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.maintainerOptions = response.data;
});
}, },
reload() { value: API_STATUS[0].id,
this.loading = true options: API_STATUS,
this.$nextTick(() => {
this.loading = false
})
},
validate() {
this.$refs['basicForm'].validate((valid) => {
if (valid) {
this.$emit('callback');
}
})
},
createModules(){
this.$emit("createRootModelInTree");
},
handleClose(tag) {
this.basicForm.tags.splice(this.basicForm.tags.indexOf(tag), 1);
},
showInput() {
this.inputVisible = true;
this.$nextTick(_ => {
this.$refs.saveTagInput.$refs.input.focus();
});
},
handleInputConfirm() {
let inputValue = this.inputValue;
if (inputValue) {
this.basicForm.tags.push(inputValue);
}
this.inputVisible = false;
this.inputValue = '';
}
} }
},
methods: {
getMaintainerOptions() {
let workspaceId = localStorage.getItem(WORKSPACE_ID);
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
this.maintainerOptions = response.data;
});
},
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
validate() {
this.$refs['basicForm'].validate((valid) => {
if (valid) {
this.$emit('callback');
}
})
},
createModules() {
this.$emit("createRootModelInTree");
},
} }
}
</script> </script>
<style scoped> <style scoped>
.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;
}
</style> </style>

View File

@ -6,87 +6,91 @@
<el-col> <el-col>
<!--操作按钮--> <!--操作按钮-->
<div style="float: right;margin-right: 20px;margin-top: 20px"> <div style="float: right;margin-right: 20px;margin-top: 20px">
<el-button type="primary" size="small" @click="saveApi">{{$t('commons.save')}}</el-button> <el-button type="primary" size="small" @click="saveApi">{{ $t('commons.save') }}</el-button>
<el-button type="primary" size="small" @click="runTest">{{$t('commons.test')}}</el-button> <el-button type="primary" size="small" @click="runTest">{{ $t('commons.test') }}</el-button>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<!-- 基础信息 --> <!-- 基础信息 -->
<p class="tip">{{$t('test_track.plan_view.base_info')}} </p> <p class="tip">{{ $t('test_track.plan_view.base_info') }} </p>
<br/> <br/>
<el-row> <el-row>
<el-col> <el-col>
<ms-basis-api @createRootModelInTree="createRootModelInTree" :moduleOptions="moduleOptions" :basisData="basisData" ref="basicForm" @callback="callback"/> <ms-basis-api @createRootModelInTree="createRootModelInTree" :moduleOptions="moduleOptions" :basisData="basisData" ref="basicForm"
@callback="callback"/>
</el-col> </el-col>
</el-row> </el-row>
<!-- 请求参数 --> <!-- 请求参数 -->
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p> <p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<ms-basis-parameters :showScript="false" :request="request"/> <ms-basis-parameters :showScript="false" :request="request"/>
</div> </div>
</template> </template>
<script> <script>
import MsBasisApi from "./BasisApi"; import MsBasisApi from "./BasisApi";
import MsBasisParameters from "../request/dubbo/BasisParameters"; import MsBasisParameters from "../request/dubbo/BasisParameters";
export default { export default {
name: "MsApiDubboRequestForm", name: "MsApiDubboRequestForm",
components: { components: {
MsBasisApi, MsBasisParameters MsBasisApi, MsBasisParameters
},
props: {
request: {},
basisData: {},
moduleOptions: Array,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {validated: false}
},
methods: {
callback() {
this.validated = true;
}, },
props: { validateApi() {
request: {}, this.validated = false;
basisData: {}, this.basisData.method = this.request.protocol;
moduleOptions: Array, this.$refs['basicForm'].validate();
isReadOnly: { },
type: Boolean, saveApi() {
default: false this.validateApi();
if (this.validated) {
this.basisData.request = this.request;
console.log(this.basisData)
if (this.basisData.tags instanceof Array) {
this.basisData.tags = JSON.stringify(this.basisData.tags);
}
this.$emit('saveApi', this.basisData);
} }
}, },
data() { runTest() {
return {validated: false} this.validateApi();
if (this.validated) {
this.basisData.request = this.request;
this.$emit('runTest', this.basisData);
}
}, },
methods: { createRootModelInTree() {
callback() { this.$emit("createRootModelInTree");
this.validated = true;
},
validateApi() {
this.validated = false;
this.basisData.method = this.request.protocol;
this.$refs['basicForm'].validate();
},
saveApi() {
this.validateApi();
if (this.validated) {
this.basisData.request = this.request;
console.log(this.basisData)
this.$emit('saveApi', this.basisData);
}
},
runTest() {
this.validateApi();
if (this.validated) {
this.basisData.request = this.request;
this.$emit('runTest', this.basisData);
}
},
createRootModelInTree(){
this.$emit("createRootModelInTree");
},
}, },
},
computed: {} computed: {}
} }
</script> </script>
<style scoped> <style scoped>
.tip { .tip {
padding: 3px 5px; padding: 3px 5px;
font-size: 16px; font-size: 16px;
border-radius: 4px; border-radius: 4px;
border-left: 4px solid #783887; border-left: 4px solid #783887;
margin: 0px 20px 0px; margin: 0px 20px 0px;
} }
</style> </style>

View File

@ -77,27 +77,7 @@
<el-row> <el-row>
<el-col :span="8"> <el-col :span="8">
<el-form-item :label="$t('commons.tag')" prop="tag"> <el-form-item :label="$t('commons.tag')" prop="tag">
<el-tag <ms-input-tag :currentScenario="httpForm" ref="tag"/>
:key="httpForm + '_' + index"
v-for="(tag, index) in httpForm.tags"
closable
size="mini"
:disable-transitions="false"
@close="handleClose(tag)">
{{ tag }}
</el-tag>
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
size="mini"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
>
</el-input>
<el-button v-else class="button-new-tag" size="mini" @click="showInput">+</el-button>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16">
@ -134,10 +114,11 @@ import {WORKSPACE_ID} from '../../../../../../common/js/constants';
import {API_STATUS, REQ_METHOD} from "../../model/JsonData"; import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
import MsJsr233Processor from "../processor/Jsr233Processor"; import MsJsr233Processor from "../processor/Jsr233Processor";
import {KeyValue} from "../../model/ApiTestModel"; import {KeyValue} from "../../model/ApiTestModel";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
export default { export default {
name: "MsAddCompleteHttpApi", name: "MsAddCompleteHttpApi",
components: {MsResponseText, MsApiRequestForm, MsJsr233Processor}, components: {MsResponseText, MsApiRequestForm, MsJsr233Processor, MsInputTag},
data() { data() {
let validateURL = (rule, value, callback) => { let validateURL = (rule, value, callback) => {
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) { if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
@ -165,8 +146,6 @@ export default {
currentModule: {}, currentModule: {},
reqOptions: REQ_METHOD, reqOptions: REQ_METHOD,
options: API_STATUS, options: API_STATUS,
inputVisible: false,
inputValue: ''
} }
}, },
props: {moduleOptions: {}, request: {}, response: {}, basisData: {}}, props: {moduleOptions: {}, request: {}, response: {}, basisData: {}},
@ -192,6 +171,9 @@ export default {
this.request.path = this.httpForm.path; this.request.path = this.httpForm.path;
this.request.method = this.httpForm.method; this.request.method = this.httpForm.method;
this.httpForm.request.useEnvironment = undefined; this.httpForm.request.useEnvironment = undefined;
if (this.httpForm.tags instanceof Array) {
this.httpForm.tags = JSON.stringify(this.httpForm.tags);
}
}, },
saveApi() { saveApi() {
this.$refs['httpForm'].validate((valid) => { this.$refs['httpForm'].validate((valid) => {
@ -243,26 +225,6 @@ export default {
this.$error(this.$t('api_test.request.url_invalid'), 2000); 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() { created() {
@ -270,11 +232,6 @@ export default {
if (!this.basisData.environmentId) { if (!this.basisData.environmentId) {
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)); this.httpForm = JSON.parse(JSON.stringify(this.basisData));
} }
@ -314,22 +271,4 @@ export default {
.ms-left-buttion { .ms-left-buttion {
margin: 6px 0px 8px 30px; 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;
}
</style> </style>

View File

@ -5,84 +5,88 @@
<el-col> <el-col>
<!--操作按钮--> <!--操作按钮-->
<div style="float: right;margin-right: 20px;margin-top: 20px"> <div style="float: right;margin-right: 20px;margin-top: 20px">
<el-button type="primary" size="small" @click="saveApi">{{$t('commons.save')}}</el-button> <el-button type="primary" size="small" @click="saveApi">{{ $t('commons.save') }}</el-button>
<el-button type="primary" size="small" @click="runTest">{{$t('commons.test')}}</el-button> <el-button type="primary" size="small" @click="runTest">{{ $t('commons.test') }}</el-button>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<!-- 基础信息 --> <!-- 基础信息 -->
<p class="tip">{{$t('test_track.plan_view.base_info')}} </p> <p class="tip">{{ $t('test_track.plan_view.base_info') }} </p>
<br/> <br/>
<el-row> <el-row>
<el-col> <el-col>
<ms-basis-api @createRootModelInTree="createRootModelInTree" :moduleOptions="moduleOptions" :basisData="basisData" ref="basicForm" @callback="callback"/> <ms-basis-api @createRootModelInTree="createRootModelInTree" :moduleOptions="moduleOptions" :basisData="basisData" ref="basicForm"
@callback="callback"/>
</el-col> </el-col>
</el-row> </el-row>
<!-- 请求参数 --> <!-- 请求参数 -->
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p> <p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<ms-basis-parameters :showScript="false" :request="request"/> <ms-basis-parameters :showScript="false" :request="request"/>
</div> </div>
</template> </template>
<script> <script>
import MsBasisApi from "./BasisApi"; import MsBasisApi from "./BasisApi";
import MsBasisParameters from "../request/database/BasisParameters"; import MsBasisParameters from "../request/database/BasisParameters";
export default { export default {
name: "MsApiSqlRequestForm", name: "MsApiSqlRequestForm",
components: { components: {
MsBasisApi, MsBasisParameters MsBasisApi, MsBasisParameters
},
props: {
request: {},
basisData: {},
moduleOptions: Array,
isReadOnly: {
type: Boolean,
default: false
}
},
data() {
return {validated: false}
},
methods: {
callback() {
this.validated = true;
}, },
props: { validateApi() {
request: {}, this.validated = false;
basisData: {}, this.$refs['basicForm'].validate();
moduleOptions: Array, },
isReadOnly: { saveApi() {
type: Boolean, this.validateApi();
default: false if (this.validated) {
this.basisData.method = this.basisData.protocol;
if (this.basisData.tags instanceof Array) {
this.basisData.tags = JSON.stringify(this.basisData.tags);
}
this.$emit('saveApi', this.basisData);
} }
}, },
runTest() {
data() { this.validateApi();
return {validated: false} if (this.validated) {
this.basisData.request = this.request;
this.$emit('runTest', this.basisData);
}
}, },
methods: { createRootModelInTree() {
callback() { this.$emit("createRootModelInTree");
this.validated = true;
},
validateApi() {
this.validated = false;
this.$refs['basicForm'].validate();
},
saveApi() {
this.validateApi();
if (this.validated) {
this.basisData.method = this.basisData.protocol;
this.$emit('saveApi', this.basisData);
}
},
runTest() {
this.validateApi();
if (this.validated) {
this.basisData.request = this.request;
this.$emit('runTest', this.basisData);
}
},
createRootModelInTree(){
this.$emit("createRootModelInTree");
},
}, },
} },
}
</script> </script>
<style scoped> <style scoped>
.tip { .tip {
padding: 3px 5px; padding: 3px 5px;
font-size: 16px; font-size: 16px;
border-radius: 4px; border-radius: 4px;
border-left: 4px solid #783887; border-left: 4px solid #783887;
margin: 0px 20px 0px; margin: 0px 20px 0px;
} }
</style> </style>

View File

@ -5,22 +5,23 @@
<el-col> <el-col>
<!--操作按钮--> <!--操作按钮-->
<div style="float: right;margin-right: 20px;margin-top: 20px"> <div style="float: right;margin-right: 20px;margin-top: 20px">
<el-button type="primary" size="small" @click="saveApi">{{$t('commons.save')}}</el-button> <el-button type="primary" size="small" @click="saveApi">{{ $t('commons.save') }}</el-button>
<el-button type="primary" size="small" @click="runTest">{{$t('commons.test')}}</el-button> <el-button type="primary" size="small" @click="runTest">{{ $t('commons.test') }}</el-button>
</div> </div>
</el-col> </el-col>
</el-row> </el-row>
<!-- 基础信息 --> <!-- 基础信息 -->
<p class="tip">{{$t('test_track.plan_view.base_info')}} </p> <p class="tip">{{ $t('test_track.plan_view.base_info') }} </p>
<br/> <br/>
<el-row> <el-row>
<el-col> <el-col>
<ms-basis-api @createRootModelInTree="createRootModelInTree" :moduleOptions="moduleOptions" :basisData="basisData" ref="basicForm" @callback="callback"/> <ms-basis-api @createRootModelInTree="createRootModelInTree" :moduleOptions="moduleOptions" :basisData="basisData" ref="basicForm"
@callback="callback"/>
</el-col> </el-col>
</el-row> </el-row>
<!-- 请求参数 --> <!-- 请求参数 -->
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p> <p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<ms-basis-parameters :request="request"/> <ms-basis-parameters :request="request"/>
</div> </div>
@ -28,62 +29,65 @@
</template> </template>
<script> <script>
import MsBasisApi from "./BasisApi"; import MsBasisApi from "./BasisApi";
import MsBasisParameters from "../request/tcp/BasisParameters"; import MsBasisParameters from "../request/tcp/BasisParameters";
export default { export default {
name: "MsAddCompleteTcpApi", name: "MsAddCompleteTcpApi",
components: {MsBasisApi, MsBasisParameters}, components: {MsBasisApi, MsBasisParameters},
props: { props: {
request: {}, request: {},
basisData: {}, basisData: {},
moduleOptions: Array, moduleOptions: Array,
isReadOnly: { isReadOnly: {
type: Boolean, type: Boolean,
default: false default: false
}
},
data() {
return {
validated: false,
}
},
methods: {
callback() {
this.validated = true;
},
validateApi() {
this.validated = false;
this.basisData.method = "TCP";
this.$refs['basicForm'].validate();
},
saveApi() {
this.validateApi();
if (this.validated) {
if (this.basisData.tags instanceof Array) {
this.basisData.tags = JSON.stringify(this.basisData.tags);
}
this.$emit('saveApi', this.basisData);
} }
}, },
data() { runTest() {
return { this.validateApi();
validated: false, if (this.validated) {
this.basisData.request = this.request;
this.$emit('runTest', this.basisData);
} }
}, },
createRootModelInTree() {
methods: { this.$emit("createRootModelInTree");
callback() {
this.validated = true;
},
validateApi() {
this.validated = false;
this.basisData.method = "TCP";
this.$refs['basicForm'].validate();
},
saveApi() {
this.validateApi();
if (this.validated) {
this.$emit('saveApi', this.basisData);
}
},
runTest() {
this.validateApi();
if (this.validated) {
this.basisData.request = this.request;
this.$emit('runTest', this.basisData);
}
},
createRootModelInTree(){
this.$emit("createRootModelInTree");
},
}, },
} },
}
</script> </script>
<style scoped> <style scoped>
.tip { .tip {
padding: 3px 5px; padding: 3px 5px;
font-size: 16px; font-size: 16px;
border-radius: 4px; border-radius: 4px;
border-left: 4px solid #783887; border-left: 4px solid #783887;
margin: 0px 20px 0px; margin: 0px 20px 0px;
} }
</style> </style>

View File

@ -26,7 +26,7 @@
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p> <p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
<!-- HTTP 请求参数 --> <!-- HTTP 请求参数 -->
<ms-api-request-form :headers="request.headers" :request="request"/> <ms-api-request-form :headers="request.headers" :request="request" :response="responseData"/>
</el-form> </el-form>
<!-- HTTP 请求返回数据 --> <!-- HTTP 请求返回数据 -->

View File

@ -12,6 +12,7 @@
<el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" style="margin-left: 10px"/> <el-button size="mini" icon="el-icon-copy-document" circle @click="copyRow" style="margin-left: 10px"/>
<el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove" style="margin-left: 10px"/> <el-button size="mini" icon="el-icon-delete" type="danger" circle @click="remove" style="margin-left: 10px"/>
</div> </div>
<!-- 请求参数--> <!-- 请求参数-->
<el-collapse-transition> <el-collapse-transition>
<div v-if="extract.active"> <div v-if="extract.active">
@ -36,10 +37,17 @@
<el-button v-if="!type" :disabled="true" type="primary" size="small">Add</el-button> <el-button v-if="!type" :disabled="true" type="primary" size="small">Add</el-button>
</el-row> </el-row>
</div> </div>
<api-json-path-suggest-button :open-tip="$t('api_test.request.extract.json_path_suggest')"
:clear-tip="$t('api_test.request.extract.json_path_clear')" @open="suggestJsonOpen" @clear="clearJson"/>
<ms-api-extract-edit :is-read-only="isReadOnly" :reloadData="reloadData" :extract="extract"/> <ms-api-extract-edit :is-read-only="isReadOnly" :reloadData="reloadData" :extract="extract"/>
</div> </div>
</div> </div>
</el-collapse-transition> </el-collapse-transition>
<ms-api-jsonpath-suggest :tip="$t('api_test.request.extract.suggest_tip')" @addSuggest="addJsonPathSuggest" :data="suggestData" ref="jsonpathSuggest"/>
</el-card> </el-card>
</div> </div>
</template> </template>
@ -49,17 +57,23 @@
import MsApiExtractEdit from "./ApiExtractEdit"; import MsApiExtractEdit from "./ApiExtractEdit";
import MsApiExtractCommon from "./ApiExtractCommon"; import MsApiExtractCommon from "./ApiExtractCommon";
import {getUUID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton";
import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest";
import {ExtractJSONPath} from "../../../test/model/ScenarioModel";
export default { export default {
name: "MsApiExtract", name: "MsApiExtract",
components: { components: {
MsApiJsonpathSuggest,
ApiJsonPathSuggestButton,
MsApiExtractCommon, MsApiExtractCommon,
MsApiExtractEdit, MsApiExtractEdit,
}, },
props: { props: {
extract: {}, extract: {},
response: {},
node: {}, node: {},
customizeStyle: { customizeStyle: {
type: String, type: String,
@ -77,6 +91,7 @@
type: "", type: "",
reloadData: "", reloadData: "",
loading: false, loading: false,
suggestData: {}
} }
}, },
@ -101,6 +116,28 @@
item.active = !item.active; item.active = !item.active;
this.reload(); 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: { computed: {
list() { list() {

View File

@ -10,6 +10,8 @@
<el-table v-loading="result.loading" <el-table v-loading="result.loading"
ref="apiDefinitionTable" ref="apiDefinitionTable"
border border
@sort-change="sort"
@filter-change="filter"
:data="tableData" row-key="id" class="test-content adjust-table ms-select-all" :data="tableData" row-key="id" class="test-content adjust-table ms-select-all"
@select-all="handleSelectAll" @select-all="handleSelectAll"
@select="handleSelect" :height="screenHeight"> @select="handleSelect" :height="screenHeight">
@ -27,18 +29,21 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="num" label="ID" show-overflow-tooltip/> <el-table-column prop="num" label="ID" show-overflow-tooltip
<el-table-column prop="name" :label="$t('api_test.definition.api_name')" show-overflow-tooltip/> sortable="custom"/>
<el-table-column prop="name" :label="$t('api_test.definition.api_name')"
show-overflow-tooltip
sortable="custom"/>
<el-table-column <el-table-column
prop="status" prop="status"
column-key="api_status" column-key="status"
:label="$t('api_test.definition.api_status')" sortable="custom"
show-overflow-tooltip> :filters="statusFilters"
:label="$t('api_test.definition.api_status')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-tag v-if="scope.row.status == 'Prepare'" type="info" effect="plain" :content="$t('test_track.plan.plan_status_prepare')"/> <span class="el-dropdown-link">
<ms-tag v-if="scope.row.status == 'Underway'" type="warning" effect="plain" :content="$t('test_track.plan.plan_status_running')"/> <api-status :value="scope.row.status"/>
<ms-tag v-if="scope.row.status == 'Completed'" type="success" effect="plain" :content="$t('test_track.plan.plan_status_completed')"/> </span>
<ms-tag v-if="scope.row.status == 'Trash'" type="danger" effect="plain" content="废弃"/>
</template> </template>
</el-table-column> </el-table-column>
@ -66,10 +71,9 @@
<el-table-column prop="tags" :label="$t('commons.tag')"> <el-table-column prop="tags" :label="$t('commons.tag')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-tag v-for="(tag, index) in scope.row.showTags" <div v-for="(itemName,index) in scope.row.tags" :key="index">
:key="tag + '_' + index" <ms-tag type="success" effect="plain" :content="itemName"/>
:effect="'light'" </div>
:content="tag"/>
</template> </template>
</el-table-column> </el-table-column>
@ -128,14 +132,16 @@ import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn"; import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit"; import MsBatchEdit from "../basis/BatchEdit";
import {API_METHOD_COLOUR, API_STATUS, REQ_METHOD} from "../../model/JsonData"; 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 {WORKSPACE_ID} from '@/common/js/constants';
import ApiListContainer from "./ApiListContainer"; import ApiListContainer from "./ApiListContainer";
import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll"; import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll";
import ApiStatus from "@/business/components/api/definition/components/list/ApiStatus";
export default { export default {
name: "ApiList", name: "ApiList",
components: { components: {
ApiStatus,
MsTableSelectAll, MsTableSelectAll,
ApiListContainer, ApiListContainer,
MsTableButton, MsTableButton,
@ -168,6 +174,12 @@ export default {
{id: 'method', name: this.$t('api_test.definition.api_type')}, {id: 'method', name: this.$t('api_test.definition.api_type')},
{id: 'userId', name: this.$t('api_test.definition.api_principal')}, {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: { valueArr: {
status: API_STATUS, status: API_STATUS,
method: REQ_METHOD, method: REQ_METHOD,
@ -208,6 +220,7 @@ export default {
}, },
}, },
created: function () { created: function () {
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
this.initTable(); this.initTable();
this.getMaintainerOptions(); this.getMaintainerOptions();
}, },
@ -220,8 +233,12 @@ export default {
}, },
trashEnable() { trashEnable() {
if (this.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: { methods: {
@ -235,14 +252,8 @@ export default {
this.unSelection = []; this.unSelection = [];
this.selectDataCounts = 0; this.selectDataCounts = 0;
this.condition.filters = ["Prepare", "Underway", "Completed"];
this.condition.moduleIds = this.selectNodeIds; this.condition.moduleIds = this.selectNodeIds;
if (this.trashEnable) {
this.condition.filters = ["Trash"];
this.condition.moduleIds = [];
}
this.condition.projectId = getCurrentProjectID(); this.condition.projectId = getCurrentProjectID();
if (this.currentProtocol != null) { if (this.currentProtocol != null) {
this.condition.protocol = this.currentProtocol; this.condition.protocol = this.currentProtocol;
@ -256,15 +267,6 @@ export default {
case 'thisWeekCount': case 'thisWeekCount':
this.condition.selectThisWeedData = true; this.condition.selectThisWeedData = true;
break; 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': case 'uncoverage':
this.condition.apiCaseCoverage = 'uncoverage'; this.condition.apiCaseCoverage = 'uncoverage';
break; break;
@ -278,8 +280,10 @@ export default {
this.tableData = response.data.listObject; this.tableData = response.data.listObject;
this.unSelection = response.data.listObject.map(s => s.id); this.unSelection = response.data.listObject.map(s => s.id);
this.tableData.forEach(row => { this.tableData.forEach(item => {
row.showTags = JSON.parse(row.tags); if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
}) })
}); });
} }
@ -342,9 +346,13 @@ export default {
this.$emit('editApi', row); this.$emit('editApi', row);
}, },
reductionApi(row) { reductionApi(row) {
row.request = null; let tmp = JSON.parse(JSON.stringify(row));
row.response = null; tmp.request = null;
let rows = [row]; tmp.response = null;
if (tmp.tags instanceof Array) {
tmp.tags = JSON.stringify(tmp.tags);
}
let rows = [tmp];
this.$post('/api/definition/reduction/', rows, () => { this.$post('/api/definition/reduction/', rows, () => {
this.$success(this.$t('commons.save_success')); this.$success(this.$t('commons.save_success'));
this.search(); this.search();
@ -495,7 +503,19 @@ export default {
let rowArray = Array.from(rowSets) let rowArray = Array.from(rowSets)
let ids = rowArray.map(s => s.id); let ids = rowArray.map(s => s.id);
return ids; 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();
},
}, },
} }
</script> </script>

View File

@ -0,0 +1,26 @@
<template>
<span>
<ms-tag v-if="value === 'Prepare'" type="info" effect="plain" :content="$t('test_track.plan.plan_status_prepare')"/>
<ms-tag v-if="value === 'Underway'" type="warning" effect="plain" :content="$t('test_track.plan.plan_status_running')"/>
<ms-tag v-if="value === 'Completed'" type="success" effect="plain" :content="$t('test_track.plan.plan_status_completed')"/>
<ms-tag v-if="value === 'Trash'" type="danger" effect="plain" content="废弃"/>
</span>
</template>
<script>
import MsTag from "@/business/components/common/components/MsTag";
export default {
name: "ApiStatus",
components: {MsTag},
props: {
value: {
type: String
}
}
}
</script>
<style scoped>
</style>

View File

@ -76,12 +76,10 @@
<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3" <ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"
:jsr223-processor="row"/> :jsr223-processor="row"/>
<!--断言规则--> <!--断言规则-->
<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/> <ms-api-assertions :response="response" v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>
<!--提取规则--> <!--提取规则-->
<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/> <ms-api-extract :response="response" :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>
</div> </div>
</div> </div>
</el-col> </el-col>
<!--操作按钮--> <!--操作按钮-->
@ -131,6 +129,7 @@
}, },
props: { props: {
request: {}, request: {},
response: {},
showScript: Boolean, showScript: Boolean,
headers: { headers: {
type: Array, type: Array,

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="request-form"> <div class="request-form">
<component :is="component" :showScript="showScript" :is-read-only="isReadOnly" :referenced="referenced" :request="request" :headers="headers" :isShowEnable="isShowEnable"/> <component :is="component" :showScript="showScript" :is-read-only="isReadOnly" :referenced="referenced" :request="request" :response="response" :headers="headers" :isShowEnable="isShowEnable"/>
</div> </div>
</template> </template>
@ -12,6 +12,7 @@
components: {MsApiHttpRequestForm}, components: {MsApiHttpRequestForm},
props: { props: {
request: {}, request: {},
response: {},
headers: Array, headers: Array,
isShowEnable: { isShowEnable: {
type: Boolean, type: Boolean,

View File

@ -77,7 +77,7 @@ import {Request, Scenario} from "../model/ScenarioModel";
import draggable from 'vuedraggable'; import draggable from 'vuedraggable';
import MsApiScenarioSelect from "@/business/components/api/test/components/ApiScenarioSelect"; import MsApiScenarioSelect from "@/business/components/api/test/components/ApiScenarioSelect";
import {parseEnvironment} from "../model/EnvironmentModel"; import {parseEnvironment} from "../model/EnvironmentModel";
import MsHorizontalDragBar from "../../../common/components/MsHorizontalDragBar"; import MsHorizontalDragBar from "../../../common/components/dragbar/MsLeft2RightDragBar";
export default { export default {
name: "MsApiScenarioConfig", name: "MsApiScenarioConfig",

View File

@ -11,7 +11,7 @@
</template> </template>
<script> <script>
import MsHorizontalDragBar from "./MsHorizontalDragBar"; import MsHorizontalDragBar from "./dragbar/MsLeft2RightDragBar";
export default { export default {
name: "MsAsideContainer", name: "MsAsideContainer",
components: {MsHorizontalDragBar}, components: {MsHorizontalDragBar},

View File

@ -1,90 +0,0 @@
<template>
<div direction="vertical" :class="direction" @mousedown="mouseDown"></div>
</template>
<script>
export default {
name: "MsDragMoveBar",
data() {
return {
lastX: '',
lastY: '',
};
},
props: {
direction: {
type: String,
default() {
return 'vertical';
}
}
},
created() {
document.addEventListener("mouseup", this.mouseUp);
},
destroyed() {
document.removeEventListener("mouseup", this.mouseUp);
},
methods: {
mouseDown(event) {
document.addEventListener("mousemove", this.mouseMove);
this.lastX = event.screenX;
this.lastY = event.screenY;
},
mouseMove(event) {
this.$emit("widthChange", this.lastX - event.screenX);
this.$emit("heightChange", this.lastY - event.screenY);
this.lastX = event.screenX;
this.lastY = event.screenY;
},
mouseUp() {
this.lastX = "";
this.lastY = "";
document.removeEventListener("mousemove", this.mouseMove);
}
}
};
</script>
<style >
.drag-bar {
width: 100%;
height: 2px;
cursor: row-resize;
z-index: 10;
background: #ccc;
}
.horizontal {
width: 2px;
height: 100%;
cursor: col-resize;
z-index: 10;
}
.vertical {
width: 100%;
height: 2px;
cursor: row-resize;
z-index: 10;
}
.vertical:hover {
height: 3px;
background-color: #ccc;
/*-webkit-box-shadow: 0 8px 10px -5px rgba(0,0,0,.2), 0 16px 24px 2px rgba(0,0,0,.14), 0 6px 30px 5px rgba(0,0,0,.12);*/
/*box-shadow: 0 8px 10px -5px rgba(0,0,0,.2), 0 16px 24px 2px rgba(0,0,0,.14), 0 6px 30px 5px rgba(0,0,0,.12);*/
}
.horizontal:hover {
width: 3px;
/*background-color: #7C3985;*/
background-color: #ccc;
}
</style>

View File

@ -1,21 +1,29 @@
<template> <template>
<div id="ms-drawer" class="ms-drawer" :class="directionStyle" :style="{width: w + 'px', height: h + 'px'}" ref="msDrawer"> <div v-if="visible" id="ms-drawer" class="ms-drawer" :class="directionStyle" :style="{width: w + 'px', height: h + 'px'}" ref="msDrawer">
<ms-drag-move-bar :direction="dragBarDirection" @widthChange="widthChange" @heightChange="heightChange"/>
<div class="ms-drawer-header" > <ms-bottom2-top-drag-bar v-if="direction == 'bottom'"/>
<ms-right2-left-drag-bar v-if="direction == 'right'"/>
<div class="ms-drawer-header">
<slot name="header"></slot> <slot name="header"></slot>
<i class="el-icon-close" @click="close"/> <i class="el-icon-close" @click="close"/>
</div> </div>
<div class="ms-drawer-body"> <div class="ms-drawer-body">
<slot></slot> <slot></slot>
</div> </div>
<ms-left2-right-drag-bar v-if="direction == 'left'"/>
</div> </div>
</template> </template>
<script> <script>
import MsDragMoveBar from "./MsDragMoveBar"; import MsRight2LeftDragBar from "./dragbar/MsRight2LeftDragBar";
import MsLeft2RightDragBar from "./dragbar/MsLeft2RightDragBar";
import MsBottom2TopDragBar from "./dragbar/MsBottom2TopDragBar";
export default { export default {
name: "MsDrawer", name: "MsDrawer",
components: {MsDragMoveBar}, components: {MsBottom2TopDragBar, MsLeft2RightDragBar, MsRight2LeftDragBar},
data() { data() {
return { return {
x: 0, x: 0,
@ -33,6 +41,12 @@
return "left"; return "left";
} }
}, },
visible: {
type: Boolean,
default() {
return true;
}
},
size: { size: {
type: Number, type: Number,
default() { default() {
@ -81,58 +95,12 @@
break; break;
} }
}, },
resize() {
},
getWidthPercentage(per) { getWidthPercentage(per) {
return document.body.clientWidth * per / 100.0; return document.body.clientWidth * per / 100.0;
}, },
getHeightPercentage(per) { getHeightPercentage(per) {
return document.body.clientHeight * per / 100.0; return document.body.clientHeight * per / 100.0;
}, },
widthChange(movement) {
if (this.direction != 'left' && this.direction != 'right') {
return;
}
switch (this.direction) {
case 'top':
this.w -= movement;
break;
case 'bottom':
this.w += movement;
break;
}
this._widthChange();
},
heightChange(movement) {
if (this.direction != 'top' && this.direction != 'bottom') {
return;
}
switch (this.direction) {
case 'top':
this.h -= movement;
break;
case 'bottom':
this.h += movement;
break;
}
this._heightChange();
},
_heightChange() {
if (this.h < 0) {
this.h = 0;
}
if (this.h > document.body.clientHeight) {
this.h = document.body.clientHeight;
}
},
_widthChange() {
if (this.w < 0) {
this.w = 0;
}
if (this.w > document.body.clientWidth) {
this.w = document.body.clientWidth;
}
},
close() { close() {
this.$emit('close') this.$emit('close')
} }
@ -178,17 +146,31 @@
} }
.ms-drawer-header { .ms-drawer-header {
position: fixed;
width: 100%;
z-index: 999; z-index: 999;
width: 100%;
}
.bottom-style .ms-drawer-header {
position: fixed;
} }
.el-icon-close { .el-icon-close {
position: absolute; position: absolute;
font-size: 20px;
right: 10px;
top: 10px;
color: gray;
}
.bottom-style .el-icon-close {
right: 10px; right: 10px;
top: 13px; top: 13px;
color: gray; }
font-size: 20px;
.right-style .el-icon-close {
position: fixed;
right: 10px;
top: 10px;
} }
.el-icon-close:hover { .el-icon-close:hover {

View File

@ -0,0 +1,27 @@
<template>
<div class="drag-bar" v-bottom-to-top-drag/>
</template>
<script>
export default {
name: "MsBottom2TopDragBar"
}
</script>
<style scoped>
.drag-bar {
height: 1px;
width: 100%;
z-index: 9999;
cursor: row-resize;
position: fixed;
background-color: #E6E6E6;
border: 0px;
}
.drag-bar:hover {
height: 3px;
}
</style>

View File

@ -1,10 +1,10 @@
<template> <template>
<div class="drag-bar" v-horizontal-drag/> <div class="drag-bar" v-left-to-right-drag/>
</template> </template>
<script> <script>
export default { export default {
name: "MsHorizontalDragBar" name: "MsLeft2RightDragBar"
} }
</script> </script>

View File

@ -0,0 +1,28 @@
<template>
<div class="drag-bar" v-right-to-left-drag/>
</template>
<script>
export default {
name: "MsRight2LeftDragBar"
}
</script>
<style scoped>
.drag-bar {
height: 100%;
width: 1px;
position: absolute;
left: 0px;
top: 0;
cursor: col-resize;
background-color: #E6E6E6;
border: 0px;
}
.drag-bar:hover {
width: 3px;
}
</style>

View File

@ -69,27 +69,7 @@
<el-row> <el-row>
<el-col :span="10" :offset="1"> <el-col :span="10" :offset="1">
<el-form-item :label="$t('commons.tag')" :label-width="formLabelWidth" prop="tag"> <el-form-item :label="$t('commons.tag')" :label-width="formLabelWidth" prop="tag">
<el-tag <ms-input-tag :currentScenario="form" ref="tag"/>
:key="form + '_' + index"
v-for="(tag, index) in form.caseTags"
closable
size="mini"
:disable-transitions="false"
@close="handleClose(tag)">
{{ tag }}
</el-tag>
<el-input
class="input-new-tag"
v-if="inputVisible"
v-model="inputValue"
ref="saveTagInput"
size="mini"
@keyup.enter.native="handleInputConfirm"
@blur="handleInputConfirm"
>
</el-input>
<el-button v-else class="button-new-tag" size="mini" @click="showInput">+</el-button>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -298,10 +278,11 @@ import TestCaseAttachment from "@/business/components/track/case/components/Test
import {getCurrentProjectID} from "../../../../../common/js/utils"; import {getCurrentProjectID} from "../../../../../common/js/utils";
import {buildNodePath} from "../../../api/definition/model/NodeTree"; import {buildNodePath} from "../../../api/definition/model/NodeTree";
import CaseComment from "@/business/components/track/case/components/CaseComment"; import CaseComment from "@/business/components/track/case/components/CaseComment";
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
export default { export default {
name: "TestCaseEdit", name: "TestCaseEdit",
components: {CaseComment, MsDialogFooter, TestCaseAttachment}, components: {MsInputTag, CaseComment, MsDialogFooter, TestCaseAttachment},
data() { data() {
return { return {
result: {}, result: {},
@ -323,7 +304,6 @@ export default {
result: '' result: ''
}], }],
remark: '', remark: '',
caseTags: []
}, },
moduleOptions: [], moduleOptions: [],
maintainerOptions: [], maintainerOptions: [],
@ -355,8 +335,6 @@ export default {
{value: 'manual', label: this.$t('test_track.case.manual')} {value: 'manual', label: this.$t('test_track.case.manual')}
], ],
testCase: {}, testCase: {},
inputVisible: false,
inputValue: ''
}; };
}, },
props: { props: {
@ -387,7 +365,7 @@ export default {
open(testCase) { open(testCase) {
this.testCase = {}; this.testCase = {};
if (testCase) { if (testCase) {
testCase.caseTags = JSON.parse(testCase.tags); testCase.tags = JSON.parse(testCase.tags);
// //
this.testCase = testCase.isCopy ? {} : testCase; this.testCase = testCase.isCopy ? {} : testCase;
} }
@ -425,7 +403,6 @@ export default {
this.form.type = 'functional'; this.form.type = 'functional';
this.form.method = 'manual'; this.form.method = 'manual';
this.form.maintainer = user.id; this.form.maintainer = user.id;
this.form.caseTags = [];
} }
this.getSelectOptions(); this.getSelectOptions();
@ -538,6 +515,10 @@ export default {
if (param.method != 'auto') { if (param.method != 'auto') {
param.testId = null; param.testId = null;
} }
if (this.form.tags instanceof Array) {
this.form.tags = JSON.stringify(this.form.tags);
}
param.tags = this.form.tags;
return param; return param;
}, },
getOption(param) { getOption(param) {
@ -641,7 +622,6 @@ export default {
desc: '', desc: '',
result: '' result: ''
}]; }];
this.caseTags = [];
this.uploadList = []; this.uploadList = [];
this.fileList = []; this.fileList = [];
this.tableData = []; this.tableData = [];
@ -729,26 +709,6 @@ export default {
/// todo: /// todo:
return file.size > 0; 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 = '';
}
} }
} }
</script> </script>
@ -788,21 +748,4 @@ export default {
height: calc(100vh - 120px); 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;
}
</style> </style>

View File

@ -113,10 +113,9 @@
<el-table-column prop="tags" :label="$t('commons.tag')"> <el-table-column prop="tags" :label="$t('commons.tag')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-tag v-for="(tag, index) in scope.row.showTags" <div v-for="(itemName,index) in scope.row.tags" :key="index">
:key="tag + '_' + index" <ms-tag type="success" effect="plain" :content="itemName"/>
:effect="'light'" </div>
:content="tag"/>
</template> </template>
</el-table-column> </el-table-column>
@ -319,8 +318,10 @@ export default {
this.tableData = data.listObject; this.tableData = data.listObject;
// this.selectIds.clear(); // this.selectIds.clear();
this.selectRows.clear(); this.selectRows.clear();
this.tableData.forEach(row => { this.tableData.forEach(item => {
row.showTags = JSON.parse(row.tags); if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
}) })
}); });
} }

View File

@ -62,10 +62,9 @@
<el-table-column prop="tags" :label="$t('commons.tag')"> <el-table-column prop="tags" :label="$t('commons.tag')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<ms-tag v-for="(tag, index) in scope.row.showTags" <div v-for="(itemName,index) in scope.row.tags" :key="index">
:key="tag + '_' + index" <ms-tag type="success" effect="plain" :content="itemName"/>
:effect="'light'" </div>
:content="tag"/>
</template> </template>
</el-table-column> </el-table-column>
@ -265,8 +264,10 @@ export default {
this.result = this.$post('/test/plan/api/case/list/' + this.currentPage + "/" + this.pageSize, this.condition, response => { this.result = this.$post('/test/plan/api/case/list/' + this.currentPage + "/" + this.pageSize, this.condition, response => {
this.total = response.data.itemCount; this.total = response.data.itemCount;
this.tableData = response.data.listObject; this.tableData = response.data.listObject;
this.tableData.forEach(row => { this.tableData.forEach(item => {
row.showTags = JSON.parse(row.tags); if (item.tags && item.tags.length > 0) {
item.tags = JSON.parse(item.tags);
}
}) })
}); });
}, },
@ -479,7 +480,4 @@ export default {
margin-right: 20px; margin-right: 20px;
} }
.el-tag {
margin-left: 10px;
}
</style> </style>

View File

@ -12,7 +12,7 @@
class="table-list" class="table-list"
@refresh="refresh" @refresh="refresh"
:plan-id="planId" :plan-id="planId"
:select-node-ids="selectNodeIds" :select-project-id="selectProjectId"
:select-parent-nodes="selectParentNodes" :select-parent-nodes="selectParentNodes"
@relevanceCase="openTestCaseRelevanceDialog" @relevanceCase="openTestCaseRelevanceDialog"
ref="testPlanLoadCaseList"/> ref="testPlanLoadCaseList"/>
@ -46,6 +46,7 @@ export default {
result: {}, result: {},
selectNodeIds: [], selectNodeIds: [],
selectParentNodes: [], selectParentNodes: [],
selectProjectId: "",
treeNodes: [], treeNodes: [],
} }
}, },
@ -74,8 +75,7 @@ export default {
this.$refs.testCaseLoadRelevance.open(); this.$refs.testCaseLoadRelevance.open();
}, },
nodeChange(node, nodeIds, pNodes) { nodeChange(node, nodeIds, pNodes) {
this.selectNodeIds = nodeIds; this.selectProjectId = node.key;
this.selectParentNodes = pNodes;
// node // node
this.$refs.testPlanLoadCaseList.currentPage = 1; this.$refs.testPlanLoadCaseList.currentPage = 1;
this.$refs.testPlanLoadCaseList.pageSize = 10; this.$refs.testPlanLoadCaseList.pageSize = 10;
@ -84,6 +84,8 @@ export default {
if (this.planId) { if (this.planId) {
this.result = this.$get("/case/node/list/plan/" + this.planId, response => { this.result = this.$get("/case/node/list/plan/" + this.planId, response => {
this.treeNodes = response.data; this.treeNodes = response.data;
//
this.treeNodes.map(node => node.children = null);
}); });
} }
}, },

View File

@ -30,12 +30,11 @@
:label="$t('commons.name')" :label="$t('commons.name')"
show-overflow-tooltip> show-overflow-tooltip>
</el-table-column> </el-table-column>
<!-- <el-table-column--> <el-table-column
<!-- prop="projectName"--> prop="projectName"
<!-- :label="$t('load_test.project_name')"--> :label="$t('load_test.project_name')"
<!-- width="150"--> show-overflow-tooltip>
<!-- show-overflow-tooltip>--> </el-table-column>
<!-- </el-table-column>-->
<el-table-column <el-table-column
prop="userName" prop="userName"
:label="$t('load_test.user_name')" :label="$t('load_test.user_name')"
@ -148,7 +147,7 @@ export default {
} }
}, },
props: { props: {
selectNodeIds: Array, selectProjectId: String,
isReadOnly: { isReadOnly: {
type: Boolean, type: Boolean,
default: false default: false
@ -159,7 +158,7 @@ export default {
this.initTable(); this.initTable();
}, },
watch: { watch: {
selectNodeIds() { selectProjectId() {
this.initTable(); this.initTable();
}, },
planId() { planId() {
@ -168,7 +167,13 @@ export default {
}, },
methods: { methods: {
initTable() { initTable() {
this.$post("/test/plan/load/case/list/" + this.currentPage + "/" + this.pageSize, {testPlanId: this.planId}, response => { 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; let data = response.data;
this.total = data.itemCount; this.total = data.itemCount;
this.tableData = data.listObject; this.tableData = data.listObject;

@ -1 +1 @@
Subproject commit 7d43154a7c19732407a8e9ace8a7d1ea13c91f36 Subproject commit 010ad7a5f072a5e9d368c756a2473bbd20781433

View File

@ -18,8 +18,10 @@ import '../common/css/menu-header.css';
import '../common/css/main.css'; import '../common/css/main.css';
import CKEditor from '@ckeditor/ckeditor5-vue'; import CKEditor from '@ckeditor/ckeditor5-vue';
import VueFab from 'vue-float-action-button' 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 JsonSchemaEditor from './components/common/json-schema/schema/index';
import JSONPathPicker from 'vue-jsonpath-picker';
Vue.use(JsonSchemaEditor); Vue.use(JsonSchemaEditor);
import VuePapaParse from 'vue-papa-parse' import VuePapaParse from 'vue-papa-parse'
Vue.use(VuePapaParse) Vue.use(VuePapaParse)
@ -37,6 +39,7 @@ Vue.use(message);
Vue.use(CKEditor); Vue.use(CKEditor);
Vue.use(YanProgress); Vue.use(YanProgress);
Vue.use(VueFab); Vue.use(VueFab);
Vue.use(JSONPathPicker);
// v-permission // v-permission
Vue.directive('permission', permission); Vue.directive('permission', permission);
@ -49,7 +52,9 @@ Vue.directive('xpack', xpack);
Vue.directive('tester', tester); 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({ new Vue({
el: '#app', el: '#app',

View File

@ -161,7 +161,7 @@ html,body {
overflow: visible; overflow: visible;
} }
.ms-select-all .el-icon-arrow-down { .ms-select-all th:nth-child(2) .el-icon-arrow-down {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
top: -7px; top: -7px;

View File

@ -1,4 +1,4 @@
export const horizontalDrag = { export const left2RightDrag = {
inserted(el, binding) { inserted(el, binding) {
el.onmousedown = function (e) { el.onmousedown = function (e) {
const init = e.clientX; const init = e.clientX;
@ -7,7 +7,49 @@ export const horizontalDrag = {
document.onmousemove = function (e) { document.onmousemove = function (e) {
const end = e.clientX; const end = e.clientX;
const newWidth = end - init + initWidth; 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.onmouseup = function () {
document.onmousemove = document.onmouseup = null; document.onmousemove = document.onmouseup = null;

View File

@ -732,6 +732,7 @@ export default {
json_path_suggest: "JSONPath Assertion Suggest", json_path_suggest: "JSONPath Assertion Suggest",
json_path_clear: "Clear JSONPath Assertion", json_path_clear: "Clear JSONPath Assertion",
debug_first: "First, debug to get the response", debug_first: "First, debug to get the response",
suggest_tip: "Click the note to add the JSONPath assertion",
}, },
extract: { extract: {
label: "Extract from response", label: "Extract from response",
@ -742,6 +743,9 @@ export default {
regex_expression: "Regular expression", regex_expression: "Regular expression",
json_path_expression: "JSONPath expression", json_path_expression: "JSONPath expression",
xpath_expression: "XPath 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: { processor: {
pre_exec_script: "PreProcessor", pre_exec_script: "PreProcessor",
@ -1085,6 +1089,7 @@ export default {
plan_status_prepare: "Not started", plan_status_prepare: "Not started",
plan_status_running: "Starting", plan_status_running: "Starting",
plan_status_completed: "Completed", plan_status_completed: "Completed",
plan_status_trash: "Trashed",
planned_start_time: "Scheduled Start Time", planned_start_time: "Scheduled Start Time",
planned_end_time: "Scheduled End Time", planned_end_time: "Scheduled End Time",
actual_start_time: "Actual Start Time", actual_start_time: "Actual Start Time",

View File

@ -732,6 +732,7 @@ export default {
variable_name: "变量名称", variable_name: "变量名称",
set_failure_status: "设置失败状态", set_failure_status: "设置失败状态",
set_failure_msg: "设置失败消息", set_failure_msg: "设置失败消息",
suggest_tip: "点击便签添加JSONPath断言",
}, },
extract: { extract: {
label: "提取", label: "提取",
@ -742,6 +743,9 @@ export default {
regex_expression: "Perl型正则表达式", regex_expression: "Perl型正则表达式",
json_path_expression: "JSONPath表达式", json_path_expression: "JSONPath表达式",
xpath_expression: "XPath表达式", xpath_expression: "XPath表达式",
suggest_tip: "点击便签添加JSONPath提取",
json_path_suggest: "推荐JSONPath提取",
json_path_clear: "清空JSONPath提取",
}, },
processor: { processor: {
pre_exec_script: "预执行脚本", pre_exec_script: "预执行脚本",
@ -1086,6 +1090,7 @@ export default {
plan_status_prepare: "未开始", plan_status_prepare: "未开始",
plan_status_running: "进行中", plan_status_running: "进行中",
plan_status_completed: "已完成", plan_status_completed: "已完成",
plan_status_trash: "废弃",
planned_start_time: "计划开始", planned_start_time: "计划开始",
planned_end_time: "计划结束", planned_end_time: "计划结束",
actual_start_time: "实际开始", actual_start_time: "实际开始",

View File

@ -65,7 +65,7 @@ export default {
refresh: '刷新', refresh: '刷新',
remark: '備註', remark: '備註',
delete: '刪除', delete: '刪除',
reduction: '恢', reduction: '恢',
not_filled: '未填寫', not_filled: '未填寫',
please_select: '請選擇', please_select: '請選擇',
search_by_name: '根據名稱搜索', search_by_name: '根據名稱搜索',
@ -250,9 +250,9 @@ export default {
mail: '郵件', mail: '郵件',
nail_robot: '釘釘機器人', nail_robot: '釘釘機器人',
enterprise_wechat_robot: '企業微信機器人', enterprise_wechat_robot: '企業微信機器人',
notes: '1.事件,接收方式,接收人為必填項;\n' + notes: '1.釘釘和企業群裏新建壹個自定義機器人,然後復制 webhook 地址在我們平臺上;\n' +
' 2.接收方式除郵件外webhook為必填\n' + ' 2.機器人選擇為群機器人,安全驗證選擇“自定義關鍵詞” "任務通知";\n' +
' 3.機器人選擇為群機器人,安全驗證選擇“自定義關鍵詞” "任務通知"', ' 3.選擇接收人時必須是妳所建的群裏包含的人,接收人手機號為必填項且為釘釘企業所使用的手機號,',
message: '事件,接收人,接收方式為必填項', message: '事件,接收人,接收方式為必填項',
message_webhook: '接收方式為釘釘和企業機器人時webhook為必填項', message_webhook: '接收方式為釘釘和企業機器人時webhook為必填項',
template: "模版" template: "模版"
@ -307,7 +307,7 @@ export default {
manager: '項目管理', manager: '項目管理',
no_data: '無數據', no_data: '無數據',
select: '選擇項目', select: '選擇項目',
repeatable: '接口定义URL可重复' repeatable: '接口定義URL可重復'
}, },
member: { member: {
create: '添加成員', create: '添加成員',
@ -377,9 +377,9 @@ export default {
test_stop_now: '立即停止', test_stop_now: '立即停止',
test_stop_now_confirm: '確定要立即停止當前測試嗎?', test_stop_now_confirm: '確定要立即停止當前測試嗎?',
test_rerun_confirm: '確定要再次執行當前測試嗎?', test_rerun_confirm: '確定要再次執行當前測試嗎?',
downloadJtl: '下載JTL',
test_stop_success: '停止成功', test_stop_success: '停止成功',
test_execute_again: '再次執行', test_execute_again: '再次執行',
downloadJtl: '下載JTL',
export: '導出', export: '導出',
compare: '比較', compare: '比較',
generation_error: '報告生成錯誤, 無法查看, 請檢查日誌詳情!', generation_error: '報告生成錯誤, 無法查看, 請檢查日誌詳情!',
@ -436,9 +436,9 @@ export default {
ramp_up_time_within: '在', ramp_up_time_within: '在',
ramp_up_time_minutes: '秒內,分', ramp_up_time_minutes: '秒內,分',
ramp_up_time_seconds: '秒內增加並發用戶', ramp_up_time_seconds: '秒內增加並發用戶',
iterate_num: '代次數 (次): ', iterate_num: '代次數 (次): ',
by_iteration: '按代次數', by_iteration: '按代次數',
by_duration: '按壓測時長', by_duration: '按持續時間',
ramp_up_time_times: '次增加並發用戶', ramp_up_time_times: '次增加並發用戶',
advanced_config_error: '高級配置校驗失敗', advanced_config_error: '高級配置校驗失敗',
domain_bind: '域名綁定', domain_bind: '域名綁定',
@ -514,18 +514,18 @@ export default {
add_data: "去添加" add_data: "去添加"
}, },
request: { request: {
grade_info: "按等級筛选", grade_info: "按等級篩選",
run_env: "運行環境", run_env: "運行環境",
select_case: "搜索用例", select_case: "搜索用例",
case: "用例", case: "用例",
responsible: "任人", responsible: "任人",
title: "建接口", title: "建接口",
path_info: "請輸入接口的URL如/api/demo/#{id}其中id為路徑參數", path_info: "請輸入接口的URL如/api/demo/#{id}其中id為路徑參數",
path_all_info: "請輸入完整測試地址", path_all_info: "請輸入完整測試地址",
fast_debug: "快捷調試", fast_debug: "快捷調試",
close_all_label: "關閉所有標簽", close_all_label: "關閉所有標簽",
save_as: "另存為新接口", save_as: "另存為新接口",
load_case: "加用例", load_case: "加用例",
save_as_case: "另存為新用例", save_as_case: "另存為新用例",
update_api: "更新接口", update_api: "更新接口",
body_form_data: "form-data", body_form_data: "form-data",
@ -541,10 +541,10 @@ export default {
verified: "認證", verified: "認證",
encryption: "加密", encryption: "加密",
req_param: "請求參數", req_param: "請求參數",
res_param: "響應容", res_param: "響應容",
batch_delete: "批量除", batch_delete: "批量除",
delete_confirm: "確認刪除接口", delete_confirm: "確認刪除接口",
delete_confirm_step: "確認刪除步", delete_confirm_step: "確認刪除步",
assertions_rule: "斷言規則", assertions_rule: "斷言規則",
response_header: "響應頭", response_header: "響應頭",
response_body: "響應體", response_body: "響應體",
@ -557,12 +557,12 @@ export default {
post_script: "後置腳本", post_script: "後置腳本",
extract_param: "提取參數", extract_param: "提取參數",
add_module: "創建模塊", add_module: "創建模塊",
edit_api: "编辑接口", edit_api: "編輯接口",
test_plan_select: "請選擇測試計劃", test_plan_select: "請選擇測試計劃",
create_info: '創建', create_info: '創建',
update_info: '更新', update_info: '更新',
batch_edit: "批量編輯", batch_edit: "批量編輯",
path_valid_info: "請求路径无效", path_valid_info: "請求路徑無效",
} }
}, },
automation: { automation: {
@ -575,15 +575,15 @@ export default {
external_import: "外部導入", external_import: "外部導入",
wait_controller: "等待控制器", wait_controller: "等待控制器",
if_controller: "條件控制器", if_controller: "條件控制器",
loop_controller: "循控制器", loop_controller: "循控制器",
scenario_import: "場景導入", scenario_import: "場景導入",
customize_script: "自定義本", customize_script: "自定義本",
customize_req: "自定義請求", customize_req: "自定義請求",
reference_info: "請選擇接口或用例", reference_info: "請選擇接口或用例",
scenario_test: "場景", scenario_test: "場景",
add_scenario: "建場景", add_scenario: "建場景",
scenario_name: "場景名", scenario_name: "場景名",
case_level: "用例等", case_level: "用例等",
tag: "標簽", tag: "標簽",
creator: "創建人", creator: "創建人",
update_time: "最後更新時間", update_time: "最後更新時間",
@ -597,14 +597,14 @@ export default {
edit: "編輯", edit: "編輯",
execute: "執行", execute: "執行",
copy: "復制", copy: "復制",
remove: "除", remove: "除",
view_ref: "查看引用", view_ref: "查看引用",
case_ref: "用例引用", case_ref: "用例引用",
schedule: "定時任務", schedule: "定時任務",
scenario_ref: "景引用", scenario_ref: "景引用",
plan_ref: "测试计划引用", plan_ref: "測試計劃引用",
batch_add_plan: "添加到测试计划", batch_add_plan: "添加到測試計劃",
batch_execute: "批量行", batch_execute: "批量行",
scenario: { scenario: {
principal: "責任人", principal: "責任人",
select_principal: "請選擇責任人", select_principal: "請選擇責任人",
@ -614,7 +614,7 @@ export default {
}, },
report_name_info: '請輸入報告名稱', report_name_info: '請輸入報告名稱',
save_case_info: '請先保存用例', save_case_info: '請先保存用例',
reference_deleted: '引用已除', reference_deleted: '引用已除',
}, },
environment: { environment: {
name: "環境名稱", name: "環境名稱",
@ -707,6 +707,7 @@ export default {
text: "文本", text: "文本",
regex: "正則", regex: "正則",
response_time: "響應時間", response_time: "響應時間",
jsr223: "腳本",
select_type: "請選擇類型", select_type: "請選擇類型",
select_subject: "請選擇對象", select_subject: "請選擇對象",
select_condition: "請選擇條件", select_condition: "請選擇條件",
@ -731,6 +732,7 @@ export default {
variable_name: "變量名稱", variable_name: "變量名稱",
set_failure_status: "設置失敗狀態", set_failure_status: "設置失敗狀態",
set_failure_msg: "設置失敗消息", set_failure_msg: "設置失敗消息",
suggest_tip: "點擊便簽添加JSONPath斷言",
}, },
extract: { extract: {
label: "提取", label: "提取",
@ -741,6 +743,9 @@ export default {
regex_expression: "Perl型正則表達式", regex_expression: "Perl型正則表達式",
json_path_expression: "JSONPath表達式", json_path_expression: "JSONPath表達式",
xpath_expression: "XPath表達式", xpath_expression: "XPath表達式",
suggest_tip: "點擊便簽添加JSONPath提取",
json_path_suggest: "推薦JSONPath提取",
json_path_clear: "清空JSONPath提取",
}, },
processor: { processor: {
pre_exec_script: "預執行腳本", pre_exec_script: "預執行腳本",
@ -791,7 +796,7 @@ export default {
close_connection: "關閉連接", close_connection: "關閉連接",
so_linger: "SO LINGER", so_linger: "SO LINGER",
eol_byte: "行尾(EOL)字節值", eol_byte: "行尾(EOL)字節值",
request: "發送文本", request: "發送文本",
username: "用戶名", username: "用戶名",
password: "密碼", password: "密碼",
login: "登錄設置", login: "登錄設置",
@ -808,7 +813,7 @@ export default {
ms_tip: "支持 Metersphere json 格式", ms_tip: "支持 Metersphere json 格式",
ms_export_tip: "通過 Metersphere 接口測試頁面或者瀏覽器插件導出 json 格式文件", ms_export_tip: "通過 Metersphere 接口測試頁面或者瀏覽器插件導出 json 格式文件",
postman_tip: "只支持 Postman Collection v2.1 格式的 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 導出測試集合", post_export_tip: "通過 Postman 導出測試集合",
swagger_export_tip: "通過 Swagger 頁面導出", swagger_export_tip: "通過 Swagger 頁面導出",
suffixFormatErr: "文件格式不符合要求", suffixFormatErr: "文件格式不符合要求",
@ -850,42 +855,42 @@ export default {
}, },
api_details_card: { api_details_card: {
title: "接口", title: "接口",
this_week_add: "本新增:", this_week_add: "本新增:",
}, },
test_case_details_card: { test_case_details_card: {
title: "用例", title: "用例",
this_week_add: "本新增:", this_week_add: "本新增:",
this_week_execute: "本執行: {0}次", this_week_execute: "本執行: {0}次",
executed: "歷史總執行: {0}次", executed: "歷史總執行: {0}次",
this_week_add_sm: "本新增:", this_week_add_sm: "本新增:",
this_week_execute_sm: "本執行:<br/>{0}次", this_week_execute_sm: "本執行:<br/>{0}次",
executed_sm: "歷史總執行:<br/>{0}次", executed_sm: "歷史總執行:<br/>{0}次",
}, },
test_scene_details_card: { test_scene_details_card: {
title: "場景", title: "場景",
this_week_add: "本新增:", this_week_add: "本新增:",
this_week_execute: "本執行: {0}次", this_week_execute: "本執行: {0}次",
executed: "歷史總執行: {0}次", executed: "歷史總執行: {0}次",
this_week_add_sm: "本新增:", this_week_add_sm: "本新增:",
this_week_execute_sm: "本執行:<br/>{0}次", this_week_execute_sm: "本執行:<br/>{0}次",
executed_sm: "歷史總執行:<br/>{0}次", executed_sm: "歷史總執行:<br/>{0}次",
}, },
schedule_task_details_card: { schedule_task_details_card: {
title: "定時任務", title: "定時任務",
this_week_add: "本週新增: {0}个", this_week_add: "本周新增: {0}個",
this_week_execute: "本執行: {0}次", this_week_execute: "本執行: {0}次",
executed: "歷史總執行: {0}次", executed: "歷史總執行: {0}次",
this_week_add_sm: "本週新增:<br/>{0}个", this_week_add_sm: "本周新增:<br/>{0}個",
this_week_execute_sm: "本執行:<br/>{0}次", this_week_execute_sm: "本執行:<br/>{0}次",
executed_sm: "歷史總執行:<br/>{0}次", executed_sm: "歷史總執行:<br/>{0}次",
}, },
failed_case_list: { failed_case_list: {
title: "過去7天測試計失敗用例TOP 10", title: "過去7天測試計失敗用例TOP 10",
table_coloum: { table_coloum: {
index: "排名", index: "排名",
case_name: "用例名稱", case_name: "用例名稱",
case_type: "用例類型", case_type: "用例類型",
test_plan: "所屬測試計", test_plan: "所屬測試計",
failure_times: "失敗次數", failure_times: "失敗次數",
}, },
table_value: { table_value: {
@ -949,11 +954,11 @@ export default {
not_exist: "測試報告不存在", not_exist: "測試報告不存在",
}, },
api_monitor: { api_monitor: {
to: "", to: "",
start_time: "開始時間", start_time: "開始日期",
end_time: "結束時間", end_time: "結束日期",
today: "今", today: "今",
this_week: "本", this_week: "本",
this_mouth: "本月", this_mouth: "本月",
please_search: "請搜索", please_search: "請搜索",
date: "日期" date: "日期"
@ -1085,6 +1090,7 @@ export default {
plan_status_prepare: "未開始", plan_status_prepare: "未開始",
plan_status_running: "進行中", plan_status_running: "進行中",
plan_status_completed: "已完成", plan_status_completed: "已完成",
plan_status_trash: "廢棄",
planned_start_time: "計劃開始", planned_start_time: "計劃開始",
planned_end_time: "計劃結束", planned_end_time: "計劃結束",
actual_start_time: "實際開始", actual_start_time: "實際開始",
@ -1124,8 +1130,8 @@ export default {
send: "發送", send: "發送",
description_is_null: "評論內容不能為空!", description_is_null: "評論內容不能為空!",
send_success: "評論成功!", send_success: "評論成功!",
cannot_edit: "無法編輯此評論!", cannot_edit: "無法編輯此評論",
cannot_delete: "無法刪除此評論!", cannot_delete: "無法刪除此評論",
}, },
review_view: { review_view: {
review: "評審", review: "評審",
@ -1363,7 +1369,7 @@ export default {
performance: "性能測試數量", performance: "性能測試數量",
resource_pool: "可用測試資源池", resource_pool: "可用測試資源池",
max_threads: "最大並發數", max_threads: "最大並發數",
duration: "壓測時長(分鐘", duration: "壓測時長(",
use_default: "使用默認配額", use_default: "使用默認配額",
yes: "是", yes: "是",
no: "否", no: "否",
@ -1376,55 +1382,54 @@ export default {
clean: "清空" clean: "清空"
}, },
schema: { schema: {
title: "标题", title: "標題",
import_json: "入 json", import_json: "入 json",
base_setting: "基础设置", base_setting: "基礎設置",
all_setting: "编辑源码", all_setting: "編輯源碼",
default: "默值", default: "默值",
description: "描述", description: "描述",
adv_setting: "高级设置", adv_setting: "高級設置",
add_child_node: "添加子节点", add_child_node: "添加子節點",
add_sibling_node: "添加兄弟节点", add_sibling_node: "添加兄弟節點",
add_node: "添加兄弟/子节点", add_node: "添加兄弟/子節點",
remove_node: "删除节点", remove_node: "刪除節點",
child_node: "子节点", child_node: "子節點",
sibling_node: "兄弟节点", sibling_node: "兄弟節點",
ok: "定", ok: "定",
cancel: "取消", cancel: "取消",
minLength: "最小度", minLength: "最小度",
maxLength: "最大度", maxLength: "最大度",
pattern: "用正则表达式约束字符串", pattern: "用正則表達式約束字符串",
exclusiveMinimum: "开启后,数据必须大于最小值", exclusiveMinimum: "開啟後,數據必須大於最小值",
exclusiveMaximum: "开启后,数据必须小于最大值", exclusiveMaximum: "開啟後,數據必須小於最大值",
minimum: "最小值", minimum: "最小值",
maximum: "最大值", maximum: "最大值",
uniqueItems: "开启后,每个元素都不相同", uniqueItems: "開啟後,每個元素都不相同",
minItems: "最小元素个数", minItems: "最小元素個數",
maxItems: "最大元素个数", maxItems: "最大元素個數",
minProperties: "最小元素个数", minProperties: "最小元素個數",
maxProperties: "最大元素个数", maxProperties: "最大元素個數",
checked_all: "全", checked_all: "全",
valid_json: "不是合法的json字符串", valid_json: "不是合法的json字符串",
enum: "枚", enum: "枚",
enum_msg: "每行只能写一个值", enum_msg: "每行只能寫壹個值",
enum_desc: "备注", enum_desc: "備註",
enum_desc_msg: "备注描述信息", enum_desc_msg: "備註描述信息",
required: "是否必", required: "是否必",
mock: "mock", mock: "mock",
mockLink: "查看文", mockLink: "查看文",
format: "格式化", format: "格式化",
nothing: "", nothing: "",
preview: "预览", preview: "預覽",
add_custom: "添加自定义属性" add_custom: "添加自定義屬性"
}, },
loop: { loop: {
loops_title: "次數循環", loops_title: "次數循環",
foreach: "ForEach 循環", foreach: "ForEach 循環",
while: "While 循環", while: "While 循環",
loops: "循環次", loops: "循環次",
interval: "循環間隔", interval: "循環間隔",
proceed: "成功後繼續循環", proceed: "成功後繼續循環",
timeout: "循環超時時間", timeout: "循環超時時間",
} }
}; };