feat(测试跟踪): 关联测试用例支持点击id跳转

--story=1013675 --user=王孝刚 github#27426 【测试跟踪】关联测试用例支持点击用例ID在新开页面打开该用例
https://www.tapd.cn/55049933/s/1436649
This commit is contained in:
wxg0103 2023-11-15 12:16:03 +08:00 committed by Craftsman
parent 3cb5202f98
commit 0ec81e3668
10 changed files with 192 additions and 56 deletions

View File

@ -116,4 +116,6 @@ public interface ExtApiTestCaseMapper {
String findPassRateById(String id);
List<ApiTestCaseInfo> selectByCaseIds(List<String> ids);
ApiTestCaseDTO getApiTestCaseDTO(String id);
}

View File

@ -339,6 +339,14 @@
WHERE t1.id = #{0}
</select>
<select id="getApiTestCaseDTO" resultType="io.metersphere.api.dto.definition.ApiTestCaseDTO">
SELECT t1.*,
a.protocol AS protocol
FROM api_test_case t1
inner join api_definition a on t1.api_definition_id = a.id
WHERE t1.id = #{0}
</select>
<select id="selectByCaseIds" resultType="io.metersphere.api.dto.definition.ApiTestCaseInfo">
SELECT t1.*,
a.method AS apiMethod,

View File

@ -300,4 +300,9 @@ public class ApiTestCaseController {
public int getCaseCountById(@RequestBody SaveApiTestCaseRequest request) {
return apiTestCaseService.getCaseCountById(request.getId());
}
@GetMapping("/{id}")
public ApiTestCaseDTO getApiTestCaseDTO(@PathVariable String id) {
return apiTestCaseService.getApiTestCaseDTO(id);
}
}

View File

@ -1409,4 +1409,7 @@ public class ApiTestCaseService {
}
}
public ApiTestCaseDTO getApiTestCaseDTO(String id) {
return extApiTestCaseMapper.getApiTestCaseDTO(id);
}
}

View File

@ -1,12 +1,10 @@
package io.metersphere.controller.remote;
import io.metersphere.dto.ApiCaseRelevanceRequest;
import io.metersphere.plan.dto.ApiTestCaseDTO;
import io.metersphere.plan.service.TestPlanService;
import io.metersphere.service.remote.api.RelevanceApiCaseService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import jakarta.annotation.Resource;
@ -19,9 +17,16 @@ public class TrackApiCaseController {
@Resource
TestPlanService testPlanService;
@PostMapping("/relevance")
public void relevance(@RequestBody ApiCaseRelevanceRequest request) {
request.setAllowedRepeatCase(testPlanService.isAllowedRepeatCase(request.getPlanId()));
relevanceApiCaseService.relevance(request);
}
@GetMapping("/{id}")
public ApiTestCaseDTO get(@PathVariable String id) {
return relevanceApiCaseService.getApiTestCaseDTO(id);
}
}

View File

@ -11,4 +11,5 @@ public class TestCaseTestDao extends TestCaseTest {
private String num;
private String projectName;
private String versionName;
private String projectId;
}

View File

@ -66,7 +66,6 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
@ -2915,21 +2914,21 @@ public class TestCaseService {
List<TestCaseTestDao> testCaseTestList = new ArrayList<>();
apiCases.forEach(item -> {
getTestCaseTestDaoList(TestCaseTestType.testcase.name(), item.getNum(), item.getName(), item.getId(), projectNameMap.get(item.getProjectId()), versionNameMap.get(item.getVersionId()),
testCaseTestList, testCaseTestsMap);
testCaseTestList, testCaseTestsMap, item.getProjectId());
});
apiScenarios.forEach(item -> {
// 所属项目是否开启自定义场景ID
String customType = customTypeMap.get(item.getProjectId());
getTestCaseTestDaoList(TestCaseTestType.automation.name(), StringUtils.equals(customType, "true") ? item.getCustomNum() : item.getNum(), item.getName(), item.getId(), projectNameMap.get(item.getProjectId()), versionNameMap.get(item.getVersionId()),
testCaseTestList, testCaseTestsMap);
testCaseTestList, testCaseTestsMap, item.getProjectId());
});
apiLoadTests.forEach(item -> {
getTestCaseTestDaoList(TestCaseTestType.performance.name(), item.getNum(), item.getName(), item.getId(), projectNameMap.get(item.getProjectId()), versionNameMap.get(item.getVersionId()),
testCaseTestList, testCaseTestsMap);
testCaseTestList, testCaseTestsMap, item.getProjectId());
});
uiScenarios.forEach(item -> {
getTestCaseTestDaoList(TestCaseTestType.uiAutomation.name(), item.getNum(), item.getName(), item.getId(), projectNameMap.get(item.getProjectId()), versionNameMap.get(item.getVersionId()),
testCaseTestList, testCaseTestsMap);
testCaseTestList, testCaseTestsMap, item.getProjectId());
});
// 根据关联记录时间展示
Collections.sort(testCaseTestList, Comparator.comparingLong(TestCaseTestDao::getCreateTime));
@ -2937,7 +2936,7 @@ public class TestCaseService {
}
public void getTestCaseTestDaoList(String type, Object num, String name, String testId, String projectName, String versionName,
List<TestCaseTestDao> testCaseTestList, Map<String, TestCaseTest> testCaseTestsMap) {
List<TestCaseTestDao> testCaseTestList, Map<String, TestCaseTest> testCaseTestsMap, String projectId) {
TestCaseTestDao testCaseTestDao = new TestCaseTestDao();
BeanUtils.copyBean(testCaseTestDao, testCaseTestsMap.get(testId));
testCaseTestDao.setNum(num.toString());
@ -2945,6 +2944,7 @@ public class TestCaseService {
testCaseTestDao.setTestType(type);
testCaseTestDao.setProjectName(projectName);
testCaseTestDao.setVersionName(versionName);
testCaseTestDao.setProjectId(projectId);
testCaseTestList.add(testCaseTestDao);
}

View File

@ -3,6 +3,7 @@ package io.metersphere.service.remote.api;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiTestCase;
import io.metersphere.dto.ApiCaseRelevanceRequest;
import io.metersphere.plan.dto.ApiTestCaseDTO;
import org.springframework.stereotype.Service;
import java.util.List;
@ -23,4 +24,8 @@ public class RelevanceApiCaseService extends TrackApiTestService{
public List<ApiScenario> getScenarioCaseByIds(List<String> ids) {
return microService.postForDataArray(serviceName, BASE_URL + "/getScenarioCaseByIds", ids, ApiScenario.class);
}
public ApiTestCaseDTO getApiTestCaseDTO(String id) {
return microService.getForData(serviceName, BASE_URL + "/" + id, ApiTestCaseDTO.class);
}
}

View File

@ -1,21 +1,24 @@
import {post, get} from "metersphere-frontend/src/plugins/request";
import {getCurrentProjectID, getCurrentWorkspaceId} from "metersphere-frontend/src/utils/token";
import {buildPagePath} from "@/api/base-network";
import { post, get } from "metersphere-frontend/src/plugins/request";
import {
getCurrentProjectID,
getCurrentWorkspaceId,
} from "metersphere-frontend/src/utils/token";
import { buildPagePath } from "@/api/base-network";
const BASE_URL = "/test/case/";
export function testCaseList({pageNum, pageSize}, param) {
let url = buildPagePath({pageNum, pageSize, path: 'list'});
export function testCaseList({ pageNum, pageSize }, param) {
let url = buildPagePath({ pageNum, pageSize, path: "list" });
return post(BASE_URL + url, param);
}
export function testCasePublicList({pageNum, pageSize}, param) {
let url = buildPagePath({pageNum, pageSize, path: 'public/list'});
export function testCasePublicList({ pageNum, pageSize }, param) {
let url = buildPagePath({ pageNum, pageSize, path: "public/list" });
return post(BASE_URL + url, param);
}
export function testCaseAdd(param) {
return post(BASE_URL + 'save', param);
return post(BASE_URL + "save", param);
}
export function getTestCase(id) {
@ -67,35 +70,35 @@ export function testCaseDeleteToGc(id) {
}
export function testCaseBatchDelete(param) {
return post(BASE_URL + 'batch/delete', param);
return post(BASE_URL + "batch/delete", param);
}
export function testCaseBatchDeleteToGc(param) {
return post(BASE_URL + 'batch/deleteToGc', param);
return post(BASE_URL + "batch/deleteToGc", param);
}
export function testCaseReduction(param) {
return post(BASE_URL + 'reduction', param);
return post(BASE_URL + "reduction", param);
}
export function testCaseBatchEdit(param) {
return post(BASE_URL + 'batch/edit', param);
return post(BASE_URL + "batch/edit", param);
}
export function testCaseBatchCopy(param) {
return post(BASE_URL + 'batch/copy', param);
return post(BASE_URL + "batch/copy", param);
}
export function testCasePublicBatchCopy(param) {
return post(BASE_URL + 'batch/copy/public', param);
return post(BASE_URL + "batch/copy/public", param);
}
export function testCasePublicBatchDeleteToGc(param) {
return post(BASE_URL + 'batch/movePublic/deleteToGc', param);
return post(BASE_URL + "batch/movePublic/deleteToGc", param);
}
export function testCaseBatchRelateDemand(param) {
return post(BASE_URL + 'batch/relate/demand', param);
return post(BASE_URL + "batch/relate/demand", param);
}
export function getTestCaseFollow(id) {
@ -110,13 +113,13 @@ export function addTestCaseRelationship(param) {
return post(BASE_URL + `relationship/add`, param);
}
export function testCaseRelationshipRelateList({pageNum, pageSize}, param) {
let url = buildPagePath({pageNum, pageSize, path: 'relationship/relate'});
export function testCaseRelationshipRelateList({ pageNum, pageSize }, param) {
let url = buildPagePath({ pageNum, pageSize, path: "relationship/relate" });
return post(BASE_URL + url, param);
}
export function testCaseRelateList({pageNum, pageSize}, param) {
let url = buildPagePath({pageNum, pageSize, path: 'relate'});
export function testCaseRelateList({ pageNum, pageSize }, param) {
let url = buildPagePath({ pageNum, pageSize, path: "relate" });
return post(BASE_URL + url, param);
}
@ -126,7 +129,7 @@ function getMinderPageInfo(request) {
if (!minderPageInfoMap.get(request.nodeId)) {
minderPageInfoMap.set(request.nodeId, {
pageNum: 1,
pageSize: 100
pageSize: 100,
});
}
return minderPageInfoMap.get(request.nodeId);
@ -134,7 +137,11 @@ function getMinderPageInfo(request) {
export function getTestCasesForMinder(request, callback) {
let minderPageInfo = getMinderPageInfo(request);
let url = '/test/case/list/minder/' + minderPageInfo.pageNum + '/' + minderPageInfo.pageSize;
let url =
"/test/case/list/minder/" +
minderPageInfo.pageNum +
"/" +
minderPageInfo.pageSize;
return post(url, request).then((response) => {
if (callback) {
minderPageInfo.total = response.data.itemCount;
@ -145,7 +152,11 @@ export function getTestCasesForMinder(request, callback) {
export function getPlanCasesForMinder(request, callback) {
let minderPageInfo = getMinderPageInfo(request);
let url = '/test/plan/case/list/minder/' + minderPageInfo.pageNum + '/' + minderPageInfo.pageSize;
let url =
"/test/plan/case/list/minder/" +
minderPageInfo.pageNum +
"/" +
minderPageInfo.pageSize;
return post(url, request).then((response) => {
if (callback) {
minderPageInfo.total = response.data.itemCount;
@ -156,7 +167,11 @@ export function getPlanCasesForMinder(request, callback) {
export function getReviewCasesForMinder(request, callback) {
let minderPageInfo = getMinderPageInfo(request);
let url = '/test/review/case/list/minder/' + minderPageInfo.pageNum + '/' + minderPageInfo.pageSize;
let url =
"/test/review/case/list/minder/" +
minderPageInfo.pageNum +
"/" +
minderPageInfo.pageSize;
return post(url, request).then((response) => {
if (callback) {
minderPageInfo.total = response.data.itemCount;
@ -166,71 +181,71 @@ export function getReviewCasesForMinder(request, callback) {
}
export function getRelateTest(caseId) {
return get('/test/case/relate/test/list/' + caseId);
return get("/test/case/relate/test/list/" + caseId);
}
export function deleteRelateTest(caseId, testId) {
return get('/test/case/relate/delete/' + caseId + '/' + testId);
return get("/test/case/relate/delete/" + caseId + "/" + testId);
}
export function editTestCaseOrder(request) {
return post('/test/case/edit/order', request);
return post("/test/case/edit/order", request);
}
export function getMinderExtraNode(groupId, nodeId) {
return get('/minder/extra/node/list/' + groupId + '/' + nodeId);
return get("/minder/extra/node/list/" + groupId + "/" + nodeId);
}
export function testCaseMinderEdit(param) {
return post(BASE_URL + 'minder/edit', param);
return post(BASE_URL + "minder/edit", param);
}
export function editTestReviewTestCaseOrder(request) {
return post('/test/review/case/edit/order', request);
return post("/test/review/case/edit/order", request);
}
export function getTestCaseNodesByCaseFilter(projectId, param) {
return post('/case/node/list/' + projectId, param);
return post("/case/node/list/" + projectId, param);
}
export function getTestCaseNodesCountMap(projectId, param) {
return post('/case/node/count/' + projectId, param);
return post("/case/node/count/" + projectId, param);
}
export function getTestPlanCaseNodesByCaseFilter(planId, param) {
return post('/case/node/list/plan/' + planId, param);
return post("/case/node/list/plan/" + planId, param);
}
export function getTestReviewCaseNodesByCaseFilter(reviewId, param) {
return post('/case/node/list/review/' + reviewId, param);
return post("/case/node/list/review/" + reviewId, param);
}
export function getTestCasePublicNodes(param) {
return post('/case/node/list/public/' + getCurrentWorkspaceId(), param);
return post("/case/node/list/public/" + getCurrentWorkspaceId(), param);
}
export function getTestCaseTrashNodes(param) {
return post('/case/node/list/trash/' + getCurrentProjectID(), param);
return post("/case/node/list/trash/" + getCurrentProjectID(), param);
}
export function getRelationshipCase(id, relationshipType) {
return get('/test/case/relationship/case/' + id + '/' + relationshipType);
return get("/test/case/relationship/case/" + id + "/" + relationshipType);
}
export function getRelationshipCountCase(id) {
return get('/test/case/relationship/case/count/' + id);
return get("/test/case/relationship/case/count/" + id);
}
export function getTestPlanTestCase(pageNum, pageSize, param) {
return post('/test/plan/case/list/' + pageNum + '/' + pageSize, param);
return post("/test/plan/case/list/" + pageNum + "/" + pageSize, param);
}
export function getTestReviewTestCase(pageNum, pageSize, param) {
return post('/test/review/case/list/' + pageNum + '/' + pageSize, param);
return post("/test/review/case/list/" + pageNum + "/" + pageSize, param);
}
export function getMinderTreeExtraNodeCount(param) {
return post('/case/node/minder/extraNode/count', param);
return post("/case/node/minder/extraNode/count", param);
}
export function getTestCaseIssueList(param) {
@ -242,19 +257,31 @@ export function getTestCaseRelateIssue(pageNum, pageSize, param) {
}
export function getTestCaseRelevanceApiList(pageNum, pageSize, param) {
return post(BASE_URL + "relevance/api/list/" + pageNum + "/" + pageSize, param);
return post(
BASE_URL + "relevance/api/list/" + pageNum + "/" + pageSize,
param
);
}
export function getTestCaseRelevanceScenarioList(pageNum, pageSize, param) {
return post(BASE_URL + "relevance/scenario/list/" + pageNum + "/" + pageSize, param);
return post(
BASE_URL + "relevance/scenario/list/" + pageNum + "/" + pageSize,
param
);
}
export function getTestCaseRelevanceUiScenarioList(pageNum, pageSize, param) {
return post(BASE_URL + "relevance/uiScenario/list/" + pageNum + "/" + pageSize, param);
return post(
BASE_URL + "relevance/uiScenario/list/" + pageNum + "/" + pageSize,
param
);
}
export function getTestCaseRelevanceLoadList(pageNum, pageSize, param) {
return post(BASE_URL + "relevance/load/list/" + pageNum + "/" + pageSize, param);
return post(
BASE_URL + "relevance/load/list/" + pageNum + "/" + pageSize,
param
);
}
export function saveCaseRelevanceApi(caseId, param) {
@ -277,3 +304,6 @@ export function checkProjectPermission(projectId) {
return get(BASE_URL + "check/permission/" + projectId);
}
export function getApiCaseProtocol(id) {
return get("/api/testcase/" + id);
}

View File

@ -51,6 +51,19 @@
min-width="100px"
width="100px"
>
<template v-slot:default="scope">
<span
style="cursor: pointer"
v-if="!isHasPermission(scope.row)"
>
{{ scope.row.num }}
</span>
<el-link @click="openById(scope.row)" v-else>
<span>
{{ scope.row.num }}
</span>
</el-link>
</template>
</ms-table-column>
<ms-table-column
@ -127,11 +140,14 @@ import MsTable from "metersphere-frontend/src/components/new-ui/MsTable";
import MsTableColumn from "metersphere-frontend/src/components/table/MsTableColumn";
import TestCaseApiRelate from "@/business/case/components/case/relate/CaseApiRelate";
import { deleteRelateTest, getRelateTest } from "@/api/testCase";
import { operationConfirm } from "@/business/utils/sdk-utils";
import {hasPermission,} from "metersphere-frontend/src/utils/permission";
import {getUUID} from "metersphere-frontend/src/utils";
import TestCaseScenarioRelate from "@/business/case/components/case/relate/CaseScenarioRelate";
import TestCaseUiScenarioRelate from "@/business/case/components/case/relate/CaseUiScenarioRelate";
import TestCaseLoadRelate from "@/business/case/components/case/relate/CaseLoadRelate";
import TestCaseUiScenarioRelevance from "@/business/plan/view/comonents/ui/TestCaseUiScenarioRelevance";
import {getProject} from "@/api/project";
import {getApiCaseProtocol} from "@/api/testCase";
export default {
name: "CaseTestRelate",
@ -187,6 +203,7 @@ export default {
this.initTable();
},
methods: {
hasPermission,
handleCommand(key) {
if (!this.caseId) {
this.$warning(this.$t("api_test.automation.save_case_info"));
@ -246,6 +263,66 @@ export default {
target = target + "";
return target.indexOf(key) !== -1;
},
isHasPermission(item) {
return (item.testType === 'testcase' && hasPermission("PROJECT_API_DEFINITION:READ+EDIT_CASE")) ||
(item.testType === 'automation' && hasPermission("PROJECT_API_SCENARIO:READ+EDIT")) ||
(item.testType === 'uiAutomation') ||
(item.testType === 'performance');
},
openById(item) {
let projectId = item.projectId;
getProject(projectId).then((rsp) => {
if (rsp.data) {
let workspaceId = rsp.data.workspaceId;
let url;
if (item.testType === "testcase") {
getApiCaseProtocol(item.testId).then((rsp) => {
if (rsp.data) {
url = "/api/definition/default/" +
getUUID() +
"/apiTestCase/single:" +
item.testId +
"/" +
projectId +
"/" +
rsp.data.protocol +
"/" +
workspaceId;
window.open(this.$router.resolve(url).href, "_blank");
}
});
} else if (item.testType === "automation") {
url = "/api/automation/default/" +
getUUID() +
"/scenario/edit:" +
item.testId +
"/" +
projectId +
"/" +
workspaceId;
} else if (item.testType === "uiAutomation") {
url = {
path: "/ui/automation",
query: {
redirectID: getUUID(),
dataType: "scenario",
dataSelectRange: "edit:" + item.testId,
projectId: projectId,
workspaceId: workspaceId,
}
}
} else if (item.testType === "performance") {
url = {
path: '/performance/test/edit/' + item.testId,
query: {projectId: projectId}
}
}
if (url) {
window.open(this.$router.resolve(url).href, "_blank");
}
}
});
},
},
};
</script>