refactor: 测试用例关联测试重构

This commit is contained in:
chenjianxing 2021-08-10 15:52:58 +08:00 committed by jianxing
parent 1a8a7b1692
commit fbf36241da
21 changed files with 1204 additions and 181 deletions

View File

@ -2457,4 +2457,13 @@ public class ApiAutomationService {
result.setCheckMsg(checkMsgList);
return result;
}
public List<ApiScenario> getScenarioCaseByIds(List<String> ids) {
if (CollectionUtils.isNotEmpty(ids)) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(ids);
return apiScenarioMapper.selectByExample(example);
}
return new ArrayList<>();
}
}

View File

@ -955,4 +955,12 @@ public class ApiTestCaseService {
return jmxInfoDTO;
}
public List<ApiTestCase> getApiCaseByIds(List<String> apiCaseIds) {
if (CollectionUtils.isNotEmpty(apiCaseIds)) {
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andIdIn(apiCaseIds);
return apiTestCaseMapper.selectByExample(example);
}
return new ArrayList<>();
}
}

View File

@ -1,8 +1,15 @@
package io.metersphere.base.mapper;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.base.domain.TestCaseTest;
import io.metersphere.base.domain.TestCaseTestExample;
import java.util.List;
import io.metersphere.dto.LoadTestDTO;
import io.metersphere.track.request.testplan.LoadCaseRequest;
import org.apache.ibatis.annotations.Param;
public interface TestCaseTestMapper {
@ -19,4 +26,10 @@ public interface TestCaseTestMapper {
int updateByExampleSelective(@Param("record") TestCaseTest record, @Param("example") TestCaseTestExample example);
int updateByExample(@Param("record") TestCaseTest record, @Param("example") TestCaseTestExample example);
}
List<ApiTestCaseDTO> relevanceApiList(@Param("request") ApiTestCaseRequest request);
List<ApiScenarioDTO> relevanceScenarioList(@Param("request") ApiScenarioRequest request);
List<LoadTestDTO> relevanceLoadList(@Param("request") LoadCaseRequest request);
}

View File

@ -90,9 +90,9 @@
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.TestCaseTest">
insert into test_case_test (test_case_id, test_id, test_type,
insert into test_case_test (test_case_id, test_id, test_type,
create_time, update_time)
values (#{testCaseId,jdbcType=VARCHAR}, #{testId,jdbcType=VARCHAR}, #{testType,jdbcType=VARCHAR},
values (#{testCaseId,jdbcType=VARCHAR}, #{testId,jdbcType=VARCHAR}, #{testType,jdbcType=VARCHAR},
#{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestCaseTest">
@ -138,6 +138,89 @@
<include refid="Example_Where_Clause" />
</if>
</select>
<select id="relevanceApiList" resultType="io.metersphere.api.dto.definition.ApiTestCaseDTO">
SELECT
atc.id, atc.project_id , atc.name , atc.num , atc.priority , atc.tags
from api_test_case atc
left join test_case_test tct on atc.id = tct.test_id
inner join api_definition ad on ad.id = atc.api_definition_id
where tct.test_id is NULL and ad.status != 'Trash'
<if test="request.protocol != null and request.protocol!=''">
and ad.protocol = #{request.protocol}
</if>
<if test="request.projectId != null and request.projectId!=''">
and ad.project_id = #{request.projectId}
</if>
<if test="request.name != null and request.name!=''">
and (atc.name like CONCAT('%', #{request.name},'%')
or atc.tags like CONCAT('%', #{request.name},'%')
or atc.num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and ad.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'priority'">
and atc.priority in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='status'">
and atc.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
<select id="relevanceScenarioList" resultType="io.metersphere.api.dto.automation.ApiScenarioDTO">
SELECT
atc.id, atc.project_id , atc.name , atc.num , atc.`level`, atc.step_total, atc.status
from api_scenario atc
left join test_case_test tct on atc.id = tct.test_id
where tct.test_id is NULL and atc.status != 'Trash'
<if test="request.projectId != null and request.projectId!=''">
and atc.project_id = #{request.projectId}
</if>
<if test="request.name != null and request.name!=''">
and (atc.name like CONCAT('%', #{request.name},'%')
or atc.tags like CONCAT('%', #{request.name},'%')
or atc.num like CONCAT('%', #{request.name},'%'))
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and atc.api_scenario_module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
<select id="relevanceLoadList" resultType="io.metersphere.dto.LoadTestDTO">
SELECT
atc.id, atc.project_id , atc.name , atc.num , atc.status
from load_test atc
left join test_case_test tct on atc.id = tct.test_id
where tct.test_id is NULL and atc.status != 'Trash'
<if test="request.projectId != null and request.projectId!=''">
and atc.project_id = #{request.projectId}
</if>
<if test="request.name != null and request.name!=''">
and (atc.name like CONCAT('%', #{request.name},'%')
or atc.num like CONCAT('%', #{request.name},'%'))
</if>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
<update id="updateByExampleSelective" parameterType="map">
update test_case_test
<set>
@ -172,4 +255,4 @@
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
</mapper>
</mapper>

View File

@ -0,0 +1,12 @@
package io.metersphere.dto;
import io.metersphere.base.domain.TestCaseTest;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TestCaseTestDao extends TestCaseTest {
private String name;
private String num;
}

View File

@ -780,4 +780,13 @@ public class PerformanceTestService {
mapper.insert(scenarioLoadTest);
});
}
public List<LoadTest> getLoadCaseByIds(List<String> ids) {
if (org.apache.commons.collections.CollectionUtils.isNotEmpty(ids)) {
LoadTestExample example = new LoadTestExample();
example.createCriteria().andIdIn(ids);
return loadTestMapper.selectByExample(example);
}
return new ArrayList<>();
}
}

View File

@ -2,15 +2,18 @@ package io.metersphere.track.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.FileMetadata;
import io.metersphere.base.domain.Project;
import io.metersphere.base.domain.TestCase;
import io.metersphere.base.domain.TestCaseWithBLOBs;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.base.domain.*;
import io.metersphere.commons.constants.OperLogConstants;
import io.metersphere.commons.constants.PermissionConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.LoadTestDTO;
import io.metersphere.dto.TestCaseTestDao;
import io.metersphere.excel.domain.ExcelResponse;
import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.service.CheckPermissionService;
@ -21,6 +24,7 @@ import io.metersphere.track.request.testcase.QueryTestCaseRequest;
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
import io.metersphere.track.request.testcase.TestCaseMinderEditRequest;
import io.metersphere.track.request.testplan.FileOperationRequest;
import io.metersphere.track.request.testplan.LoadCaseRequest;
import io.metersphere.track.service.TestCaseService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.http.HttpHeaders;
@ -110,6 +114,39 @@ public class TestCaseController {
return PageUtils.setPageInfo(page, testCaseService.getTestCaseIssueRelateList(request));
}
@PostMapping("/relevance/api/list/{goPage}/{pageSize}")
public Pager<List<ApiTestCaseDTO>> getTestCaseApiCaseRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiTestCaseRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, testCaseService.getTestCaseApiCaseRelateList(request));
}
@PostMapping("/relevance/scenario/list/{goPage}/{pageSize}")
public Pager<List<ApiScenarioDTO>> getTestCaseScenarioCaseRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiScenarioRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, testCaseService.getTestCaseScenarioCaseRelateList(request));
}
@PostMapping("/relevance/load/list/{goPage}/{pageSize}")
public Pager<List<LoadTestDTO>> getTestCaseLoadCaseRelateList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody LoadCaseRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, testCaseService.getTestCaseLoadCaseRelateList(request));
}
@GetMapping("/relate/test/list/{caseId}")
public List<TestCaseTestDao> getRelateTest(@PathVariable String caseId) {
return testCaseService.getRelateTest(caseId);
}
@PostMapping("/relate/test/{type}/{caseId}")
public void relateTest(@PathVariable String type, @PathVariable String caseId, @RequestBody List<String> apiIds) {
testCaseService.relateTest(type, caseId, apiIds);
}
@GetMapping("/relate/delete/{caseId}/{testId}")
public void relateDelete(@PathVariable String caseId, @PathVariable String testId) {
testCaseService.relateDelete(caseId, testId);
}
@PostMapping("/reviews/case/{goPage}/{pageSize}")
public Pager<List<TestCase>> getReviewCase(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestCaseRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);

View File

@ -42,17 +42,17 @@ public abstract class ZentaoClient extends BaseClient {
getUserResponse = (GetUserResponse) getResultForObject(GetUserResponse.class, response);
} catch (Exception e) {
LogUtil.error("get result for object error," + e.getMessage());
MSException.throwException("zentao login fail");
MSException.throwException(e.getMessage());
}
GetUserResponse.User user = getUserResponse.getUser();
if (user == null) {
LogUtil.error(JSONObject.toJSON(getUserResponse));
// 登录失败获取的session无效置空session
MSException.throwException("zentao login fail");
MSException.throwException("zentao login fail, user null");
}
if (!StringUtils.equals(user.getAccount(), USER_NAME)) {
LogUtil.error("login failinconsistent users");
MSException.throwException("zentao login fail");
MSException.throwException("zentao login fail, inconsistent user");
}
return sessionId;
}

View File

@ -6,6 +6,12 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
@ -18,7 +24,9 @@ import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.dto.CustomFieldDao;
import io.metersphere.dto.LoadTestDTO;
import io.metersphere.dto.TestCaseTemplateDao;
import io.metersphere.dto.TestCaseTestDao;
import io.metersphere.excel.domain.*;
import io.metersphere.excel.handler.FunctionCaseTemplateWriteHandler;
import io.metersphere.excel.listener.TestCaseNoModelDataListener;
@ -29,6 +37,7 @@ import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.log.vo.track.TestCaseReference;
import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.service.*;
import io.metersphere.track.dto.TestCaseCommentDTO;
import io.metersphere.track.dto.TestCaseDTO;
@ -36,6 +45,7 @@ import io.metersphere.track.request.testcase.EditTestCaseRequest;
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
import io.metersphere.track.request.testcase.TestCaseMinderEditRequest;
import io.metersphere.track.request.testplan.LoadCaseRequest;
import io.metersphere.xmind.XmindCaseParser;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
@ -119,6 +129,15 @@ public class TestCaseService {
private IssuesMapper issuesMapper;
@Resource
private CustomFieldService customFieldService;
@Resource
@Lazy
private ApiTestCaseService apiTestCaseService;
@Resource
@Lazy
private ApiAutomationService apiAutomationService;
@Resource
@Lazy
private PerformanceTestService performanceTestService;
private void setNode(TestCaseWithBLOBs testCase) {
if (StringUtils.isEmpty(testCase.getNodeId()) || "default-module".equals(testCase.getNodeId())) {
@ -305,6 +324,7 @@ public class TestCaseService {
TestCaseTestExample examples = new TestCaseTestExample();
examples.createCriteria().andTestCaseIdEqualTo(testCaseId);
testCaseTestMapper.deleteByExample(examples);
relateDelete(testCaseId);
return testCaseMapper.deleteByPrimaryKey(testCaseId);
}
@ -1712,4 +1732,93 @@ public class TestCaseService {
}
return null;
}
public List<ApiTestCaseDTO> getTestCaseApiCaseRelateList(ApiTestCaseRequest request) {
return testCaseTestMapper.relevanceApiList(request);
}
public void relateTest(String type, String caseId, List<String> apiIds) {
apiIds.forEach(testId -> {
TestCaseTest testCaseTest = new TestCaseTest();
testCaseTest.setTestType(type);
testCaseTest.setTestCaseId(caseId);
testCaseTest.setTestId(testId);
testCaseTest.setCreateTime(System.currentTimeMillis());
testCaseTest.setUpdateTime(System.currentTimeMillis());
testCaseTestMapper.insert(testCaseTest);
});
}
public void relateDelete(String caseId, String testId) {
TestCaseTestExample example = new TestCaseTestExample();
example.createCriteria()
.andTestCaseIdEqualTo(caseId)
.andTestIdEqualTo(testId);
testCaseTestMapper.deleteByExample(example);
}
public void relateDelete(String caseId) {
TestCaseTestExample example = new TestCaseTestExample();
example.createCriteria()
.andTestCaseIdEqualTo(caseId);
testCaseTestMapper.deleteByExample(example);
}
public List<TestCaseTestDao> getRelateTest(String caseId) {
TestCaseTestExample example = new TestCaseTestExample();
example.createCriteria()
.andTestCaseIdEqualTo(caseId);
List<TestCaseTest> testCaseTests = testCaseTestMapper.selectByExample(example);
Map<String, TestCaseTest> testCaseTestsMap = testCaseTests.stream()
.collect(Collectors.toMap(TestCaseTest::getTestId, i -> i));
List<ApiTestCase> apiCases = apiTestCaseService.getApiCaseByIds(
getTestIds(testCaseTests, "testcase")
);
List<ApiScenario> apiScenarios = apiAutomationService.getScenarioCaseByIds(
getTestIds(testCaseTests, "automation")
);
List<LoadTest> apiLoadTests = performanceTestService.getLoadCaseByIds(
getTestIds(testCaseTests, "performance")
);
List<TestCaseTestDao> testCaseTestList = new ArrayList<>();
apiCases.forEach(item -> {
getTestCaseTestDaoList("testcase", item.getNum(), item.getName(), item.getId(),
testCaseTestList, testCaseTestsMap);
});
apiScenarios.forEach(item -> {
getTestCaseTestDaoList("automation", item.getNum(), item.getName(), item.getId(),
testCaseTestList, testCaseTestsMap);
});
apiLoadTests.forEach(item -> {
getTestCaseTestDaoList("performance", item.getNum(), item.getName(), item.getId(),
testCaseTestList, testCaseTestsMap);
});
return testCaseTestList;
}
public void getTestCaseTestDaoList(String type, Object num, String name, String testId,
List<TestCaseTestDao> testCaseTestList, Map<String, TestCaseTest> testCaseTestsMap) {
TestCaseTestDao testCaseTestDao = new TestCaseTestDao();
BeanUtils.copyBean(testCaseTestDao, testCaseTestsMap.get(testId));
testCaseTestDao.setNum(num.toString());
testCaseTestDao.setName(name);
testCaseTestDao.setTestType(type);
testCaseTestList.add(testCaseTestDao);
}
public List<String> getTestIds(List<TestCaseTest> testCaseTests, String type) {
List<String> caseIds = testCaseTests.stream()
.filter(item -> item.getTestType().equals(type))
.map(TestCaseTest::getTestId)
.collect(Collectors.toList());
return caseIds;
}
public List<ApiScenarioDTO> getTestCaseScenarioCaseRelateList(ApiScenarioRequest request) {
return testCaseTestMapper.relevanceScenarioList(request);
}
public List<LoadTestDTO> getTestCaseLoadCaseRelateList(LoadCaseRequest request) {
return testCaseTestMapper.relevanceLoadList(request);
}
}

View File

@ -0,0 +1,100 @@
<template>
<test-case-relevance-base
@setProject="setProject"
@save="saveCaseRelevance"
width="50%"
ref="baseRelevance">
<template v-slot:aside>
<ms-api-module
:relevance-project-id="projectId"
@nodeSelectEvent="nodeChange"
@protocolChange="handleProtocolChange"
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
:is-read-only="true"
ref="nodeTree"/>
</template>
<test-case-relate-api-list
:current-protocol="currentProtocol"
:select-node-ids="selectNodeIds"
:project-id="projectId"
ref="apiCaseList"/>
</test-case-relevance-base>
</template>
<script>
import TestCaseRelateApiList from "@/business/components/track/case/components/TestCaseRelateApiList";
import MsApiModule from "@/business/components/api/definition/components/module/ApiModule";
import TestCaseRelevanceBase from "@/business/components/track/plan/view/comonents/base/TestCaseRelevanceBase";
export default {
name: "TestCaseApiRelate",
components: {
TestCaseRelevanceBase,
MsApiModule,
TestCaseRelateApiList,
},
data() {
return {
currentProtocol: null,
selectNodeIds: [],
moduleOptions: {},
condition: {},
projectId: ""
};
},
props: {
caseId: {
type: String
}
},
methods: {
open() {
this.init();
this.$refs.baseRelevance.open();
this.$refs.apiCaseList.clear();
},
init() {
if (this.$refs.apiCaseList) {
this.$refs.apiCaseList.initTable();
}
if (this.$refs.nodeTree) {
this.$refs.nodeTree.list();
}
},
setProject(projectId) {
this.projectId = projectId;
},
refresh(data) {
this.$refs.apiCaseList.initTable(data);
},
nodeChange(node, nodeIds, pNodes) {
this.selectNodeIds = nodeIds;
},
handleProtocolChange(protocol) {
this.currentProtocol = protocol;
},
setModuleOptions(data) {
this.moduleOptions = data;
},
saveCaseRelevance() {
let ids = this.$refs.apiCaseList.getSelectIds();
this.result = this.$post("/test/case/relate/test/testcase/" + this.caseId, ids, (response) => {
this.$success(this.$t('commons.save_success'));
this.$emit('refresh');
this.$refs.baseRelevance.close();
});
},
}
}
</script>
<style scoped>
</style>

View File

@ -81,7 +81,7 @@
<test-case-step-item :label-width="formLabelWidth" v-if="form.stepModel === 'STEP' || !form.stepModel" :form="form" :read-only="readOnly"/>
<test-case-edit-other-info :sys-list="sysList" :read-only="readOnly" :project-id="projectIds" :form="form"
<test-case-edit-other-info :read-only="readOnly" :project-id="projectIds" :form="form"
:label-width="formLabelWidth" :case-id="form.id" ref="otherInfo"/>
<el-row style="margin-top: 10px" v-if="type!='add'">
@ -176,7 +176,7 @@
},
data() {
return {
sysList: [],//
// sysList: [],//
path: "/test/case/add",
testCaseTemplate: {},
options: REVIEW_STATUS,
@ -215,7 +215,7 @@
customNum: ''
},
maintainerOptions: [],
testOptions: [],
// testOptions: [],
workspaceId: '',
rules: {
name: [
@ -337,146 +337,8 @@
this.form.module = this.treeNodes[0].id;
this.form.nodePath = this.treeNodes[0].path;
}
this.loadOptions();
},
methods: {
async loadOptions(sysLib) {
if (this.form.list) {
return;
}
sysLib = TEST
.filter(item => {
return enableModules([item.module]);
})//
.map(item => ({
value: item.id,
label: item.name,
}));
let array = [];
for (let i = 0; i < sysLib.length; i++) {
if (sysLib.length > 0) {
let res = await this.getTestOptions(sysLib[i].value);
sysLib[i].children = res;
}
array.push(sysLib[i]);
}
this.sysList = array;
},
getTestOptions(val) {
this.result.loading = true;
this.form.type = val;
this.testOptions = [];
let url = '';
if (this.form.type === 'performance') {
url = '/' + this.form.type + '/list/' + this.projectId;
if (!url) {
return;
}
this.result.loading = true;
return new Promise((resolve, reject) => {
this.$get(url).then(res => {
const data = res.data.data.map(item => ({
value: item.id,
label: item.name,
leaf: true
}));
this.result.loading = false;
resolve(data);
}).catch((err) => {
reject(err);
});
});
} else if (this.form.type === 'automation') {
url = '/api/automation/module/list/' + this.projectId;
if (!url) {
return;
}
this.result.loading = true;
return new Promise((resolve, reject) => {
this.$get("/api/automation/module/list/" + this.projectId, response => {
if (response.data != undefined && response.data != null) {
this.buildTreeValue(response.data);
}
this.result.loading = false;
resolve(response.data);
});
});
} else if (this.form.type === 'testcase') {
this.result.loading = true;
return new Promise((resolve, reject) => {
TEST_CASE.forEach(test => {
let url = "/api/module/list/" + this.projectId + "/" + test.value;
this.$get(url, response => {
if (response.data != undefined && response.data != null) {
this.buildTreeValueApiCase(response.data);
test.children = response.data;
}
});
});
this.result.loading = false;
resolve(TEST_CASE);
});
}
},
buildTreeValueApiCase(list) {
list.forEach(item => {
item.value = item.id,
item.label = item.name,
item.leaf = true;
if (item.children) {
this.buildTreeValueApiCase(item.children);
} else {
let url = "/api/testcase/list/";
let param = {};
param.moduleId = item.id;
param.projectId = this.projectId;
this.$post(url, param, response => {
if (response.data != undefined && response.data != null) {
item.children = response.data;
this.buildTreeValueApiCase(item.children);
}
});
}
});
},
buildTreeValue(list) {
let url = '/api/automation/list';
list.forEach(item => {
item.value = item.id,
item.label = item.name,
item.leaf = true;
if (item.children) {
this.buildTreeValue(item.children);
} else {
let param = {};
param.moduleId = item.id;
param.projectId = this.projectId;
this.$post(url, param, response => {
if (response.data != undefined && response.data != null) {
item.children = response.data;
this.buildTreeValue(item.children);
}
});
}
});
},
buildValue(url) {
return new Promise((resolve, reject) => {
this.$get(url).then(res => {
const data = res.data.data.map(item => ({
value: item.id,
label: item.name,
leaf: true
}));
this.result.loading = false;
resolve(data);
}).catch((err) => {
reject(err);
});
});
},
openHis() {
this.$refs.changeHistory.open(this.form.id);
},

View File

@ -6,23 +6,7 @@
</el-row>
</el-tab-pane>
<el-tab-pane :label="$t('test_track.case.relate_test')" name="relateTest">
<el-col v-if="form.list" :span="7" :offset="1">
<span class="cast_label">{{ $t('test_track.case.relate_test') }}</span>
<span v-for="(item,index) in form.list" :key="index">
<el-button @click="openTest(item)" type="text" style="margin-left: 7px;">{{
item.testName
}}</el-button>
</span>
</el-col>
<el-col v-else :span="7" style="margin-top: 10px;">
<el-form-item :label="$t('test_track.case.relate_test')">
<el-cascader :options="sysList" filterable :placeholder="$t('test_track.case.please_select_relate_test')"
show-all-levels
v-model="form.selected" :props="props"
:disabled="readOnly"
ref="cascade"></el-cascader>
</el-form-item>
</el-col>
<test-case-test-relate :read-only="readOnly" :case-id="caseId"/>
</el-tab-pane>
<el-tab-pane :label="$t('test_track.related_requirements')" name="demand">
@ -90,16 +74,17 @@
import {Message} from "element-ui";
import TestCaseRichText from "@/business/components/track/case/components/MsRichText";
import MsRichText from "@/business/components/track/case/components/MsRichText";
import {TEST} from "@/business/components/api/definition/model/JsonData";
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";
import TestCaseIssueRelate from "@/business/components/track/case/components/TestCaseIssueRelate";
import {enableModules} from "@/common/js/utils";
import FormRichTextItem from "@/business/components/track/case/components/FormRichTextItem";
import TestCaseTestRelate from "@/business/components/track/case/components/TestCaseTestRelate";
export default {
name: "TestCaseEditOtherInfo",
components: {FormRichTextItem, TestCaseIssueRelate, TestCaseAttachment, MsRichText, TestCaseRichText},
props: ['form', 'labelWidth', 'caseId', 'readOnly', 'projectId', 'isTestPlan', 'planId', 'sysList'],
components: {
TestCaseTestRelate,
FormRichTextItem, TestCaseIssueRelate, TestCaseAttachment, MsRichText, TestCaseRichText},
props: ['form', 'labelWidth', 'caseId', 'readOnly', 'projectId', 'isTestPlan', 'planId'],
data() {
return {
result: {},
@ -143,9 +128,6 @@ export default {
/// todo:
return file.size > 0;
},
openTest(data) {
this.$emit('openTest', data);
},
beforeUpload(file) {
if (!this.fileValidator(file)) {
/// todo:

View File

@ -0,0 +1,80 @@
<template>
<test-case-relevance-base
@setProject="setProject"
@save="saveCaseRelevance"
width="50%"
ref="baseRelevance">
<test-case-relate-load-list
:project-id="projectId"
ref="apiCaseList"/>
</test-case-relevance-base>
</template>
<script>
import TestCaseRelateApiList from "@/business/components/track/case/components/TestCaseRelateApiList";
import MsApiModule from "@/business/components/api/definition/components/module/ApiModule";
import TestCaseRelevanceBase from "@/business/components/track/plan/view/comonents/base/TestCaseRelevanceBase";
import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule";
import TestCaseRelateScenarioList from "@/business/components/track/case/components/TestCaseRelateScenarioList";
import TestCaseRelateLoadList from "@/business/components/track/case/components/TestCaseRelateLoadList";
export default {
name: "TestCaseLoadRelate",
components: {
TestCaseRelateLoadList,
TestCaseRelateScenarioList,
MsApiScenarioModule,
TestCaseRelevanceBase,
MsApiModule,
TestCaseRelateApiList,
},
data() {
return {
condition: {},
projectId: ""
};
},
props: {
caseId: {
type: String
}
},
methods: {
open() {
this.init();
this.$refs.baseRelevance.open();
this.$refs.apiCaseList.clear();
},
init() {
if (this.$refs.apiCaseList) {
this.$refs.apiCaseList.initTable();
}
if (this.$refs.nodeTree) {
this.$refs.nodeTree.list();
}
},
setProject(projectId) {
this.projectId = projectId;
},
refresh(data) {
this.$refs.apiCaseList.initTable(data);
},
saveCaseRelevance() {
let ids = this.$refs.apiCaseList.getSelectIds();
this.result = this.$post("/test/case/relate/test/performance/" + this.caseId, ids, (response) => {
this.$success(this.$t('commons.save_success'));
this.$emit('refresh');
this.$refs.baseRelevance.close();
});
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,167 @@
<template>
<div>
<el-input :placeholder="$t('commons.search_by_name_or_id')" @blur="initTable"
@keyup.enter.native="initTable" class="search-input" size="small" v-model="condition.name"/>
<ms-table v-loading="result.loading" :data="tableData" :select-node-ids="selectNodeIds" :condition="condition" :page-size="pageSize"
:total="total"
:showSelectAll="false"
:screenHeight="screenHeight"
@refresh="initTable"
ref="table">
<ms-table-column
prop="num"
label="ID"
width="100px"
sortable=true>
</ms-table-column>
<ms-table-column
prop="name"
:label="$t('test_track.case.name')"/>
<ms-table-column
prop="priority"
:filters="priorityFilters"
column-key="priority"
:label="$t('test_track.case.priority')">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority"/>
</template>
</ms-table-column>
<!-- <ms-table-column-->
<!-- prop="path"-->
<!-- width="180px"-->
<!-- :label="'API'+ $t('api_test.definition.api_path')"/>-->
<!-- <ms-table-column-->
<!-- sortable="custom"-->
<!-- prop="casePath"-->
<!-- width="180px"-->
<!-- :label="$t('api_test.definition.request.case')+ $t('api_test.definition.api_path')"/>-->
</ms-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<table-select-count-bar :count="selectRows.size"/>
</div>
</template>
<script>
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import {API_METHOD_COLOUR} from "@/business/components/api/definition/model/JsonData";
import PriorityTableItem from "@/business/components/track/common/tableItems/planview/PriorityTableItem";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import TableSelectCountBar from "@/business/components/api/automation/scenario/api/TableSelectCountBar";
export default {
name: "TestCaseRelateApiList",
components: {
TableSelectCountBar,
MsTablePagination,
PriorityTableItem,
MsTable,
MsTableColumn
},
data() {
return {
condition: {},
selectCase: {},
result: {},
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
methodColorMap: new Map(API_METHOD_COLOUR),
screenHeight: 'calc(100vh - 600px)',//
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
}
},
props: {
currentProtocol: String,
selectNodeIds: Array,
projectId: String,
},
created: function () {
this.initTable();
},
watch: {
selectNodeIds() {
this.initTable();
},
currentProtocol() {
this.initTable();
},
projectId() {
this.initTable();
}
},
computed: {
selectRows() {
if (this.$refs.table) {
return this.$refs.table.getSelectRows();
} else {
return new Set();
}
}
},
methods: {
initTable(projectId) {
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
if (projectId != null && typeof projectId === 'string') {
this.condition.projectId = projectId;
} else if (this.projectId != null) {
this.condition.projectId = this.projectId;
}
if (this.currentProtocol != null) {
this.condition.protocol = this.currentProtocol;
}
let url = '/test/case/relevance/api/list/';
this.result = this.$post(this.buildPagePath(url), this.condition, response => {
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
});
},
clear() {
if (this.$refs.table) {
this.$refs.table.clear();
}
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
getSelectIds() {
return this.$refs.table.selectIds;
},
clearSelection() {
if (this.$refs.table) {
this.$refs.table.clearSelectRows();
}
},
},
}
</script>
<style scoped>
.search-input {
float: right;
width: 300px;
/*margin-bottom: 20px;*/
margin-right: 20px;
}
</style>

View File

@ -0,0 +1,132 @@
<template>
<div>
<el-input :placeholder="$t('commons.search_by_name_or_id')" @blur="initTable"
@keyup.enter.native="initTable" class="search-input" size="small" v-model="condition.name"/>
<ms-table v-loading="result.loading" :data="tableData" :condition="condition" :page-size="pageSize"
:total="total"
:showSelectAll="false"
:screenHeight="screenHeight"
@refresh="initTable"
ref="table">
<ms-table-column
prop="num"
label="ID"
width="100px"
sortable=true>
</ms-table-column>
<ms-table-column
prop="name"
:label="$t('commons.name')"/>
<ms-table-column
prop="status"
:label="$t('commons.status')"
min-width="80">
<template v-slot:default="{row}">
<ms-performance-test-status :row="row"/>
</template>
</ms-table-column>
</ms-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<table-select-count-bar :count="selectRows.size"/>
</div>
</template>
<script>
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import TableSelectCountBar from "@/business/components/api/automation/scenario/api/TableSelectCountBar";
import MsPerformanceTestStatus from "@/business/components/performance/test/PerformanceTestStatus";
export default {
name: "TestCaseRelateLoadList",
components: {
MsPerformanceTestStatus,
TableSelectCountBar,
MsTablePagination,
MsTable,
MsTableColumn
},
data() {
return {
condition: {},
result: {},
screenHeight: 'calc(100vh - 600px)',//
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
}
},
props: {
projectId: String,
},
created: function () {
this.initTable();
},
watch: {
projectId() {
this.initTable();
}
},
computed: {
selectRows() {
if (this.$refs.table) {
return this.$refs.table.getSelectRows();
} else {
return new Set();
}
}
},
methods: {
initTable(projectId) {
this.condition.status = "";
if (projectId != null && typeof projectId === 'string') {
this.condition.projectId = projectId;
} else if (this.projectId != null) {
this.condition.projectId = this.projectId;
}
let url = '/test/case/relevance/load/list/';
this.result = this.$post(this.buildPagePath(url), this.condition, response => {
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
});
},
clear() {
if (this.$refs.table) {
this.$refs.table.clear();
}
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
getSelectIds() {
return this.$refs.table.selectIds;
},
clearSelection() {
if (this.$refs.table) {
this.$refs.table.clearSelectRows();
}
},
},
}
</script>
<style scoped>
.search-input {
float: right;
width: 300px;
margin-right: 20px;
}
</style>

View File

@ -0,0 +1,169 @@
<template>
<div>
<el-input :placeholder="$t('commons.search_by_name_or_id')" @blur="initTable"
@keyup.enter.native="initTable" class="search-input" size="small" v-model="condition.name"/>
<ms-table v-loading="result.loading" :data="tableData" :select-node-ids="selectNodeIds" :condition="condition" :page-size="pageSize"
:total="total"
:showSelectAll="false"
:screenHeight="screenHeight"
@refresh="initTable"
ref="table">
<ms-table-column
prop="num"
label="ID"
width="100px"
sortable=true>
</ms-table-column>
<!-- <ms-table-column-->
<!-- v-if="item.id == 'num' && customNum" prop="customNum"-->
<!-- label="ID"-->
<!-- sortable-->
<!-- :fields-width="fieldsWidth"-->
<!-- min-width="120px">-->
<!-- <template slot-scope="scope">-->
<!-- &lt;!&ndash;<span style="cursor:pointer" v-if="isReadOnly"> {{ scope.row.customNum }} </span>&ndash;&gt;-->
<!-- <el-tooltip content="编辑">-->
<!-- <a style="cursor:pointer" @click="edit(scope.row)"> {{ scope.row.customNum }} </a>-->
<!-- </el-tooltip>-->
<!-- </template>-->
<!-- </ms-table-column>-->
<ms-table-column
prop="name"
:label="$t('api_test.automation.scenario_name')"/>
<ms-table-column
prop="level"
sortable
min-width="130px"
:label="$t('api_test.automation.case_level')">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.level"/>
</template>
</ms-table-column>
<ms-table-column prop="status"
:label="$t('test_track.plan.plan_status')"
sortable
min-width="120px">
<template v-slot:default="scope">
<plan-status-table-item :value="scope.row.status"/>
</template>
</ms-table-column>
</ms-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<table-select-count-bar :count="selectRows.size"/>
</div>
</template>
<script>
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import PriorityTableItem from "@/business/components/track/common/tableItems/planview/PriorityTableItem";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import TableSelectCountBar from "@/business/components/api/automation/scenario/api/TableSelectCountBar";
import PlanStatusTableItem from "@/business/components/track/common/tableItems/plan/PlanStatusTableItem";
export default {
name: "TestCaseRelateScenarioList",
components: {
PlanStatusTableItem,
TableSelectCountBar,
MsTablePagination,
PriorityTableItem,
MsTable,
MsTableColumn
},
data() {
return {
condition: {},
result: {},
priorityFilters: [
{text: 'P0', value: 'P0'},
{text: 'P1', value: 'P1'},
{text: 'P2', value: 'P2'},
{text: 'P3', value: 'P3'}
],
screenHeight: 'calc(100vh - 600px)',//
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
}
},
props: {
selectNodeIds: Array,
projectId: String,
},
created: function () {
this.initTable();
},
watch: {
selectNodeIds() {
this.initTable();
},
projectId() {
this.initTable();
}
},
computed: {
selectRows() {
if (this.$refs.table) {
return this.$refs.table.getSelectRows();
} else {
return new Set();
}
}
},
methods: {
initTable(projectId) {
this.condition.status = "";
this.condition.moduleIds = this.selectNodeIds;
if (projectId != null && typeof projectId === 'string') {
this.condition.projectId = projectId;
} else if (this.projectId != null) {
this.condition.projectId = this.projectId;
}
let url = '/test/case/relevance/scenario/list/';
this.result = this.$post(this.buildPagePath(url), this.condition, response => {
this.total = response.data.itemCount;
this.tableData = response.data.listObject;
});
},
clear() {
if (this.$refs.table) {
this.$refs.table.clear();
}
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
getSelectIds() {
return this.$refs.table.selectIds;
},
clearSelection() {
if (this.$refs.table) {
this.$refs.table.clearSelectRows();
}
},
},
}
</script>
<style scoped>
.search-input {
float: right;
width: 300px;
margin-right: 20px;
}
</style>

View File

@ -0,0 +1,100 @@
<template>
<test-case-relevance-base
@setProject="setProject"
@save="saveCaseRelevance"
width="50%"
ref="baseRelevance">
<template v-slot:aside>
<ms-api-scenario-module
:relevance-project-id="projectId"
@nodeSelectEvent="nodeChange"
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
@enableTrash="false"
:is-read-only="true"
ref="nodeTree"/>
</template>
<test-case-relate-scenario-list
:select-node-ids="selectNodeIds"
:project-id="projectId"
ref="apiCaseList"/>
</test-case-relevance-base>
</template>
<script>
import TestCaseRelateApiList from "@/business/components/track/case/components/TestCaseRelateApiList";
import MsApiModule from "@/business/components/api/definition/components/module/ApiModule";
import TestCaseRelevanceBase from "@/business/components/track/plan/view/comonents/base/TestCaseRelevanceBase";
import MsApiScenarioModule from "@/business/components/api/automation/scenario/ApiScenarioModule";
import TestCaseRelateScenarioList from "@/business/components/track/case/components/TestCaseRelateScenarioList";
export default {
name: "TestCaseScenarioRelate",
components: {
TestCaseRelateScenarioList,
MsApiScenarioModule,
TestCaseRelevanceBase,
MsApiModule,
TestCaseRelateApiList,
},
data() {
return {
selectNodeIds: [],
moduleOptions: {},
condition: {},
projectId: ""
};
},
props: {
caseId: {
type: String
}
},
methods: {
open() {
this.init();
this.$refs.baseRelevance.open();
this.$refs.apiCaseList.clear();
},
init() {
if (this.$refs.apiCaseList) {
this.$refs.apiCaseList.initTable();
}
if (this.$refs.nodeTree) {
this.$refs.nodeTree.list();
}
},
setProject(projectId) {
this.projectId = projectId;
},
refresh(data) {
this.$refs.apiCaseList.initTable(data);
},
nodeChange(node, nodeIds, pNodes) {
this.selectNodeIds = nodeIds;
},
setModuleOptions(data) {
this.moduleOptions = data;
},
saveCaseRelevance() {
let ids = this.$refs.apiCaseList.getSelectIds();
this.result = this.$post("/test/case/relate/test/automation/" + this.caseId, ids, (response) => {
this.$success(this.$t('commons.save_success'));
this.$emit('refresh');
this.$refs.baseRelevance.close();
});
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,125 @@
<template>
<div>
<el-dropdown @command="handleCommand">
<el-button type="primary" size="mini" :disabled="readOnly">
{{$t('test_track.case.relate_test')}}<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="api">{{ $t('api_test.home_page.failed_case_list.table_value.case_type.api') }}</el-dropdown-item>
<el-dropdown-item command="scenario">{{ $t('api_test.home_page.failed_case_list.table_value.case_type.scene') }}</el-dropdown-item>
<el-dropdown-item command="performance">{{$t('api_test.home_page.failed_case_list.table_value.case_type.load')}}</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<ms-table
v-loading="result.loading"
:show-select-all="false"
:data="data"
:enable-selection="false"
:operators="operators"
@refresh="initTable">
<ms-table-column
prop="num"
label="ID">
</ms-table-column>
<ms-table-column
prop="name"
:label="$t('test_track.case.name')"/>
<ms-table-column
:label="$t('test_resource_pool.type')"
prop="type">
<template v-slot:default="{row}">
{{typeMap[row.testType]}}
</template>
</ms-table-column>
</ms-table>
<test-case-api-relate
:case-id="caseId"
@refresh="initTable"
ref="apiCaseRelevance"/>
<test-case-scenario-relate
:case-id="caseId"
@refresh="initTable"
ref="apiScenarioRelevance"/>
<test-case-load-relate
:case-id="caseId"
@refresh="initTable"
ref="loadRelevance"/>
</div>
</template>
<script>
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import TestCaseApiRelate from "@/business/components/track/case/components/TestCaseApiRelate";
import {deleteRelateTest, getRelateTest} from "@/network/testCase";
import TestCaseScenarioRelate from "@/business/components/track/case/components/TestCaseScenarioRelate";
import TestCaseLoadRelate from "@/business/components/track/case/components/TestCaseLoadRelate";
export default {
name: "TestCaseTestRelate",
components: {TestCaseLoadRelate, TestCaseScenarioRelate, TestCaseApiRelate, MsTableColumn, MsTable},
data() {
return {
data: [],
result: {},
typeMap: {
testcase: this.$t('api_test.home_page.failed_case_list.table_value.case_type.api'),
automation: this.$t('api_test.home_page.failed_case_list.table_value.case_type.scene'),
performance: this.$t('api_test.home_page.failed_case_list.table_value.case_type.load')
},
operators: [
{
tip: this.$t('test_track.plan_view.cancel_relevance'), icon: "el-icon-unlock",
exec: this.remove,
type: 'danger',
isDisable: () => {
return this.readOnly;
}
}
],
}
},
props: ['caseId', 'readOnly'],
watch: {
caseId() {
this.initTable();
}
},
methods: {
handleCommand(key) {
if (!this.caseId) {
this.$warning(this.$t('api_test.automation.save_case_info'));
return;
}
if (key === 'api') {
this.$refs.apiCaseRelevance.open();
} else if (key === 'scenario') {
this.$refs.apiScenarioRelevance.open();
} else if (key === 'performance') {
this.$refs.loadRelevance.open();
}
},
remove(row) {
deleteRelateTest(row.testCaseId, row.testId, () => {
this.initTable();
});
},
initTable() {
this.result = getRelateTest(this.caseId, (data) => {
this.data = data;
});
},
}
}
</script>
<style scoped>
</style>

View File

@ -2,7 +2,7 @@
<el-dialog :title="title"
:visible.sync="dialogVisible"
@close="close"
width="75%" v-loading="result.loading"
:width="width ? width : '75%'" v-loading="result.loading"
:close-on-click-modal="false"
top="50px" append-to-body>
@ -44,7 +44,7 @@
dialogVisible: false,
};
},
props: ['title'],
props: ['title', 'width'],
methods: {
open() {
this.dialogVisible = true;

View File

@ -1,5 +1,5 @@
<template>
<relevance-dialog :title="dialogTitle" ref="relevanceDialog">
<relevance-dialog :width="width" :title="dialogTitle" ref="relevanceDialog">
<template v-slot:aside>
<select-menu
@ -66,7 +66,8 @@
},
flag:{
type:Boolean,
}
},
width: String
},
watch: {

View File

@ -1,4 +1,6 @@
import {post} from "@/common/js/ajax";
import {post, get} from "@/common/js/ajax";
import {success} from "@/common/js/message";
import i18n from "@/i18n/i18n";
export function getTestCasesForMinder(request, callback) {
return post('/test/case/list/minder', request, (response) => {
@ -23,3 +25,26 @@ export function getReviewCasesForMinder(request, callback) {
}
});
}
export function getRelateTest(caseId, callback) {
if (caseId) {
return get('/test/case/relate/test/list/' + caseId, (response) => {
if (callback) {
callback(response.data);
}
});
}
return {};
}
export function deleteRelateTest(caseId, testId, callback) {
if (caseId && testId) {
return get('/test/case/relate/delete/' + caseId + '/' + testId, (response) => {
success(i18n.t('commons.save_success'));
if (callback) {
callback(response);
}
});
}
}