fix(测试计划): 测试计划细节一揽子优化

This commit is contained in:
Jianguo-Genius 2024-06-14 12:30:47 +08:00 committed by Craftsman
parent 1cf7f454d9
commit a65ad3a598
16 changed files with 114 additions and 28 deletions

View File

@ -42,6 +42,29 @@ public class BasePageRequest extends BaseCondition {
return sb.substring(0, sb.length() - 1);
}
public String getSortString(String defaultColumn, String tableAliseName) {
if (sort == null || sort.isEmpty()) {
return null;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : sort.entrySet()) {
String column = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, entry.getKey());
sb.append(tableAliseName)
.append(".")
.append(column)
.append(StringUtils.SPACE)
.append(StringUtils.equalsIgnoreCase(entry.getValue(), "DESC") ? "DESC" : "ASC")
.append(",")
.append(tableAliseName)
.append(".")
.append(defaultColumn)
.append(StringUtils.SPACE)
.append(StringUtils.equalsIgnoreCase(entry.getValue(), "DESC") ? "DESC" : "ASC");
return sb.toString();
}
return null;
}
public String getSortString(String defaultColumn) {
if (sort == null || sort.isEmpty()) {
return null;

View File

@ -5,7 +5,6 @@ import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.definition.ApiReportDTO;
import io.metersphere.api.dto.definition.ApiReportDetailDTO;
import io.metersphere.api.service.definition.ApiReportService;
import io.metersphere.plan.constants.TestPlanResourceConfig;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanApiCasePageResponse;
import io.metersphere.plan.dto.response.TestPlanAssociationResponse;
@ -32,7 +31,6 @@ import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;

View File

@ -53,4 +53,5 @@ public class TestPlanExecuteController {
testPlanManagementService.checkModuleIsOpen(request.getProjectId(), TestPlanResourceConfig.CHECK_TYPE_PROJECT, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN));
testPlanExecuteService.batchExecuteTestPlan(request, SessionUtils.getUserId());
}
}

View File

@ -27,6 +27,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
@ -61,7 +62,9 @@ public class TestPlanFunctionalCaseController {
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ)
@CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan")
public Pager<List<TestPlanCasePageResponse>> page(@Validated @RequestBody TestPlanCaseRequest request) {
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize());
Page<Object> page = PageHelper.startPage(request.getCurrent(), request.getPageSize(),
StringUtils.isNotBlank(request.getSortString("id", "test_plan_functional_case")) ? request.getSortString("id", "test_plan_functional_case") : "test_plan_functional_case.pos asc");
return PageUtils.setPageInfo(page, testPlanFunctionalCaseService.getFunctionalCasePage(request, false));
}

View File

@ -0,0 +1,16 @@
package io.metersphere.plan.dto.request;
import io.metersphere.system.dto.sdk.BasePageRequest;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class TestPlanHistoryPageRequest extends BasePageRequest {
@Schema(description = "测试计划ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{test_plan.id.not_blank}")
private String testPlanId;
}

View File

@ -0,0 +1,25 @@
package io.metersphere.plan.dto.response;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class TestPlanHistoryResponse {
@Schema(description = "报告ID")
private String id;
@Schema(description = "触发方式/执行方式")
private String triggerMode;
@Schema(description = "执行结果")
private String resultStatus;
@Schema(description = "操作人")
private String operator;
@Schema(description = "开始时间")
private Long startTime;
@Schema(description = "结束时间")
private Long endTime;
@Schema(description = "是否还能查看执行报告")
private boolean reportDeleted;
}

View File

@ -61,7 +61,7 @@
resultType="io.metersphere.project.dto.DropNode">
SELECT id, pos
FROM test_plan_api_case
WHERE test_plan_id = #{parentId}
WHERE test_plan_collection_id = #{parentId}
<if test="operator == 'moreThan'">
AND pos &gt; #{pos}
</if>

View File

@ -54,7 +54,7 @@
resultType="io.metersphere.project.dto.DropNode">
SELECT id, pos
FROM test_plan_api_scenario
WHERE test_plan_id = #{parentId}
WHERE test_plan_collection_id = #{parentId}
<if test="operator == 'moreThan'">
AND pos &gt; #{pos}
</if>

View File

@ -75,7 +75,7 @@
resultType="io.metersphere.project.dto.DropNode">
SELECT id, pos
FROM test_plan_functional_case
WHERE test_plan_id = #{parentId}
WHERE test_plan_collection_id = #{parentId}
<if test="operator == 'moreThan'">
AND pos &gt; #{pos}
</if>

View File

@ -581,7 +581,7 @@ public class TestPlanApiCaseService extends TestPlanResourceService {
TestPlanOperationResponse response = new TestPlanOperationResponse();
MoveNodeSortDTO sortDTO = super.getNodeSortDTO(
request.getTestCollectionId(),
super.getNodeMoveRequest(request, true),
super.getNodeMoveRequest(request, false),
extTestPlanApiCaseMapper::selectDragInfoById,
extTestPlanApiCaseMapper::selectNodeByPosOperator
);

View File

@ -245,7 +245,7 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
TestPlanOperationResponse response = new TestPlanOperationResponse();
MoveNodeSortDTO sortDTO = super.getNodeSortDTO(
request.getTestCollectionId(),
super.getNodeMoveRequest(request, true),
super.getNodeMoveRequest(request, false),
extTestPlanApiScenarioMapper::selectDragInfoById,
extTestPlanApiScenarioMapper::selectNodeByPosOperator
);
@ -604,9 +604,8 @@ public class TestPlanApiScenarioService extends TestPlanResourceService {
ids.forEach(id -> {
TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario();
testPlanApiScenario.setId(id);
testPlanApiScenario.setPos(nextOrder.get());
testPlanApiScenario.setPos(nextOrder.getAndAdd(DEFAULT_NODE_INTERVAL_POS));
testPlanApiScenario.setTestPlanCollectionId(targetCollectionId);
nextOrder.addAndGet(DEFAULT_NODE_INTERVAL_POS);
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
});
sqlSession.flushStatements();

View File

@ -205,7 +205,7 @@ public class TestPlanFunctionalCaseService extends TestPlanResourceService {
TestPlanOperationResponse response = new TestPlanOperationResponse();
MoveNodeSortDTO sortDTO = super.getNodeSortDTO(
request.getTestCollectionId(),
super.getNodeMoveRequest(request, true),
super.getNodeMoveRequest(request, false),
extTestPlanFunctionalCaseMapper::selectDragInfoById,
extTestPlanFunctionalCaseMapper::selectNodeByPosOperator
);

View File

@ -16,6 +16,7 @@ import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.*;
import io.metersphere.system.domain.ScheduleExample;
import io.metersphere.system.domain.TestPlanModule;
import io.metersphere.system.domain.TestPlanModuleExample;
import io.metersphere.system.domain.User;
import io.metersphere.system.dto.LogInsertModule;
@ -126,6 +127,11 @@ public class TestPlanService extends TestPlanBaseUtilsService {
private TestPlan savePlanDTO(TestPlanCreateRequest createOrCopyRequest, String operator) {
//检查模块的合法性
checkModule(createOrCopyRequest.getModuleId());
if (StringUtils.equalsIgnoreCase(createOrCopyRequest.getType(), TestPlanConstants.TEST_PLAN_TYPE_GROUP)
&& !StringUtils.equalsIgnoreCase(createOrCopyRequest.getGroupId(), TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID)) {
throw new MSException(Translator.get("test_plan.group.error"));
}
TestPlan createTestPlan = new TestPlan();
BeanUtils.copyBean(createTestPlan, createOrCopyRequest);
validateTestPlanGroup(createTestPlan.getGroupId(), 1);
@ -468,14 +474,20 @@ public class TestPlanService extends TestPlanBaseUtilsService {
public TestPlanDetailResponse detail(String id, String userId) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(id);
TestPlanDetailResponse response = new TestPlanDetailResponse();
String moduleName = getModuleName(testPlan.getModuleId());
String moduleName = Translator.get("unplanned.plan");
if (!ModuleConstants.DEFAULT_NODE_ID.equals(id)) {
TestPlanModule module = testPlanModuleMapper.selectByPrimaryKey(id);
moduleName = module == null ? Translator.get("unplanned.plan") : module.getName();
response.setModuleId(module == null ? ModuleConstants.DEFAULT_NODE_ID : module.getId());
}
//计划组只有几个参数
response.setId(testPlan.getId());
response.setNum(testPlan.getNum());
response.setStatus(testPlan.getStatus());
response.setName(testPlan.getName());
response.setTags(testPlan.getTags());
response.setModuleId(testPlan.getModuleId());
response.setModuleName(moduleName);
response.setDescription(testPlan.getDescription());
response.setType(testPlan.getType());
@ -547,9 +559,10 @@ public class TestPlanService extends TestPlanBaseUtilsService {
public String getModuleName(String id) {
if (ModuleConstants.DEFAULT_NODE_ID.equals(id)) {
return Translator.get("functional_case.module.default.name");
return Translator.get("unplanned.plan");
}
return testPlanModuleMapper.selectByPrimaryKey(id).getName();
TestPlanModule module = testPlanModuleMapper.selectByPrimaryKey(id);
return module == null ? StringUtils.EMPTY : module.getName();
}

View File

@ -151,7 +151,7 @@ public class TestPlanApiCaseControllerTests extends BaseTest {
request.setProjectId("wxx_1234");
request.setMoveId(apiList.getLast().getId());
request.setTargetId(apiList.getFirst().getId());
request.setMoveMode(MoveTypeEnum.AFTER.name());
request.setMoveMode(MoveTypeEnum.BEFORE.name());
MvcResult result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_API_CASE_SORT, request);
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
@ -164,7 +164,7 @@ public class TestPlanApiCaseControllerTests extends BaseTest {
//将这时的第30个放到第一位之后
request.setTargetId(apiList.getLast().getId());
request.setMoveId(apiList.getFirst().getId());
request.setMoveMode(MoveTypeEnum.BEFORE.name());
request.setMoveMode(MoveTypeEnum.AFTER.name());
result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_API_CASE_SORT, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanOperationResponse.class);

View File

@ -24,7 +24,6 @@ import io.metersphere.plan.domain.TestPlanApiScenarioExample;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanOperationResponse;
import io.metersphere.plan.mapper.TestPlanApiScenarioMapper;
import io.metersphere.plan.service.TestPlanApiScenarioLogService;
import io.metersphere.plan.service.TestPlanApiScenarioService;
import io.metersphere.project.api.assertion.MsResponseCodeAssertion;
import io.metersphere.project.api.assertion.MsScriptAssertion;
@ -316,7 +315,7 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
request.setProjectId("wxx_project_1234");
request.setMoveId(scenarioList.getLast().getId());
request.setTargetId(scenarioList.getFirst().getId());
request.setMoveMode(MoveTypeEnum.AFTER.name());
request.setMoveMode(MoveTypeEnum.BEFORE.name());
MvcResult result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_API_SCENARIO_SORT, request);
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
@ -329,7 +328,7 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
//将这时的第30个放到第一位之后
request.setTargetId(scenarioList.getLast().getId());
request.setMoveId(scenarioList.getFirst().getId());
request.setMoveMode(MoveTypeEnum.BEFORE.name());
request.setMoveMode(MoveTypeEnum.AFTER.name());
result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_API_SCENARIO_SORT, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanOperationResponse.class);

View File

@ -10,6 +10,7 @@ import io.metersphere.plan.dto.response.TestPlanOperationResponse;
import io.metersphere.plan.dto.response.TestPlanResponse;
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
import io.metersphere.plan.mapper.ExtTestPlanMapper;
import io.metersphere.plan.mapper.TestPlanFunctionalCaseMapper;
import io.metersphere.plan.mapper.TestPlanMapper;
import io.metersphere.plan.mapper.TestPlanReportMapper;
import io.metersphere.plan.service.*;
@ -147,6 +148,8 @@ public class TestPlanTests extends BaseTest {
private static final String[] PROJECT_MODULE = new String[]{"workstation", "testPlan", "bugManagement", "caseManagement", "apiTest", "uiTest", "loadTest"};
@Resource
private ExtTestPlanMapper extTestPlanMapper;
@Resource
private TestPlanFunctionalCaseMapper testPlanFunctionalCaseMapper;
@BeforeEach
public void initTestData() {
@ -1264,14 +1267,21 @@ public class TestPlanTests extends BaseTest {
if (FUNCTIONAL_CASES.isEmpty()) {
this.testPlanAssociationFunctionCase();
}
String collectionId = IDGenerator.nextStr();
List<TestPlanFunctionalCase> funcList = testPlanTestService.selectTestPlanFunctionalCaseByTestPlanId(repeatCaseTestPlan.getId());
funcList.forEach(item -> {
TestPlanFunctionalCase updateModel = new TestPlanFunctionalCase();
updateModel.setId(item.getId());
updateModel.setTestPlanCollectionId(collectionId);
testPlanFunctionalCaseMapper.updateByPrimaryKeySelective(updateModel);
});
//将第30个移动到第一位之前
ResourceSortRequest request = new ResourceSortRequest();
request.setTestCollectionId(repeatCaseTestPlan.getId());
request.setTestCollectionId(funcList.getFirst().getTestPlanCollectionId());
request.setProjectId(DEFAULT_PROJECT_ID);
request.setMoveId(funcList.get(29).getId());
request.setTargetId(funcList.get(0).getId());
request.setMoveMode(MoveTypeEnum.AFTER.name());
request.setMoveMode(MoveTypeEnum.BEFORE.name());
//恢复
testPlanTestService.resetProjectModule(project, PROJECT_MODULE);
@ -1290,7 +1300,7 @@ public class TestPlanTests extends BaseTest {
//将这时的第30个放到第一位之后
request.setMoveId(funcList.get(29).getId());
request.setTargetId(funcList.get(0).getId());
request.setMoveMode(MoveTypeEnum.BEFORE.name());
request.setMoveMode(MoveTypeEnum.AFTER.name());
result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
response = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), TestPlanOperationResponse.class);
@ -1302,11 +1312,10 @@ public class TestPlanTests extends BaseTest {
new CheckLogModel(request.getMoveId(), OperationLogType.UPDATE, URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT)
);
//再将这时的第30个放到第一位之后,但是第一个的pos为2检查能否触发ref操作
//再将这时的第30个放到第一位之前,但是第一个的pos为2检查能否触发ref操作
request.setMoveId(funcList.get(29).getId());
request.setTargetId(funcList.get(0).getId());
request.setMoveMode(MoveTypeEnum.AFTER.name());
request.setMoveMode(MoveTypeEnum.BEFORE.name());
testPlanTestService.setResourcePos(funcList.get(0).getId(), TestPlanResourceConstants.RESOURCE_FUNCTIONAL_CASE, 2);
result = this.requestPostWithOkAndReturn(URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
@ -1319,11 +1328,11 @@ public class TestPlanTests extends BaseTest {
new CheckLogModel(request.getMoveId(), OperationLogType.UPDATE, URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT)
);
//反例测试计划为空
//反例测试为空
request.setTestCollectionId(null);
this.requestPost(URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT, request).andExpect(status().isBadRequest());
//反例拖拽的节点不存在
request.setTestCollectionId(repeatCaseTestPlan.getId());
request.setTestCollectionId(funcList.getFirst().getTestPlanCollectionId());
request.setMoveId(null);
this.requestPost(URL_POST_RESOURCE_FUNCTIONAL_CASE_SORT, request).andExpect(status().isBadRequest());
//反例目标节点不存在