feat: 测试计划报告初版

This commit is contained in:
chenjianxing 2021-08-11 20:08:06 +08:00 committed by jianxing
parent abb446dbef
commit 3176bd45ee
79 changed files with 2327 additions and 98 deletions

View File

@ -0,0 +1,16 @@
package io.metersphere.api.dto.automation;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TestPlanFailureApiDTO extends TestPlanApiCaseDTO {
private String projectName;
private String caseId;
}

View File

@ -0,0 +1,15 @@
package io.metersphere.api.dto.automation;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@JsonInclude(JsonInclude.Include.NON_NULL)
public class TestPlanFailureScenarioDTO extends ApiScenarioDTO {
private String projectName;
private String caseId;
}

View File

@ -45,7 +45,5 @@ public class TestPlan implements Serializable {
private Boolean automaticStatusUpdate;
private String tags;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,17 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class TestPlanWithBLOBs extends TestPlan implements Serializable {
private String tags;
private String reportSummary;
private static final long serialVersionUID = 1L;
}

View File

@ -2,6 +2,7 @@ package io.metersphere.base.mapper;
import io.metersphere.base.domain.TestPlan;
import io.metersphere.base.domain.TestPlanExample;
import io.metersphere.base.domain.TestPlanWithBLOBs;
import java.util.List;
import org.apache.ibatis.annotations.Param;
@ -12,25 +13,25 @@ public interface TestPlanMapper {
int deleteByPrimaryKey(String id);
int insert(TestPlan record);
int insert(TestPlanWithBLOBs record);
int insertSelective(TestPlan record);
int insertSelective(TestPlanWithBLOBs record);
List<TestPlan> selectByExampleWithBLOBs(TestPlanExample example);
List<TestPlanWithBLOBs> selectByExampleWithBLOBs(TestPlanExample example);
List<TestPlan> selectByExample(TestPlanExample example);
TestPlan selectByPrimaryKey(String id);
TestPlanWithBLOBs selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") TestPlan record, @Param("example") TestPlanExample example);
int updateByExampleSelective(@Param("record") TestPlanWithBLOBs record, @Param("example") TestPlanExample example);
int updateByExampleWithBLOBs(@Param("record") TestPlan record, @Param("example") TestPlanExample example);
int updateByExampleWithBLOBs(@Param("record") TestPlanWithBLOBs record, @Param("example") TestPlanExample example);
int updateByExample(@Param("record") TestPlan record, @Param("example") TestPlanExample example);
int updateByPrimaryKeySelective(TestPlan record);
int updateByPrimaryKeySelective(TestPlanWithBLOBs record);
int updateByPrimaryKeyWithBLOBs(TestPlan record);
int updateByPrimaryKeyWithBLOBs(TestPlanWithBLOBs record);
int updateByPrimaryKey(TestPlan record);
}

View File

@ -23,8 +23,9 @@
<result column="execution_times" jdbcType="INTEGER" property="executionTimes" />
<result column="automatic_status_update" jdbcType="BIT" property="automaticStatusUpdate" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.TestPlan">
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.TestPlanWithBLOBs">
<result column="tags" jdbcType="LONGVARCHAR" property="tags" />
<result column="report_summary" jdbcType="LONGVARCHAR" property="reportSummary" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -90,7 +91,7 @@
actual_start_time, actual_end_time, creator, project_id, execution_times, automatic_status_update
</sql>
<sql id="Blob_Column_List">
tags
tags, report_summary
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.TestPlanExample" resultMap="ResultMapWithBLOBs">
select
@ -140,25 +141,25 @@
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.TestPlan">
<insert id="insert" parameterType="io.metersphere.base.domain.TestPlanWithBLOBs">
insert into test_plan (id, workspace_id, report_id,
`name`, description, `status`,
stage, principal, test_case_match_rule,
executor_match_rule, create_time, update_time,
planned_start_time, planned_end_time, actual_start_time,
actual_end_time, creator, project_id,
execution_times, automatic_status_update, tags
)
execution_times, automatic_status_update, tags,
report_summary)
values (#{id,jdbcType=VARCHAR}, #{workspaceId,jdbcType=VARCHAR}, #{reportId,jdbcType=VARCHAR},
#{name,jdbcType=VARCHAR}, #{description,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR},
#{stage,jdbcType=VARCHAR}, #{principal,jdbcType=VARCHAR}, #{testCaseMatchRule,jdbcType=VARCHAR},
#{executorMatchRule,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{plannedStartTime,jdbcType=BIGINT}, #{plannedEndTime,jdbcType=BIGINT}, #{actualStartTime,jdbcType=BIGINT},
#{actualEndTime,jdbcType=BIGINT}, #{creator,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR},
#{executionTimes,jdbcType=INTEGER}, #{automaticStatusUpdate,jdbcType=BIT}, #{tags,jdbcType=LONGVARCHAR}
)
#{executionTimes,jdbcType=INTEGER}, #{automaticStatusUpdate,jdbcType=BIT}, #{tags,jdbcType=LONGVARCHAR},
#{reportSummary,jdbcType=LONGVARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestPlan">
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestPlanWithBLOBs">
insert into test_plan
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -224,6 +225,9 @@
<if test="tags != null">
tags,
</if>
<if test="reportSummary != null">
report_summary,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -289,6 +293,9 @@
<if test="tags != null">
#{tags,jdbcType=LONGVARCHAR},
</if>
<if test="reportSummary != null">
#{reportSummary,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.TestPlanExample" resultType="java.lang.Long">
@ -363,6 +370,9 @@
<if test="record.tags != null">
tags = #{record.tags,jdbcType=LONGVARCHAR},
</if>
<if test="record.reportSummary != null">
report_summary = #{record.reportSummary,jdbcType=LONGVARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -390,7 +400,8 @@
project_id = #{record.projectId,jdbcType=VARCHAR},
execution_times = #{record.executionTimes,jdbcType=INTEGER},
automatic_status_update = #{record.automaticStatusUpdate,jdbcType=BIT},
tags = #{record.tags,jdbcType=LONGVARCHAR}
tags = #{record.tags,jdbcType=LONGVARCHAR},
report_summary = #{record.reportSummary,jdbcType=LONGVARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -421,7 +432,7 @@
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.TestPlan">
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.TestPlanWithBLOBs">
update test_plan
<set>
<if test="workspaceId != null">
@ -484,10 +495,13 @@
<if test="tags != null">
tags = #{tags,jdbcType=LONGVARCHAR},
</if>
<if test="reportSummary != null">
report_summary = #{reportSummary,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.TestPlan">
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.TestPlanWithBLOBs">
update test_plan
set workspace_id = #{workspaceId,jdbcType=VARCHAR},
report_id = #{reportId,jdbcType=VARCHAR},
@ -508,7 +522,8 @@
project_id = #{projectId,jdbcType=VARCHAR},
execution_times = #{executionTimes,jdbcType=INTEGER},
automatic_status_update = #{automaticStatusUpdate,jdbcType=BIT},
tags = #{tags,jdbcType=LONGVARCHAR}
tags = #{tags,jdbcType=LONGVARCHAR},
report_summary = #{reportSummary,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.TestPlan">

View File

@ -2,6 +2,7 @@ package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.Issues;
import io.metersphere.base.domain.IssuesDao;
import io.metersphere.track.dto.PlanReportIssueDTO;
import io.metersphere.track.request.testcase.IssuesRequest;
import org.apache.ibatis.annotations.Param;
@ -11,11 +12,13 @@ public interface ExtIssuesMapper {
List<IssuesDao> getIssuesByCaseId(@Param("request") IssuesRequest issuesRequest);
List<IssuesDao> getIssuesByProjectId(@Param("request") IssuesRequest issuesRequest);
List<IssuesDao> getIssues(@Param("request") IssuesRequest issuesRequest);
List<IssuesDao> getRelateIssues(@Param("request") IssuesRequest request);
Issues getNextNum(String projectId);
List<IssuesDao> getIssueForSync(String projectId);
List<PlanReportIssueDTO> selectForPlanReport(String planId);
}

View File

@ -12,7 +12,7 @@
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
<select id="getIssuesByProjectId" resultType="io.metersphere.base.domain.IssuesDao">
<select id="getIssues" resultType="io.metersphere.base.domain.IssuesDao">
select issues.id, issues.num, ifnull(issues.title, '') as title, issues.project_id, issues.create_time, issues.update_time,
ifnull(issues.description, '') as description, issues.status, issues.platform, issues.custom_fields, issues.reporter,
issues.creator,issues.resource_id,issues.platform_status,
@ -42,6 +42,9 @@
from issues
where project_id = #{projectId} and platform != 'Local';
</select>
<select id="selectForPlanReport" resultType="io.metersphere.track.dto.PlanReportIssueDTO">
select id,status,platform_status,platform from issues where resource_id = #{planId} and platform_status != 'delete';;
</select>
<sql id="queryWhereCondition">
<where>
@ -57,6 +60,10 @@
and issues.project_id = #{request.projectId}
</if>
<if test="request.resourceId != null and request.resourceId != ''">
and issues.resource_id = #{request.resourceId}
</if>
<if test="request.testCaseId != null and request.testCaseId != ''">
and test_case_issues.test_case_id = #{request.testCaseId}
</if>

View File

@ -1,10 +1,13 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.base.domain.Project;
import io.metersphere.controller.request.ProjectRequest;
import io.metersphere.dto.ProjectDTO;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
public interface ExtProjectMapper {
@ -21,4 +24,7 @@ public interface ExtProjectMapper {
List<String> getProjectIds();
String getMaxSystemId();
@MapKey("id")
Map<String, Project> queryNameByIds(@Param("ids") List<String> ids);
}

View File

@ -117,7 +117,15 @@
<select id="getMaxSystemId" resultType="java.lang.String">
SELECT max(system_id) FROM project
</select>
<select id="queryNameByIds" resultType="io.metersphere.base.domain.Project">
select id, name, custom_num
from project
WHERE id IN
<foreach collection="ids" item="id" index="index"
open="(" close=")" separator=",">
#{id}
</foreach>
</select>
<update id="removeIssuePlatform">
update project

View File

@ -1,9 +1,11 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.automation.TestPlanFailureApiDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.track.dto.PlanReportCaseDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -25,5 +27,11 @@ public interface ExtTestPlanApiCaseMapper {
ApiTestCaseWithBLOBs getApiTestCaseById(String testPlanApiCaseId);
List<TestPlanApiCase> selectLegalDataByTestPlanId(String planId);
}
List<PlanReportCaseDTO> selectForPlanReport(String planId);
List<TestPlanFailureApiDTO> getFailureList(String planId);
}

View File

@ -245,5 +245,22 @@
<select id="getStatusByTestPlanId" resultType="java.lang.String">
SELECT `status` FROM test_plan_api_case WHERE test_plan_id = #{0}
</select>
<select id="selectForPlanReport" resultType="io.metersphere.track.dto.PlanReportCaseDTO">
select id,status from test_plan_api_case where test_plan_id = #{planId};
</select>
<select id="getFailureList" resultType="io.metersphere.api.dto.automation.TestPlanFailureApiDTO">
select
t.id,
c.id as case_id, c.project_id, c.name, c.api_definition_id, c.priority, c.create_user_id,
c.num,t.create_user,
t.status execResult
from
test_plan_api_case t
inner join
api_test_case c
on t.api_case_id = c.id
and t.test_plan_id = #{request.planId} and t.status = 'error'
where t.test_plan_id = #{planId};
</select>
</mapper>

View File

@ -1,5 +1,6 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.track.dto.PlanReportCaseDTO;
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import io.metersphere.track.request.testplan.LoadCaseRequest;
import org.apache.ibatis.annotations.Param;
@ -20,4 +21,8 @@ public interface ExtTestPlanLoadCaseMapper {
List<String> getStatusByTestPlanId(@Param("planId") String planId);
List<String> selectTestPlanLoadCaseId(@Param("request") LoadCaseRequest request);
List<PlanReportCaseDTO> selectForPlanReport(String planId);
List<TestPlanLoadCaseDTO> getFailureCases(String planId);
}

View File

@ -175,4 +175,14 @@
<select id="getStatusByTestPlanId" resultType="java.lang.String">
select status from test_plan_load_case tplc where tplc.test_plan_id = #{planId}
</select>
<select id="selectForPlanReport" resultType="io.metersphere.track.dto.PlanReportCaseDTO">
select id,status from test_plan_load_case where test_plan_id = #{planId};
</select>
<select id="getFailureCases" resultType="io.metersphere.track.dto.TestPlanLoadCaseDTO">
select tplc.id, lt.id as caseId, lt.name, lt.num, lt.project_id,
tplc.status ,tplc.create_user
from test_plan_load_case tplc
inner join load_test lt on tplc.load_case_id = lt.id and tplc.status = 'error'
where tplc.test_plan_id = #{planId}
</select>
</mapper>

View File

@ -1,8 +1,10 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.TestPlanFailureScenarioDTO;
import io.metersphere.api.dto.automation.TestPlanScenarioRequest;
import io.metersphere.base.domain.TestPlanApiScenario;
import io.metersphere.track.dto.PlanReportCaseDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@ -23,4 +25,10 @@ public interface ExtTestPlanScenarioCaseMapper {
List<TestPlanApiScenario> selectByIds(@Param("ids")String ids ,@Param("oderId")String oderId );
List<TestPlanApiScenario> selectLegalDataByTestPlanId(String planId);
}
List<PlanReportCaseDTO> selectForPlanReport(String planId);
List<TestPlanFailureScenarioDTO> getFailureList(String planId);
List<Integer> getUnderwaySteps(@Param("ids") List<String> underwayIds);
}

View File

@ -195,5 +195,28 @@
</if>
where t.test_plan_id = #{planId}
</select>
<select id="selectForPlanReport" resultType="io.metersphere.track.dto.PlanReportCaseDTO">
select id,last_result as status, report_id, api_scenario_id as caseId from test_plan_api_scenario where test_plan_id = #{planId};
</select>
<select id="getFailureList" resultType="io.metersphere.api.dto.automation.TestPlanFailureScenarioDTO">
select
t.id, t.last_result, t.report_id, c.user_id, c.module_path, c.name, c.level,
c.status,c.step_total, c.step_total, c.project_id,
c.num, c.custom_num
from
test_plan_api_scenario t
inner join
api_scenario c
on t.api_scenario_id = c.id and c.status != 'Trash'
and t.test_plan_id = #{request.planId} and t.last_result = 'Fail'
where t.test_plan_id = #{request.planId}
</select>
<select id="getUnderwaySteps" resultType="java.lang.Integer">
select step_total from api_scenario c
where c.id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
</mapper>

View File

@ -1,6 +1,7 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.controller.request.BaseQueryRequest;
import io.metersphere.track.dto.PlanReportCaseDTO;
import io.metersphere.track.dto.TestCaseReportStatusResultDTO;
import io.metersphere.track.dto.TestCaseTestDTO;
import io.metersphere.track.dto.TestPlanCaseDTO;
@ -57,4 +58,8 @@ public interface ExtTestPlanTestCaseMapper {
List<String> selectIdsByQuery(@Param("request") BaseQueryRequest query);
void update(@Param("count") int count, @Param("id") String id, @Param("caseId") String caseId, @Param("issues") String issues);
List<PlanReportCaseDTO> selectForPlanReport(String planId);
List<TestPlanCaseDTO> getFailureCases(String planId);
}

View File

@ -463,6 +463,19 @@
<include refid="queryWhereCondition"/>
<include refid="io.metersphere.base.mapper.ext.ExtBaseMapper.orders"/>
</select>
<select id="selectForPlanReport" resultType="io.metersphere.track.dto.PlanReportCaseDTO">
select id,status from test_plan_test_case where plan_id = #{planId};
</select>
<select id="getFailureCases" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
select tptc.id, tc.id as caseId, tc.name, tc.priority, tc.num, tc.custom_num, tc.project_id,
tc.node_id, tc.tags, tptc.actual_result,
tptc.update_time, tptc.create_time,
tptc.issues_count,
tptc.plan_id, tptc.executor
from test_plan_test_case tptc
inner join test_case tc on tptc.case_id = tc.id and tptc.status = 'Failure'
where tptc.plan_id = #{planId}
</select>
<update id="updateTestCaseStates" parameterType="java.lang.String">
update test_plan_test_case
<set>

View File

@ -1,9 +1,11 @@
package io.metersphere.commons.utils;
import io.metersphere.base.domain.Project;
import io.metersphere.base.domain.User;
import io.metersphere.commons.exception.MSException;
import io.metersphere.controller.request.BaseQueryRequest;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.service.ProjectService;
import io.metersphere.service.UserService;
import org.apache.commons.collections.CollectionUtils;
@ -72,4 +74,22 @@ public class ServiceUtils {
});
return nameMap;
}
public static Map<String, Project> getProjectMap(List<String> ids) {
ProjectService projectService = CommonBeanFactory.getBean(ProjectService.class);
if (!CollectionUtils.isEmpty(ids)) {
Map<String, Project> projectMap = projectService.queryNameByIds(ids);
return projectMap;
}
return new HashMap<>();
}
public static Map<String, String> getProjectNameMap(List<String> ids) {
Map<String, Project> projectMap = getProjectMap(ids);
HashMap<String, String> nameMap = new HashMap<>();
projectMap.forEach((k, v) -> {
nameMap.put(k, v.getName());
});
return nameMap;
}
}

View File

@ -0,0 +1,64 @@
package io.metersphere.commons.utils;
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
import io.metersphere.track.dto.PlanReportCaseDTO;
import io.metersphere.track.dto.TestCaseReportStatusResultDTO;
import io.metersphere.track.dto.TestPlanSimpleReportDTO;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
public class TestPlanUtils {
public static void getStatusResultMap(Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap, String result) {
if (StringUtils.isBlank(result)) {
result = TestPlanTestCaseStatus.Prepare.name();
}
TestCaseReportStatusResultDTO statusResult = reportStatusResultMap.get(result);
if (statusResult == null) {
statusResult = new TestCaseReportStatusResultDTO();
statusResult.setStatus(result);
statusResult.setCount(0);
}
statusResult.setCount(statusResult.getCount() + 1);
reportStatusResultMap.put(result, statusResult);
}
public static void addToReportStatusResultList(Map<String, TestCaseReportStatusResultDTO> resultMap,
List<TestCaseReportStatusResultDTO> reportStatusResultList, String status) {
if (resultMap.get(status) != null) {
reportStatusResultList.add(resultMap.get(status));
} else if (StringUtils.isBlank(status) && resultMap.get(TestPlanTestCaseStatus.Prepare.name()) != null) {
reportStatusResultList.add(resultMap.get(TestPlanTestCaseStatus.Prepare.name()));
}
}
public static void addToReportCommonStatusResultList(Map<String, TestCaseReportStatusResultDTO> resultMap,
List<TestCaseReportStatusResultDTO> statusResult) {
addToReportStatusResultList(resultMap, statusResult, TestPlanTestCaseStatus.Pass.name());
addToReportStatusResultList(resultMap, statusResult, TestPlanTestCaseStatus.Failure.name());
addToReportStatusResultList(resultMap, statusResult, "error");
addToReportStatusResultList(resultMap, statusResult, "Fail");
addToReportStatusResultList(resultMap, statusResult, "success");
addToReportStatusResultList(resultMap, statusResult, "Success");
addToReportStatusResultList(resultMap, statusResult, TestPlanTestCaseStatus.Prepare.name());
}
public static void calculatePlanReport(List<PlanReportCaseDTO> planReportCaseDTOS,
Map<String, TestCaseReportStatusResultDTO> statusResultMap,
TestPlanSimpleReportDTO report, String successStatus) {
planReportCaseDTOS.forEach(item -> {
report.setCaseCount(report.getCaseCount() + 1);
String status = item.getStatus();
if (StringUtils.isNotBlank(status) && !StringUtils.equals(TestPlanTestCaseStatus.Underway.name(), status)) {
report.setExecuteCount(report.getExecuteCount() + 1);
if (StringUtils.equals(successStatus, status)) {
report.setPassCount(report.getPassCount() + 1);
}
}
TestPlanUtils.getStatusResultMap(statusResultMap, status);
});
}
}

View File

@ -40,10 +40,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
@Service
@ -482,4 +479,8 @@ public class ProjectService {
public List<String> getProjectIds() {
return extProjectMapper.getProjectIds();
}
public Map<String, Project> queryNameByIds(List<String> ids) {
return extProjectMapper.queryNameByIds(ids);
}
}

View File

@ -55,6 +55,11 @@ public class IssuesController {
return issuesService.getIssues(id);
}
@GetMapping("/plan/get/{planId}")
public List<IssuesDao> getIssuesByPlanoId(@PathVariable String planId) {
return issuesService.getIssuesByPlanoId(planId);
}
@GetMapping("/auth/{orgId}/{platform}")
public void testAuth(@PathVariable String orgId, @PathVariable String platform) {
issuesService.testAuth(orgId, platform);

View File

@ -2,6 +2,7 @@ package io.metersphere.track.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.automation.TestPlanFailureApiDTO;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
@ -32,6 +33,11 @@ public class TestPlanApiCaseController {
return PageUtils.setPageInfo(page, testPlanApiCaseService.list(request));
}
@GetMapping("/list/failure/{planId}")
public List<TestPlanFailureApiDTO> getFailureList(@PathVariable String planId) {
return testPlanApiCaseService.getFailureList(planId);
}
@PostMapping("/selectAllTableRows")
public List<TestPlanApiCaseDTO> selectAllTableRows(@RequestBody TestPlanApiCaseBatchRequest request) {
return testPlanApiCaseService.selectAllTableRows(request);

View File

@ -5,16 +5,14 @@ import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.Project;
import io.metersphere.base.domain.TestPlan;
import io.metersphere.base.domain.TestPlanWithBLOBs;
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.log.annotation.MsAuditLog;
import io.metersphere.service.CheckPermissionService;
import io.metersphere.track.dto.ApiRunConfigDTO;
import io.metersphere.track.dto.TestCaseReportMetricDTO;
import io.metersphere.track.dto.TestPlanDTO;
import io.metersphere.track.dto.TestPlanDTOWithMetric;
import io.metersphere.track.dto.*;
import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest;
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
import io.metersphere.track.request.testplan.AddTestPlanRequest;
@ -173,4 +171,19 @@ public class TestPlanController {
public void exportHtmlReport(@PathVariable String planId, HttpServletResponse response) throws UnsupportedEncodingException {
testPlanService.exportPlanReport(planId, response);
}
@GetMapping("/report/{planId}")
public TestPlanSimpleReportDTO getReport(@PathVariable String planId){
return testPlanService.getReport(planId);
}
@GetMapping("/report/functional/result")
public TestCaseReportStatusResultDTO getFunctionalResultReport(@PathVariable String planId){
return testPlanService.getFunctionalResultReport(planId);
}
@PostMapping("/edit/report")
public void editReport(@RequestBody TestPlanWithBLOBs testPlanWithBLOBs) {
testPlanService.editReport(testPlanWithBLOBs);
}
}

View File

@ -90,4 +90,9 @@ public class TestPlanLoadCaseController {
public void updateByApi(@RequestBody TestPlanLoadCase testPlanLoadCase) {
testPlanLoadCaseService.updateByApi(testPlanLoadCase);
}
@GetMapping("/list/failure/{planId}")
public List<TestPlanLoadCaseDTO> getFailureCases(@PathVariable String planId) {
return testPlanLoadCaseService.getFailureCases(planId);
}
}

View File

@ -31,6 +31,11 @@ public class TestPlanScenarioCaseController {
return PageUtils.setPageInfo(page, testPlanScenarioCaseService.list(request));
}
@GetMapping("/list/failure/{planId}")
public List<TestPlanFailureScenarioDTO> getFailureList(@PathVariable String planId) {
return testPlanScenarioCaseService.getFailureList(planId);
}
@PostMapping("/selectAllTableRows")
public List<ApiScenarioDTO> selectAllTableRows(@RequestBody TestPlanScenarioCaseBatchRequest request) {
return testPlanScenarioCaseService.selectAllTableRows(request);

View File

@ -130,4 +130,8 @@ public class TestPlanTestCaseController {
return testPlanTestCaseService.deleteTestCase(id);
}
@GetMapping("/list/failure/{planId}")
public List<TestPlanCaseDTO> getFailureCases(@PathVariable String planId) {
return testPlanTestCaseService.getFailureCases(planId);
}
}

View File

@ -0,0 +1,13 @@
package io.metersphere.track.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PlanReportCaseDTO {
public String id;
public String status;
public String reportId;
public String caseId;
}

View File

@ -0,0 +1,11 @@
package io.metersphere.track.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class PlanReportIssueDTO extends PlanReportCaseDTO {
private String platformStatus;
private String platform;
}

View File

@ -0,0 +1,15 @@
package io.metersphere.track.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class TestPlanApiResultReportDTO {
private List<TestCaseReportStatusResultDTO> apiCaseData;
private List<TestCaseReportStatusResultDTO> apiScenarioData;
private List<TestCaseReportStatusResultDTO> apiScenarioStepData;
}

View File

@ -22,6 +22,7 @@ public class TestPlanCaseDTO extends TestCaseWithBLOBs {
private String projectName;
private String actualResult;
private String maintainerName;
private Boolean isCustomNum;
private int issuesCount;
private List<TestCaseTestDTO> list;

View File

@ -1,6 +1,6 @@
package io.metersphere.track.dto;
import io.metersphere.base.domain.TestPlan;
import io.metersphere.base.domain.TestPlanWithBLOBs;
import lombok.Getter;
import lombok.Setter;
@ -8,7 +8,7 @@ import java.util.List;
@Getter
@Setter
public class TestPlanDTO extends TestPlan {
public class TestPlanDTO extends TestPlanWithBLOBs {
private String projectName;
private String userName;
private List<String> projectIds;

View File

@ -0,0 +1,14 @@
package io.metersphere.track.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class TestPlanFunctionResultReportDTO {
private List<TestCaseReportStatusResultDTO> caseData;
private List<TestCaseReportStatusResultDTO> issueData;
}

View File

@ -12,4 +12,5 @@ public class TestPlanLoadCaseDTO extends TestPlanLoadCase {
private String projectName;
private String caseStatus;
private String num;
private String name;
}

View File

@ -0,0 +1,13 @@
package io.metersphere.track.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class TestPlanLoadResultReportDTO {
private List<TestCaseReportStatusResultDTO> caseData;
}

View File

@ -0,0 +1,18 @@
package io.metersphere.track.dto;
import lombok.Getter;
import lombok.Setter;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
public class TestPlanScenarioStepCountDTO {
private int scenarioStepTotal;
private int scenarioStepSuccess;
private int scenarioStepError;
private List<String> underwayIds = new ArrayList<>();
private int scenarioStepUnderway;
}

View File

@ -0,0 +1,21 @@
package io.metersphere.track.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TestPlanSimpleReportDTO {
private Long startTime;
private Long endTime;
private int caseCount;
private int executeCount;
private int passCount;
private double executeRate;
private double passRate;
private String summary;
private TestPlanFunctionResultReportDTO functionResult;
private TestPlanApiResultReportDTO apiResult;
private TestPlanLoadResultReportDTO loadResult;
}

View File

@ -73,7 +73,7 @@ public class JiraPlatform extends AbstractIssuePlatform {
issuesRequest.setPlatform(IssuesManagePlatform.Jira.toString());
List<IssuesDao> issues;
if (StringUtils.isNotBlank(issuesRequest.getProjectId())) {
issues = extIssuesMapper.getIssuesByProjectId(issuesRequest);
issues = extIssuesMapper.getIssues(issuesRequest);
} else {
issues = extIssuesMapper.getIssuesByCaseId(issuesRequest);
}

View File

@ -27,7 +27,7 @@ public class LocalPlatform extends LocalAbstractPlatform {
String projectId = issuesRequest.getProjectId();
issuesRequest.setPlatform(IssuesManagePlatform.Local.toString());
if (StringUtils.isNotBlank(projectId)) {
return extIssuesMapper.getIssuesByProjectId(issuesRequest);
return extIssuesMapper.getIssues(issuesRequest);
}
return extIssuesMapper.getIssuesByCaseId(issuesRequest);
}

View File

@ -56,7 +56,7 @@ public class TapdPlatform extends AbstractIssuePlatform {
issuesRequest.setPlatform(IssuesManagePlatform.Tapd.toString());
List<IssuesDao> issues;
if (StringUtils.isNotBlank(issuesRequest.getProjectId())) {
issues = extIssuesMapper.getIssuesByProjectId(issuesRequest);
issues = extIssuesMapper.getIssues(issuesRequest);
} else {
issues = extIssuesMapper.getIssuesByCaseId(issuesRequest);
}

View File

@ -84,7 +84,7 @@ public class ZentaoPlatform extends AbstractIssuePlatform {
issuesRequest.setPlatform(IssuesManagePlatform.Zentao.toString());
List<IssuesDao> issues;
if (StringUtils.isNotBlank(issuesRequest.getProjectId())) {
issues = extIssuesMapper.getIssuesByProjectId(issuesRequest);
issues = extIssuesMapper.getIssues(issuesRequest);
} else {
issues = extIssuesMapper.getIssuesByCaseId(issuesRequest);
}

View File

@ -29,6 +29,7 @@ public class IssuesRequest extends BaseQueryRequest {
*/
private String id;
private String caseId;
private String resourceId;
private String platform;
private String customFields;
private List<String> testCaseIds;

View File

@ -1,6 +1,6 @@
package io.metersphere.track.request.testplan;
import io.metersphere.base.domain.TestPlan;
import io.metersphere.base.domain.TestPlanWithBLOBs;
import lombok.Getter;
import lombok.Setter;
@ -8,6 +8,6 @@ import java.util.List;
@Getter
@Setter
public class AddTestPlanRequest extends TestPlan {
public class AddTestPlanRequest extends TestPlanWithBLOBs {
private List<String> projectIds;
}

View File

@ -7,13 +7,12 @@ import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtIssuesMapper;
import io.metersphere.commons.constants.IssuesManagePlatform;
import io.metersphere.commons.constants.IssuesStatus;
import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.IntegrationRequest;
import io.metersphere.i18n.Translator;
import io.metersphere.log.utils.ReflexObjectUtil;
@ -26,6 +25,7 @@ import io.metersphere.service.IntegrationService;
import io.metersphere.service.IssueTemplateService;
import io.metersphere.service.ProjectService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.track.dto.*;
import io.metersphere.track.issue.*;
import io.metersphere.track.issue.domain.PlatformUser;
import io.metersphere.track.issue.domain.zentao.ZentaoBuild;
@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;
@ -181,16 +182,13 @@ public class IssuesService {
}
public List<IssuesDao> getIssuesByProject(IssuesRequest issueRequest, Project project) {
List<IssuesDao> list = new ArrayList<>();
List<String> platforms = getPlatforms(project);
platforms.add(IssuesManagePlatform.Local.toString());
List<AbstractIssuePlatform> platformList = IssueFactory.createPlatforms(platforms, issueRequest);
platformList.forEach(platform -> {
List<IssuesDao> issue = platform.getIssue(issueRequest);
list.addAll(issue);
});
return list;
List<IssuesDao> issues;
if (StringUtils.isNotBlank(issueRequest.getProjectId())) {
issues = extIssuesMapper.getIssues(issueRequest);
} else {
issues = extIssuesMapper.getIssuesByCaseId(issueRequest);
}
return issues;
}
public String getPlatformsByCaseId(String caseId) {
@ -343,7 +341,7 @@ public class IssuesService {
public List<IssuesDao> list(IssuesRequest request) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
List<IssuesDao> issues = extIssuesMapper.getIssuesByProjectId(request);
List<IssuesDao> issues = extIssuesMapper.getIssues(request);
List<String> ids = issues.stream()
.map(IssuesDao::getCreator)
@ -548,4 +546,37 @@ public class IssuesService {
AbstractIssuePlatform abstractPlatform = IssueFactory.createPlatform(authUserIssueRequest.getPlatform(), issuesRequest);
abstractPlatform.userAuth(authUserIssueRequest);
}
public void calculatePlanReport(String planId, TestPlanSimpleReportDTO report) {
List<PlanReportIssueDTO> planReportIssueDTOS = extIssuesMapper.selectForPlanReport(planId);
TestPlanFunctionResultReportDTO functionResult = report.getFunctionResult();
List<TestCaseReportStatusResultDTO> statusResult = new ArrayList<>();
Map<String, TestCaseReportStatusResultDTO> statusResultMap = new HashMap<>();
planReportIssueDTOS.forEach(item -> {
String status = null;
// 本地缺陷
if (StringUtils.equalsIgnoreCase(item.getPlatform(), IssuesManagePlatform.Local.name())
|| StringUtils.isBlank(item.getPlatform())) {
status = item.getStatus();
} else {
status = item.getPlatformStatus();
}
if (StringUtils.isBlank(status)) {
status = IssuesStatus.NEW.toString();
}
TestPlanUtils.getStatusResultMap(statusResultMap, status);
});
Set<String> status = statusResultMap.keySet();
status.forEach(item -> {
TestPlanUtils.addToReportStatusResultList(statusResultMap, statusResult, item);
});
functionResult.setIssueData(statusResult);
}
public List<IssuesDao> getIssuesByPlanoId(String planId) {
IssuesRequest issueRequest = new IssuesRequest();
issueRequest.setResourceId(planId);
return extIssuesMapper.getIssues(issueRequest);
}
}

View File

@ -1,10 +1,7 @@
package io.metersphere.track.service;
import com.alibaba.fastjson.JSON;
import io.metersphere.base.domain.TestCaseReport;
import io.metersphere.base.domain.TestCaseReportExample;
import io.metersphere.base.domain.TestCaseReportTemplate;
import io.metersphere.base.domain.TestPlan;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.TestCaseReportMapper;
import io.metersphere.base.mapper.TestCaseReportTemplateMapper;
import io.metersphere.base.mapper.TestPlanMapper;
@ -69,7 +66,7 @@ public class TestCaseReportService {
TestCaseReportTemplate template = testCaseReportTemplateMapper.selectByPrimaryKey(request.getTemplateId());
TestCaseReport report = new TestCaseReport();
BeanUtils.copyBean(report, template);
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getPlanId());
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(request.getPlanId());
report.setName(testPlan.getName());
report.setId(request.getId());
report.setCreateUser(SessionUtils.getUserId());

View File

@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.RunModeDataDTO;
import io.metersphere.api.dto.automation.TestPlanFailureApiDTO;
import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
@ -29,15 +30,13 @@ import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.TestPlanApiCaseMapper;
import io.metersphere.base.mapper.TestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.RunModeConstants;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.service.SystemParameterService;
import io.metersphere.track.dto.*;
import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest;
import io.metersphere.track.service.task.ParallelApiExecTask;
import io.metersphere.track.service.task.SerialApiExecTask;
@ -496,4 +495,27 @@ public class TestPlanApiCaseService {
public ApiTestCaseWithBLOBs getApiTestCaseById(String testPlanApiCaseId) {
return extTestPlanApiCaseMapper.getApiTestCaseById(testPlanApiCaseId);
}
public void calculatePlanReport(String planId, TestPlanSimpleReportDTO report) {
List<PlanReportCaseDTO> planReportCaseDTOS = extTestPlanApiCaseMapper.selectForPlanReport(planId);
TestPlanApiResultReportDTO apiResult = report.getApiResult();
List<TestCaseReportStatusResultDTO> statusResult = new ArrayList<>();
Map<String, TestCaseReportStatusResultDTO> statusResultMap = new HashMap<>();
TestPlanUtils.calculatePlanReport(planReportCaseDTOS, statusResultMap, report, "success");
TestPlanUtils.addToReportCommonStatusResultList(statusResultMap, statusResult);
apiResult.setApiCaseData(statusResult);
}
public List<TestPlanFailureApiDTO> getFailureList(String planId) {
List<TestPlanFailureApiDTO> apiTestCases = extTestPlanApiCaseMapper.getFailureList(planId);
if (CollectionUtils.isEmpty(apiTestCases)) {
return apiTestCases;
}
buildUserInfo(apiTestCases);
return apiTestCases;
}
}

View File

@ -10,12 +10,14 @@ import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper;
import io.metersphere.commons.constants.RunModeConstants;
import io.metersphere.commons.constants.TestPlanStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.TestPlanUtils;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.performance.request.RunTestPlanRequest;
import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import io.metersphere.track.dto.*;
import io.metersphere.track.request.testplan.LoadCaseReportBatchRequest;
import io.metersphere.track.request.testplan.LoadCaseReportRequest;
import io.metersphere.track.request.testplan.LoadCaseRequest;
@ -23,18 +25,15 @@ import io.metersphere.track.request.testplan.RunBatchTestPlanRequest;
import io.metersphere.track.service.utils.ParallelExecTask;
import io.metersphere.track.service.utils.SerialExecTask;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.apache.commons.lang3.StringUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@ -351,4 +350,30 @@ public class TestPlanLoadCaseService {
example.createCriteria().andTestPlanIdEqualTo(planId);
testPlanLoadCaseMapper.deleteByExample(example);
}
public void calculatePlanReport(String planId, TestPlanSimpleReportDTO report) {
List<PlanReportCaseDTO> planReportCaseDTOS = extTestPlanLoadCaseMapper.selectForPlanReport(planId);
TestPlanLoadResultReportDTO loadResult = new TestPlanLoadResultReportDTO();
report.setLoadResult(loadResult);
List<TestCaseReportStatusResultDTO> statusResult = new ArrayList<>();
Map<String, TestCaseReportStatusResultDTO> statusResultMap = new HashMap<>();
TestPlanUtils.calculatePlanReport(planReportCaseDTOS, statusResultMap, report, "success");
TestPlanUtils.addToReportCommonStatusResultList(statusResultMap, statusResult);
loadResult.setCaseData(statusResult);
}
public List<TestPlanLoadCaseDTO> getFailureCases(String planId) {
List<TestPlanLoadCaseDTO> failureCases = extTestPlanLoadCaseMapper.getFailureCases(planId);
// Map<String, Project> projectMap = ServiceUtils.getProjectMap(
// failureCases.stream().map(TestPlanCaseDTO::getProjectId).collect(Collectors.toList()));
Map<String, String> userNameMap = ServiceUtils.getUserNameMap(
failureCases.stream().map(TestPlanLoadCaseDTO::getCreateUser).collect(Collectors.toList()));
failureCases.forEach(item -> {
// item.setProjectName(projectMap.get(item.getProjectId()).getName());
item.setUserName(userNameMap.get(item.getCreateUser()));
});
return failureCases;
}
}

View File

@ -173,7 +173,7 @@ public class TestPlanReportService {
* saveRequest.performanceIsExecuting 性能案例是否执行中
*/
public TestPlanReport genTestPlanReport(TestPlanReportSaveRequest saveRequest) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(saveRequest.getPlanId());
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(saveRequest.getPlanId());
String testPlanReportID = saveRequest.getReportID();
TestPlanReport testPlanReport = new TestPlanReport();
testPlanReport.setTestPlanId(saveRequest.getPlanId());
@ -916,7 +916,7 @@ public class TestPlanReportService {
if (!report.getIsApiCaseExecuting() && !report.getIsPerformanceExecuting() && !report.getIsScenarioExecuting()) {
try {
//更新TestPlan状态为完成
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(report.getTestPlanId());
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(report.getTestPlanId());
if (testPlan != null) {
testPlan.setStatus(TestPlanStatus.Completed.name());
testPlanMapper.updateByPrimaryKeySelective(testPlan);

View File

@ -1,6 +1,7 @@
package io.metersphere.track.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.automation.*;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.api.service.ApiScenarioReportService;
@ -12,10 +13,12 @@ import io.metersphere.base.mapper.TestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.TestPlanUtils;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.service.ProjectService;
import io.metersphere.track.dto.RelevanceScenarioRequest;
import io.metersphere.track.dto.*;
import io.metersphere.track.request.testcase.TestPlanScenarioCaseBatchRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@ -51,6 +54,15 @@ public class TestPlanScenarioCaseService {
request.setProjectId(null);
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
List<ApiScenarioDTO> apiTestCases = extTestPlanScenarioCaseMapper.list(request);
if (CollectionUtils.isEmpty(apiTestCases)) {
return apiTestCases;
}
buildProjectInfo(apiTestCases);
buildUserInfo(apiTestCases);
return apiTestCases;
}
public void buildProjectInfo(List<? extends ApiScenarioDTO> apiTestCases) {
List<String> projectIds = apiTestCases.stream()
.map(ApiScenarioDTO::getProjectId)
.collect(Collectors.toSet())
@ -67,12 +79,6 @@ public class TestPlanScenarioCaseService {
item.setCustomNum(item.getNum().toString());
}
});
if (CollectionUtils.isEmpty(apiTestCases)) {
return apiTestCases;
}
buildUserInfo(apiTestCases);
return apiTestCases;
}
public void buildUserInfo(List<? extends ApiScenarioDTO> apiTestCases) {
@ -321,4 +327,80 @@ public class TestPlanScenarioCaseService {
}
return envMap;
}
public void calculatePlanReport(String planId, TestPlanSimpleReportDTO report) {
List<PlanReportCaseDTO> planReportCaseDTOS = extTestPlanScenarioCaseMapper.selectForPlanReport(planId);
TestPlanApiResultReportDTO apiResult = report.getApiResult();
List<TestCaseReportStatusResultDTO> statusResult = new ArrayList<>();
Map<String, TestCaseReportStatusResultDTO> statusResultMap = new HashMap<>();
TestPlanUtils.calculatePlanReport(planReportCaseDTOS, statusResultMap, report, "Success");
TestPlanUtils.addToReportCommonStatusResultList(statusResultMap, statusResult);
TestPlanScenarioStepCountDTO stepCount = new TestPlanScenarioStepCountDTO();
for (PlanReportCaseDTO item : planReportCaseDTOS) {
calculateScenarioResultDTO(item, stepCount);
}
int underwayStepsCounts = getUnderwayStepsCounts(stepCount.getUnderwayIds());
List<TestCaseReportStatusResultDTO> stepResult = new ArrayList<>();
getScenarioCaseReportStatusResultDTO(TestPlanTestCaseStatus.Failure.name(), stepCount.getScenarioStepError(), stepResult);
getScenarioCaseReportStatusResultDTO(TestPlanTestCaseStatus.Pass.name(), stepCount.getScenarioStepSuccess(), stepResult);
getScenarioCaseReportStatusResultDTO(TestPlanTestCaseStatus.Underway.name(),
stepCount.getScenarioStepTotal() - stepCount.getScenarioStepSuccess() - stepCount.getScenarioStepError() + underwayStepsCounts, stepResult);
apiResult.setApiScenarioData(statusResult);
apiResult.setApiScenarioStepData(stepResult);
}
private int getUnderwayStepsCounts(List<String> underwayIds){
if (CollectionUtils.isNotEmpty(underwayIds)) {
List<Integer> underwayStepsCounts = extTestPlanScenarioCaseMapper.getUnderwaySteps(underwayIds);
Optional<Integer> underwayStepCount = underwayStepsCounts.stream().reduce((total, count) -> {
if (count != null) {
total += count;
}
return total;
});
return underwayStepCount.orElse(0);
}
return 0;
}
private void calculateScenarioResultDTO(PlanReportCaseDTO item,
TestPlanScenarioStepCountDTO stepCount) {
if (StringUtils.isNotBlank(item.getReportId())) {
APIScenarioReportResult apiScenarioReportResult = apiScenarioReportService.get(item.getReportId());
if (apiScenarioReportResult != null) {
String content = apiScenarioReportResult.getContent();
if (StringUtils.isNotBlank(content)) {
JSONObject jsonObject = JSONObject.parseObject(content);
stepCount.setScenarioStepTotal(stepCount.getScenarioStepTotal() + jsonObject.getIntValue("scenarioStepTotal"));
stepCount.setScenarioStepSuccess(stepCount.getScenarioStepSuccess() + jsonObject.getIntValue("scenarioStepSuccess"));
stepCount.setScenarioStepError(stepCount.getScenarioStepError() + jsonObject.getIntValue("scenarioStepError"));
}
}
} else {
stepCount.getUnderwayIds().add(item.getCaseId());
}
}
private void getScenarioCaseReportStatusResultDTO(String status, int count, List<TestCaseReportStatusResultDTO> scenarioCaseList) {
if (count > 0) {
TestCaseReportStatusResultDTO scenarioCase = new TestCaseReportStatusResultDTO();
scenarioCase.setStatus(status);
scenarioCase.setCount(count);
scenarioCaseList.add(scenarioCase);
}
}
public List<TestPlanFailureScenarioDTO> getFailureList(String planId) {
List<TestPlanFailureScenarioDTO> apiTestCases = extTestPlanScenarioCaseMapper.getFailureList(planId);
if (CollectionUtils.isEmpty(apiTestCases)) {
return apiTestCases;
}
buildProjectInfo(apiTestCases);
buildUserInfo(apiTestCases);
return apiTestCases;
}
}

View File

@ -192,7 +192,7 @@ public class TestPlanService {
}
public TestPlan getTestPlan(String testPlanId) {
return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlan());
return Optional.ofNullable(testPlanMapper.selectByPrimaryKey(testPlanId)).orElse(new TestPlanWithBLOBs());
}
public String editTestPlan(TestPlanDTO testPlan, Boolean isSendMessage) {
@ -644,7 +644,7 @@ public class TestPlanService {
}
public void editTestPlanStatus(String planId) {
TestPlan testPlan = new TestPlan();
TestPlanWithBLOBs testPlan = new TestPlanWithBLOBs();
testPlan.setId(planId);
String status = calcTestPlanStatus(planId);
testPlan.setStatus(status);
@ -1160,14 +1160,14 @@ public class TestPlanService {
@Transactional(rollbackFor = Exception.class)
public TestPlan copy(String planId) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId);
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(planId);
if (testPlan == null) {
return null;
}
String sourcePlanId = testPlan.getId();
String targetPlanId = UUID.randomUUID().toString();
TestPlan targetPlan = new TestPlan();
TestPlanWithBLOBs targetPlan = new TestPlanWithBLOBs();
targetPlan.setId(targetPlanId);
targetPlan.setName(testPlan.getName() + "_COPY");
targetPlan.setWorkspaceId(testPlan.getWorkspaceId());
@ -1308,4 +1308,32 @@ public class TestPlanService {
LogUtil.error(e.getMessage(), e);
}
}
public TestPlanSimpleReportDTO getReport(String planId) {
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(planId);
TestPlanSimpleReportDTO report = new TestPlanSimpleReportDTO();
TestPlanFunctionResultReportDTO functionResult = new TestPlanFunctionResultReportDTO();
TestPlanApiResultReportDTO apiResult = new TestPlanApiResultReportDTO();
report.setFunctionResult(functionResult);
report.setApiResult(apiResult);
report.setStartTime(testPlan.getActualStartTime());
report.setStartTime(testPlan.getActualEndTime());
report.setSummary(testPlan.getReportSummary());
testPlanTestCaseService.calculatePlanReport(planId, report);
issuesService.calculatePlanReport(planId, report);
testPlanApiCaseService.calculatePlanReport(planId, report);
testPlanScenarioCaseService.calculatePlanReport(planId, report);
testPlanLoadCaseService.calculatePlanReport(planId, report);
report.setExecuteRate(report.getExecuteCount() * 0.1 / report.getCaseCount());
report.setPassRate(report.getPassCount() * 0.1 / report.getCaseCount());
return report;
}
public void editReport(TestPlanWithBLOBs testPlanWithBLOBs) {
testPlanMapper.updateByPrimaryKeySelective(testPlanWithBLOBs);
}
public TestCaseReportStatusResultDTO getFunctionalResultReport(String planId) {
return null;
}
}

View File

@ -7,16 +7,12 @@ import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
import io.metersphere.commons.user.SessionUser;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.member.QueryMemberRequest;
import io.metersphere.log.vo.DetailColumn;
import io.metersphere.log.vo.OperatingLogDetails;
import io.metersphere.service.UserService;
import io.metersphere.track.dto.TestCaseTestDTO;
import io.metersphere.track.dto.TestPlanCaseDTO;
import io.metersphere.track.dto.*;
import io.metersphere.track.request.testcase.TestPlanCaseBatchRequest;
import io.metersphere.track.request.testcase.TrackCount;
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
@ -369,4 +365,33 @@ public class TestPlanTestCaseService {
}
return null;
}
public void calculatePlanReport(String planId, TestPlanSimpleReportDTO report) {
List<PlanReportCaseDTO> planReportCaseDTOS = extTestPlanTestCaseMapper.selectForPlanReport(planId);
TestPlanFunctionResultReportDTO functionResult = report.getFunctionResult();
List<TestCaseReportStatusResultDTO> statusResult = new ArrayList<>();
Map<String, TestCaseReportStatusResultDTO> statusResultMap = new HashMap<>();
TestPlanUtils.calculatePlanReport(planReportCaseDTOS, statusResultMap, report, TestPlanTestCaseStatus.Pass.name());
TestPlanUtils.addToReportCommonStatusResultList(statusResultMap, statusResult);
TestPlanUtils.addToReportStatusResultList(statusResultMap, statusResult, TestPlanTestCaseStatus.Blocking.name());
TestPlanUtils.addToReportStatusResultList(statusResultMap, statusResult, TestPlanTestCaseStatus.Skip.name());
TestPlanUtils.addToReportStatusResultList(statusResultMap, statusResult, TestPlanTestCaseStatus.Underway.name());
functionResult.setCaseData(statusResult);
}
public List<TestPlanCaseDTO> getFailureCases(String planId) {
List<TestPlanCaseDTO> failureCases = extTestPlanTestCaseMapper.getFailureCases(planId);
Map<String, Project> projectMap = ServiceUtils.getProjectMap(
failureCases.stream().map(TestPlanCaseDTO::getProjectId).collect(Collectors.toList()));
Map<String, String> userNameMap = ServiceUtils.getUserNameMap(
failureCases.stream().map(TestPlanCaseDTO::getExecutor).collect(Collectors.toList()));
failureCases.forEach(item -> {
item.setProjectName(projectMap.get(item.getProjectId()).getName());
item.setIsCustomNum(projectMap.get(item.getProjectId()).getCustomNum());
item.setExecutorName(userNameMap.get(item.getExecutor()));
});
return failureCases;
}
}

View File

@ -55,4 +55,4 @@ CREATE TABLE IF NOT EXISTS test_plan_report_resource (
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
alter table project add azure_devops_id varchar(50) null after zentao_id;
alter table custom_field_template modify default_value varchar(100) null ;
alter table custom_field_template modify default_value varchar(100) null ;

View File

@ -38,4 +38,6 @@ CREATE TABLE `load_test_report_result_realtime`
)
ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE utf8mb4_general_ci;
COLLATE utf8mb4_general_ci;
ALTER TABLE test_plan ADD report_summary TEXT NULL COMMENT '测试计划报告总结';

View File

@ -0,0 +1,118 @@
<template>
<div>
<ms-chart v-if="visible && data.length > 0" :options="options"/>
<div v-if="visible && data.length <= 0" style="height: 300px">
</div>
</div>
</template>
<script>
import MsChart from "@/business/components/common/chart/MsChart";
export default {
name: "MsDoughnutPieChart",
components: {MsChart},
data() {
return {
visible: false,
options: {
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
right: 50,
bottom: '40%',
formatter: function (name) {
return name;
}
},
series: [
{
name: this.name,
type: 'pie',
left: -150,
radius: ['40%', '60%'],
avoidLabelOverlap: false,
label: {
// padding: [10, 10, 20, 10],
lineHeight: 35,
fontSize: 20,
// fontWeight: 'bold',
position: 'center',
color: 'gray',
formatter: function (params) {
return '';
},
},
labelLine: {
show: false
},
data: this.data
}
]
}
}
},
props: {
name: {
type: String,
default: '数据名称'
},
data: {
type: Array,
default() {
return []
}
},
},
watch: {
data() {
this.reload();
}
},
mounted() {
this.reload();
},
methods: {
reload() {
this.setFormatterFunc();
this.visible = false;
this.$nextTick(() => {
this.visible = true;
});
},
setFormatterFunc() {
let data = this.data;
let count = 0;
let title = this.name;
this.options.series[0].data = data;
this.data.forEach(item => {
count += item.value;
});
this.options.legend.formatter = (name) => {
let total = 0;
let target = 0;
for (let i = 0, l = data.length; i < l; i++) {
total += data[i].value;
if (data[i].name == name) {
target = data[i].value;
}
}
return name + ' | ' + target + ' ' + ((target / total) * 100).toFixed(0) + '%';
};
this.options.series[0].label.formatter = (params) => {
return title + '\n' + count;
};
},
}
}
</script>
<style scoped>
</style>

View File

@ -13,6 +13,7 @@
:class="{'ms-select-all-fixed': showSelectAll}"
:height="screenHeight"
v-loading="tableIsLoading"
:highlight-current-row="highlightCurrentRow"
ref="table" @row-click="handleRowClick">
<el-table-column v-if="enableSelection" width="50" type="selection"/>
@ -194,7 +195,8 @@ export default {
},
fields: Array,
fieldKey: String,
customFields: Array
customFields: Array,
highlightCurrentRow: Boolean,
},
mounted() {
getLabel(this, TEST_CASE_LIST);

View File

@ -25,7 +25,8 @@
:plan-id="planId"/>
<test-plan-load v-if="activeIndex === 'load'" :redirectCharType="redirectCharType" :clickType="clickType"
:plan-id="planId"/>
<test-case-statistics-report-view :test-plan="currentPlan" v-if="activeIndex === 'report'"/>
<!-- <test-case-statistics-report-view :test-plan="currentPlan" v-if="activeIndex === 'report'"/>-->
<test-plan-detail-report :test-plan="currentPlan" v-if="activeIndex === 'report'"/>
<test-report-template-list @openReport="openReport" ref="testReportTemplateList"/>
@ -49,10 +50,12 @@ import TestCaseStatisticsReportView from "./comonents/report/statistics/TestCase
import TestReportTemplateList from "./comonents/TestReportTemplateList";
import TestPlanLoad from "@/business/components/track/plan/view/comonents/load/TestPlanLoad";
import {getCurrentProjectID} from "@/common/js/utils";
import TestPlanDetailReport from "./comonents/report/TestPlanDetailReport";
export default {
name: "TestPlanView",
components: {
TestPlanDetailReport,
TestReportTemplateList,
TestCaseStatisticsReportView,
TestPlanApi,

View File

@ -0,0 +1,121 @@
<template>
<div v-loading="result.loading">
<el-row type="flex" class="head-bar">
<el-col :span="12">
</el-col>
<el-col :span="11" class="head-right">
<!-- <el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExport(report.name)">-->
<!-- {{$t('test_track.plan_view.export_report')}}-->
<!-- </el-button>-->
<el-button :disabled="!isTestManagerOrTestUser" plain size="mini" @click="handleExportHtml(report.name)">
{{'导出HTML'}}
</el-button>
</el-col>
</el-row>
<div class="container" ref="resume" id="app">
<el-main>
<test-plan-report-content :plan-id="planId"/>
</el-main>
</div>
</div>
</template>
<script>
import {exportPdf} from "@/common/js/utils";
import BaseInfoComponent from "./TemplateComponent/BaseInfoComponent";
import TestResultChartComponent from "./TemplateComponent/TestResultChartComponent";
import TestResultComponent from "./TemplateComponent/TestResultComponent";
import RichTextComponent from "./TemplateComponent/RichTextComponent";
import TestCaseReportTemplateEdit from "./TestCaseReportTemplateEdit";
import TemplateComponent from "./TemplateComponent/TemplateComponent";
import html2canvas from "html2canvas";
import MsTestCaseReportExport from "../TestCaseReportExport";
import TestReportTemplateList from "../TestReportTemplateList";
import TestPlanReportContent from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportContent";
export default {
name: "TestPlanDetailReport",
components: {
TestPlanReportContent,
TestReportTemplateList,
MsTestCaseReportExport,
TemplateComponent,
TestCaseReportTemplateEdit,
RichTextComponent, TestResultComponent, TestResultChartComponent, BaseInfoComponent
},
data() {
return {
result: {},
imgUrl: "",
previews: [],
report: {},
metric: {},
reportExportVisible: false,
isTestManagerOrTestUser: false
}
},
mounted() {
this.isTestManagerOrTestUser = true;
},
computed: {
planId() {
return this.testPlan.id;
},
},
props: ['testPlan'],
methods: {
handleExport(name) {
this.result.loading = true;
this.reportExportVisible = true;
let reset = this.exportReportReset;
this.$nextTick(function () {
setTimeout(() => {
html2canvas(document.getElementById('testCaseReportExport'), {
scale: 2
}).then(function(canvas) {
exportPdf(name, [canvas]);
reset();
});
}, 1000);
});
},
handleExportHtml(name) {
let config = {
url: '/test/plan/report/export/' + this.planId,
method: 'get',
responseType: 'blob'
};
this.$download(config, name + '.html');
},
exportReportReset() {
this.reportExportVisible = false;
this.result.loading = false;
},
}
}
</script>cd
<style scoped>
.head-right {
text-align: right;
}
.head-bar .el-button {
margin-bottom: 10px;
width: 80px;
}
.el-button+.el-button {
margin-left: 0px;
}
.head-bar {
position: fixed;
right: 10px;
padding: 20px;
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<test-plan-report-container :title="'接口用例统计'">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="测试结果" name="first">
<api-result :api-result="report.apiResult"/>
</el-tab-pane>
<el-tab-pane label="失败用例" name="second">
<api-failure-result :plan-id="planId"/>
</el-tab-pane>
<!-- <el-tab-pane label="所有用例" name="fourth">所有用例</el-tab-pane>-->
</el-tabs>
</test-plan-report-container>
</template>
<script>
import MsFormDivider from "@/business/components/common/components/MsFormDivider";
import ApiResult from "@/business/components/track/plan/view/comonents/report/detail/component/ApiResult";
import TestPlanReportContainer
from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportContainer";
import ApiFailureResult from "@/business/components/track/plan/view/comonents/report/detail/component/ApiFailureResult";
export default {
name: "TestPlanApiReport",
components: {ApiFailureResult, TestPlanReportContainer, ApiResult, MsFormDivider},
data() {
return {
activeName: 'first'
};
},
props: [
'report', 'planId'
],
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,49 @@
<template>
<test-plan-report-container :title="'功能用例统计'">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="测试结果" name="first">
<functional-result :function-result="report.functionResult"/>
</el-tab-pane>
<el-tab-pane label="失败用例" name="second">
<functional-failure-result :plan-id="planId"/>
</el-tab-pane>
<el-tab-pane label="缺陷列表" name="third">
<functional-issue-list :plan-id="planId"/>
</el-tab-pane>
<!-- <el-tab-pane label="所有用例" name="fourth">所有用例</el-tab-pane>-->
</el-tabs>
</test-plan-report-container>
</template>
<script>
import MsFormDivider from "@/business/components/common/components/MsFormDivider";
import FunctionalResult from "@/business/components/track/plan/view/comonents/report/detail/component/FunctionalResult";
import FunctionalFailureResult
from "@/business/components/track/plan/view/comonents/report/detail/component/FunctionalFailureResult";
import FunctionalIssueList
from "@/business/components/track/plan/view/comonents/report/detail/component/FunctionalIssueList";
import TestPlanReportContainer
from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportContainer";
export default {
name: "TestPlanFunctionalReport",
components: {TestPlanReportContainer, FunctionalIssueList, FunctionalFailureResult, FunctionalResult, MsFormDivider},
data() {
return {
activeName: 'first'
};
},
props: [
'report','planId'
],
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,49 @@
<template>
<test-plan-report-container :title="'性能用例数'">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="测试结果" name="first">
<load-result :load-result="report.loadResult"/>
</el-tab-pane>
<el-tab-pane label="失败用例" name="second">
<load-failure-result :plan-id="planId"/>
</el-tab-pane>
<!-- <el-tab-pane label="所有用例" name="fourth">所有用例</el-tab-pane>-->
</el-tabs>
</test-plan-report-container>
</template>
<script>
import MsFormDivider from "@/business/components/common/components/MsFormDivider";
import LoadResult from "@/business/components/track/plan/view/comonents/report/detail/component/LoadResult";
import TestPlanReportContainer
from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportContainer";
import LoadFailureResult
from "@/business/components/track/plan/view/comonents/report/detail/component/LoadFailureResult";
export default {
name: "TestPlanLoadReport",
components: {
LoadFailureResult,
TestPlanReportContainer,
LoadResult, MsFormDivider},
data() {
return {
activeName: 'first'
};
},
props: [
'report',
'planId'
],
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,23 @@
<template>
<div>
<ms-form-divider :title="title"/>
<!-- <el-main>-->
<slot/>
<!-- </el-main>-->
</div>
</template>
<script>
import MsFormDivider from "@/business/components/common/components/MsFormDivider";
export default {
name: "TestPlanReportContainer",
components: {MsFormDivider},
props: ['title']
}
</script>
<style scoped>
.el-main {
/*padding: 20px;*/
}
</style>

View File

@ -0,0 +1,64 @@
<template>
<el-card>
<test-plan-report-header :report="report" :plan-id="planId"/>
<test-plan-functional-report v-if="functionalEnable" :plan-id="planId" :report="report"/>
<test-plan-api-report v-if="apiEnable" :report="report" :plan-id="planId"/>
<test-plan-load-report v-if="loadEnable" :report="report" :plan-id="planId"/>
</el-card>
</template>
<script>
import TestPlanReportHeader from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportHeader";
import TestPlanFunctionalReport
from "@/business/components/track/plan/view/comonents/report/detail/TestPlanFunctionalReport";
import {getTestPlanReport} from "@/network/test-plan";
import TestPlanApiReport from "@/business/components/track/plan/view/comonents/report/detail/TestPlanApiReport";
import TestPlanLoadReport from "@/business/components/track/plan/view/comonents/report/detail/TestPlanLoadReport";
export default {
name: "TestPlanReportContent",
components: {TestPlanLoadReport, TestPlanApiReport, TestPlanFunctionalReport, TestPlanReportHeader},
props: {
planId:String,
},
data() {
return {
report: {}
};
},
created() {
this.getReport();
},
computed: {
functionalEnable() {
return this.report.functionResult.caseData.length > 0;
},
apiEnable() {
return this.report.apiResult.apiCaseData.length > 0 || this.report.apiResult.apiScenarioData.length > 0;
},
loadEnable() {
return this.report.loadResult.caseData.length > 0;
}
},
methods: {
getReport() {
this.result = getTestPlanReport(this.planId, (data) => {
this.report = data;
});
},
}
}
</script>
<style scoped>
.el-card {
/*width: 95% !important;*/
padding: 15px;
}
/deep/ .el-tabs .el-tabs__header {
padding-left: 15px;
padding-right: 15px;
padding-top: 15px;
}
</style>

View File

@ -0,0 +1,98 @@
<template>
<test-plan-report-container :title="'概览'">
<el-form class="form-info" v-loading="result.loading">
<el-form-item :label="$t('测试时间') + ':'">
{{showTime}}
</el-form-item>
<el-row type="flex" justify="space-between" class="select-time">
<el-col :span="8">
<el-form-item :label="'测试总数' + ':'">
{{report.caseCount}}
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="'执行率' + ':'">
{{ (report.executeRate * 100).toFixed(0) + '%'}}
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item :label="'通过率' + ':'">
{{ (report.passRate * 100).toFixed(0) + '%'}}
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="'报告总结'">
<el-link @click="isEdit = true">
编辑
</el-link>
</el-form-item>
<div v-if="!isEdit">
{{report.summary}}
</div>
<div v-else>
<el-input
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
placeholder="请输入内容"
v-model="report.summary" @blur="saveSummary"/>
</div>
</el-form>
</test-plan-report-container>
</template>
<script>
import MsFormDivider from "@/business/components/common/components/MsFormDivider";
import {editPlanReport} from "@/network/test-plan";
import TestPlanReportContainer
from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportContainer";
import {timestampFormatDate} from "@/common/js/filter";
export default {
name: "TestPlanReportHeader",
components: {TestPlanReportContainer, MsFormDivider},
props: {
planId: String,
report: Object
},
data() {
return {
result: {},
isEdit: false
}
},
computed: {
showTime() {
let startTime = 'NaN';
let endTime = 'NaN';
if (this.report.startTime) {
startTime = timestampFormatDate(this.report.startTime, false).substring(0, 10);
}
if (this.report.endTime) {
endTime = timestampFormatDate(this.report.endTime, false).substring(0, 10);
}
return startTime + ' ~ ' + endTime;
}
},
methods: {
saveSummary() {
editPlanReport({
id: this.planId,
reportSummary: this.report.summary ? this.report.summary : ''
});
this.isEdit = false;
}
}
}
</script>
<style scoped>
.el-link >>> .el-link--inner {
line-height: 40px;
font-size: 14px;
height: 42.8px;
}
.form-info {
padding: 20px;
}
</style>

View File

@ -0,0 +1,104 @@
<template>
<div>
<el-row class="scenario-info">
<el-col :span="7">
<ms-table v-loading="result.loading"
:show-select-all="false"
:screen-height="null"
:enable-selection="false"
:highlight-current-row="true"
@refresh="getScenarioApiCase"
@handleRowClick="rowClick"
:data="apiCases">
<ms-table-column
:width="80"
:label="$t('commons.id')"
prop="num">
</ms-table-column>
<ms-table-column
:label="$t('commons.name')"
prop="name">
</ms-table-column>
<ms-table-column
:label="'创建人'"
prop="creatorName"/>
<ms-table-column
:label="$t('test_track.case.priority')"
:width="80"
prop="priority">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority" ref="priority"/>
</template>
</ms-table-column>
<ms-table-column
:width="80"
:label="'执行结果'"
prop="lastResult">
<status-table-item :value="'Failure'"/>
</ms-table-column>
</ms-table>
</el-col>
<el-col :span="17" v-if="apiCases.length > 0">
<el-card>
<ms-request-result-tail :response="response" ref="debugResult"/>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import PriorityTableItem from "../../../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanApiFailureCase} from "@/network/test-plan";
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import {getApiReport} from "@/network/api";
import MsRequestResultTail from "@/business/components/api/definition/components/response/RequestResultTail";
export default {
name: "ApiCaseFailureResult",
components: {
MsRequestResultTail,
MsTableColumn, MsTable, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem},
props: {
planId: String
},
data() {
return {
apiCases: [],
result: {},
report: {},
response: {}
}
},
mounted() {
this.getScenarioApiCase();
},
methods: {
getScenarioApiCase() {
this.result = getPlanApiFailureCase(this.planId, (data) => {
this.apiCases = data;
if (data && data.length > 0) {
this.rowClick(data[0]);
}
});
},
rowClick(row) {
getApiReport(row.id, (data) => {
this.response = JSON.parse(data.content);
});
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,44 @@
<template>
<div>
<el-tabs type="card">
<el-tab-pane label="接口用例">
<api-case-failure-result :plan-id="planId"/>
</el-tab-pane>
<el-tab-pane label="场景用例">
<api-scenario-failure-result :plan-id="planId"/>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import PriorityTableItem from "../../../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import ApiScenarioFailureResult
from "@/business/components/track/plan/view/comonents/report/detail/component/ApiScenarioFailureResult";
import ApiCaseFailureResult
from "@/business/components/track/plan/view/comonents/report/detail/component/ApiCaseFailureResult";
export default {
name: "ApiFailureResult",
components: {
ApiCaseFailureResult,
ApiScenarioFailureResult, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem},
props: {
planId: String
},
data() {
return {
}
},
mounted() {
},
methods: {
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,110 @@
<template>
<div>
<el-row >
<el-col :span="12">
<ms-doughnut-pie-chart :name="$t('单接口用例')" :data="caseCharData" ref="functionChar"/>
</el-col>
<el-col :span="12">
<api-scenario-char-result :data="scenarioCharData"/>
</el-col>
</el-row>
</div>
</template>
<script>
import MsPieChart from "@/business/components/common/components/MsPieChart";
import MsDoughnutPieChart from "@/business/components/common/components/MsDoughnutPieChart";
import ApiScenarioCharResult
from "@/business/components/track/plan/view/comonents/report/detail/component/ApiScenarioCharResult";
export default {
name: "ApiResult",
components: {ApiScenarioCharResult, MsDoughnutPieChart, MsPieChart},
data() {
return {
caseDataMap: new Map([
["success", {name: this.$t('test_track.plan_view.pass'), itemStyle: {color: '#67C23A'}}],
["Success", {name: this.$t('test_track.plan_view.pass'), itemStyle: {color: '#67C23A'}}],
["error", {name: this.$t('test_track.plan_view.failure'), itemStyle: {color: '#F56C6C'}}],
["Fail", {name: this.$t('test_track.plan_view.failure'), itemStyle: {color: '#F56C6C'}}],
["Prepare", {name: this.$t('api_test.home_page.detail_card.unexecute'), itemStyle: {color: '#909399'}}],
]),
caseCharData: [],
scenarioCharData: [],
isShow: true
}
},
props: {
apiResult: {
type: Object,
default() {
return {
caseData: [],
issueData: []
}
}
}
},
watch: {
apiResult() {
this.getCaseCharData();
}
},
created() {
this.getCaseCharData();
},
methods: {
getCaseCharData() {
let caseCharData = [];
this.apiResult.apiCaseData.forEach(item => {
let data = this.getDataByStatus(item.status);
data.value = item.count;
caseCharData.push(data);
});
this.caseCharData = caseCharData;
let apiScenarioData = [];
this.apiResult.apiScenarioData.forEach(item => {
let data = this.getDataByStatus(item.status);
data.data = [item.count];
for (let i = 0; i < this.apiResult.apiScenarioStepData.length; i++) {
let stepItem = this.apiResult.apiScenarioStepData[i];
if (item.status) {
if (item.status === 'Fail' && stepItem.status === 'Failure') {
data.data.push(stepItem.count);
break;
}
if (item.status === 'Success' && stepItem.status === 'Pass') {
data.data.push(stepItem.count);
break;
}
if (item.status === 'Prepare' && stepItem.status === 'Underway') {
data.data.push(stepItem.count);
break;
}
} else {
if (stepItem.status === 'Underway') {
data.data.push(stepItem.count);
break;
}
}
}
apiScenarioData.push(data);
});
this.scenarioCharData = apiScenarioData;
},
getDataByStatus(status) {
let tmp = this.caseDataMap.get(status);
if (!tmp) {
tmp = this.caseDataMap.get('Prepare');
}
let data = {};
Object.assign(data, tmp);
return data;
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,172 @@
<template>
<ms-chart v-if="visible" :options="options"/>
</template>
<script>
import MsChart from "@/business/components/common/chart/MsChart";
let seriesLabel = {
show: true,
formatter: function (value) {
return value.data + '/' + ((value.data / 100) * 100).toFixed(0) + '%';
},
};
export default {
name: "ApiScenarioCharResult",
components: {MsChart},
data() {
return {
visible: false,
options: {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
},
},
legend: {
data: [],
// orient: 'vertical',
// right: 0,
// bottom: '40%',
},
grid: {
left: 100
},
xAxis: {
// type: 'value',
// // name: 'Days',
// axisLabel: {
// formatter: '{value}'
// }
},
yAxis: {
type: 'category',
inverse: true,
data: ['场景用例数', '步骤用例数'],
axisLabel: {
formatter: function (value) {
},
margin: 20,
rich: {
name: {
lineHeight: 30,
fontSize: 16,
align: 'center'
},
count: {
height: 40,
fontSize: 20,
align: 'center',
},
}
}
},
series: [
{
name: '未执行',
type: 'bar',
data: [165, 170],
label: seriesLabel,
itemStyle: {
color: '#909399'
}
},
{
name: '成功',
type: 'bar',
label: seriesLabel,
data: [150, 105],
itemStyle: {
color: '#F56C6C'
}
},
{
name: '失败',
type: 'bar',
label: seriesLabel,
data: [220, 82],
itemStyle: {
color: '#67C23A'
}
}
]
}
}
},
props: {
name: {
type: String,
default: '数据名称'
},
data: {
type: Array,
default() {
return []
}
},
},
watch: {
data() {
this.reload();
}
},
mounted() {
this.reload();
},
methods: {
reload() {
this.setFormatterFunc();
this.visible = false;
this.$nextTick(() => {
this.visible = true;
});
},
setFormatterFunc() {
let data = this.data;
let caseCount = 0;
let stepCount = 0;
this.data.forEach(item => {
caseCount += item.data[0];
if (item.data[1]) {
stepCount += item.data[1];
}
});
let formatterFuc = function (value) {
let total = 0;
if (value.dataIndex == 0) {
total = caseCount;
} else {
total = stepCount;
}
return value.data + '/' + ((value.data / total) * 100).toFixed(0) + '%';
};
this.data.forEach(item => {
item.type = 'bar';
item.label = {
show: true,
formatter: formatterFuc
};
});
this.options.series = data;
this.options.yAxis.axisLabel.formatter = function (value) {
if (value === '场景用例数') {
return '{name|场景用例数}\n' + '{count| ' + caseCount + '}';
} else {
return '{name|步骤用例数}\n' + '{count|' + stepCount + '}';
}
};
this.options.legend.data = data.map(i => i.name);
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,105 @@
<template>
<div>
<el-row class="scenario-info">
<el-col :span="8">
<ms-table v-loading="result.loading"
:show-select-all="false"
:screen-height="null"
:enable-selection="false"
:highlight-current-row="true"
@refresh="getScenarioApiCase"
@handleRowClick="rowClick"
:data="scenarioCases">
<ms-table-column
:width="80"
:label="$t('commons.id')"
prop="customNum">
</ms-table-column>
<ms-table-column
:label="$t('commons.name')"
prop="name">
</ms-table-column>
<ms-table-column
:label="'创建人'"
prop="creatorName">
<ms-table-column
:label="$t('test_track.case.priority')"
:width="80"
prop="level">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.level" ref="priority"/>
</template>
</ms-table-column>
</ms-table-column>
<ms-table-column
:width="70"
:label="'步骤数'"
prop="stepTotal">
</ms-table-column>
<ms-table-column
:width="80"
:label="'执行结果'"
prop="lastResult">
<status-table-item :value="'Failure'"/>
</ms-table-column>
</ms-table>
</el-col>
<el-col :span="16" v-if="scenarioCases.length > 0">
<ms-api-report @refresh="search" :infoDb="true" :report-id="reportId"/>
</el-col>
</el-row>
</div>
</template>
<script>
import PriorityTableItem from "../../../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanScenarioFailureCase} from "@/network/test-plan";
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import MsApiReport from "@/business/components/api/automation/report/ApiReportDetail";
export default {
name: "ApiScenarioFailureResult",
components: {
MsApiReport,
MsTableColumn, MsTable, StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem},
props: {
planId: String
},
data() {
return {
scenarioCases: [],
result: {},
report: {},
reportId: null
}
},
mounted() {
this.getScenarioApiCase();
},
methods: {
getScenarioApiCase() {
this.result = getPlanScenarioFailureCase(this.planId, (data) => {
this.scenarioCases = data;
if (data && data.length > 0) {
this.reportId = data[0].reportId;
}
});
},
rowClick(row) {
this.reportId = row.reportId;
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,99 @@
<template>
<div class="container">
<el-table
row-key="id"
:data="failureTestCases">
<el-table-column
prop="num"
:label="$t('commons.id')"
show-overflow-tooltip>
<template v-slot:default="{row}">
{{row.isCustomNum ? row.customNum : row.num }}
</template>
</el-table-column>
<el-table-column
prop="name"
:label="$t('commons.name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="priority"
column-key="priority"
:label="$t('test_track.case.priority')">
<template v-slot:default="scope">
<priority-table-item :value="scope.row.priority" ref="priority"/>
</template>
</el-table-column>
<!-- <el-table-column-->
<!-- prop="nodePath"-->
<!-- :label="$t('test_track.case.module')"-->
<!-- show-overflow-tooltip>-->
<!-- </el-table-column>-->
<el-table-column
prop="projectName"
:label="$t('test_track.case.project_name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="executorName"
:label="$t('test_track.plan_view.executor')">
</el-table-column>
<el-table-column
prop="status"
column-key="status"
:label="$t('test_track.plan_view.execute_result')">
<template v-slot:default="scope">
<status-table-item :value="'Failure'"/>
</template>
</el-table-column>
<el-table-column
prop="updateTime"
:label="$t('commons.update_time')"
show-overflow-tooltip>
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import PriorityTableItem from "../../../../../../common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanFunctionFailureCase} from "@/network/test-plan";
export default {
name: "FunctionalFailureResult",
components: {StatusTableItem, MethodTableItem, TypeTableItem, PriorityTableItem},
props: {
planId: String
},
data() {
return {
failureTestCases: []
}
},
mounted() {
this.getFailureTestCase();
},
methods: {
getFailureTestCase() {
getPlanFunctionFailureCase(this.planId, (data) => {
this.failureTestCases = data;
});
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,99 @@
<template>
<div class="container">
<ms-table
v-loading="result.loading"
:show-select-all="false"
:data="data"
:screen-height="null"
:enable-selection="false"
@refresh="getIssues">
<ms-table-column
:label="$t('test_track.issue.id')"
prop="id" v-if="false">
</ms-table-column>
<ms-table-column
:label="$t('test_track.issue.id')"
prop="num">
</ms-table-column>
<ms-table-column
:label="$t('test_track.issue.title')"
prop="title">
</ms-table-column>
<ms-table-column
:label="$t('test_track.issue.platform_status')"
v-if="isThirdPart"
prop="platformStatus">
<template v-slot="scope">
{{ scope.row.platformStatus ? scope.row.platformStatus : '--'}}
</template>
</ms-table-column>
<ms-table-column
v-else
:label="$t('test_track.issue.status')"
prop="status">
<template v-slot="scope">
<span>{{ issueStatusMap[scope.row.status] ? issueStatusMap[scope.row.status] : scope.row.status }}</span>
</template>
</ms-table-column>
<ms-table-column
:label="$t('test_track.issue.platform')"
prop="platform">
</ms-table-column>
<issue-description-table-item/>
</ms-table>
</div>
</template>
<script>
import MsTable from "@/business/components/common/components/table/MsTable";
import MsTableColumn from "@/business/components/common/components/table/MsTableColumn";
import IssueDescriptionTableItem from "@/business/components/track/issue/IssueDescriptionTableItem";
import {ISSUE_STATUS_MAP} from "@/common/js/table-constants";
import {getIssuesByPlanId} from "@/network/Issue";
import {getIssueTemplate} from "@/network/custom-field-template";
export default {
name: "FunctionalIssueList",
components: {IssueDescriptionTableItem, MsTableColumn, MsTable},
data() {
return {
data: [],
result: {},
isThirdPart: false
}
},
props: ['planId'],
computed: {
issueStatusMap() {
return ISSUE_STATUS_MAP;
},
},
mounted() {
getIssueTemplate()
.then((template) => {
if (template.platform === 'metersphere') {
this.isThirdPart = false;
} else {
this.isThirdPart = true;
}
});
this.getIssues();
},
methods: {
getIssues() {
this.result = getIssuesByPlanId(this.planId, (data) => {
this.data = data;
});
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,103 @@
<template>
<div>
<el-row >
<el-col :span="12">
<ms-doughnut-pie-chart :name="$t('功能用例数')" :data="caseCharData" ref="functionChar"/>
</el-col>
<el-col :span="12">
<ms-doughnut-pie-chart :name="$t('缺陷数')" :data="issueCharData"/>
</el-col>
</el-row>
</div>
</template>
<script>
import MsPieChart from "@/business/components/common/components/MsPieChart";
import MsDoughnutPieChart from "@/business/components/common/components/MsDoughnutPieChart";
export default {
name: "FunctionalResult",
components: {MsDoughnutPieChart, MsPieChart},
data() {
return {
caseDataMap: new Map([
["Pass", {name: this.$t('test_track.plan_view.pass'), itemStyle: {color: '#67C23A'}}],
["Failure", {name: this.$t('test_track.plan_view.failure'), itemStyle: {color: '#F56C6C'}}],
["Blocking", {name: this.$t('test_track.plan_view.blocking'), itemStyle: {color: '#E6A23C'}}],
["Skip", {name: this.$t('test_track.plan_view.skip'), itemStyle: {color: '#909399'}}],
["Underway", {name: this.$t('test_track.plan.plan_status_running'), itemStyle: {color: 'lightskyblue'}}],
["Prepare", {name: this.$t('test_track.plan.plan_status_prepare'), itemStyle: {color: '#DEDE10'}}]
]),
issueDataMap: new Map([
["resolved", {name: this.$t('已解决'), itemStyle: {color: '#67C23A'}}],
["new", {name: this.$t('新建'), itemStyle: {color: '#F56C6C'}}],
["closed", {name: this.$t('已关闭'), itemStyle: {color: '#909399'}}],
]),
caseCharData: [],
issueCharData: [],
isShow: true
}
},
props: {
functionResult: {
type: Object,
default() {
return {
caseData: [],
issueData: []
}
}
}
},
watch: {
functionResult() {
this.getCaseCharData();
}
},
created() {
this.getCaseCharData();
},
methods: {
getCaseCharData() {
let caseCharData = [];
this.functionResult.caseData.forEach(item => {
let data = this.caseDataMap.get(item.status);
data.value = item.count;
caseCharData.push(data);
});
this.caseCharData = caseCharData;
let issueCharData = [];
let colors = ['#F56C6C', '#67C23A', '#E6A23C', '#909399', 'lightskyblue', '#DEDE10'];
let usedSet = new Set();
this.functionResult.issueData.forEach(item => {
let status = item.status;
let data = this.issueDataMap.get(status);
if (!data) {
data = {name: status, itemStyle: {color: null}};
// if (status === 'new' || status === '' | status === '' | status === 'active') {
// data.color = '#F56C6C';
// }
if (!data.itemStyle.color) {
for (let i = 0; i < colors.length; i++) {
let color = colors[i];
if (!usedSet.has(color)) {
data.itemStyle.color = color;
break;
}
}
}
usedSet.add(data.itemStyle.color);
}
data.value = item.count;
issueCharData.push(data);
});
this.issueCharData = issueCharData;
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,70 @@
<template>
<div class="container">
<el-table
row-key="id"
:data="failureTestCases">
<el-table-column
prop="num"
:label="$t('commons.id')"
show-overflow-tooltip>
<template v-slot:default="{row}">
{{row.isCustomNum ? row.customNum : row.num }}
</template>
</el-table-column>
<el-table-column
prop="name"
:label="$t('commons.name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="userName"
:label="$t('commons.create_user')">
</el-table-column>
<el-table-column
prop="status"
column-key="status"
:label="$t('test_track.plan_view.execute_result')">
<template v-slot:default="scope">
<status-table-item :value="'Failure'"/>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import TypeTableItem from "../../../../../../common/tableItems/planview/TypeTableItem";
import MethodTableItem from "../../../../../../common/tableItems/planview/MethodTableItem";
import StatusTableItem from "../../../../../../common/tableItems/planview/StatusTableItem";
import {getPlanLoadFailureCase} from "@/network/test-plan";
export default {
name: "LoadFailureResult",
components: {StatusTableItem, MethodTableItem, TypeTableItem},
props: {
planId: String
},
data() {
return {
failureTestCases: []
}
},
mounted() {
this.getFailureTestCase();
},
methods: {
getFailureTestCase() {
getPlanLoadFailureCase(this.planId, (data) => {
this.failureTestCases = data;
});
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,67 @@
<template>
<div>
<el-row >
<el-col :span="12">
<ms-doughnut-pie-chart :name="$t('性能测试用例数')" :data="caseCharData" ref="functionChar"/>
</el-col>
<el-col :span="12">
</el-col>
</el-row>
</div>
</template>
<script>
import MsPieChart from "@/business/components/common/components/MsPieChart";
import MsDoughnutPieChart from "@/business/components/common/components/MsDoughnutPieChart";
export default {
name: "loadResult",
components: {MsDoughnutPieChart, MsPieChart},
data() {
return {
caseDataMap: new Map([
["success", {name: this.$t('test_track.plan_view.pass'), itemStyle: {color: '#67C23A'}}],
["error", {name: this.$t('test_track.plan_view.failure'), itemStyle: {color: '#F56C6C'}}],
["Prepare", {name: this.$t('api_test.home_page.detail_card.unexecute'), itemStyle: {color: '#909399'}}],
]),
caseCharData: [],
isShow: true
}
},
props: {
loadResult: {
type: Object,
default() {
return {
caseData: [],
}
}
}
},
watch: {
loadResult() {
this.getCaseCharData();
}
},
created() {
this.getCaseCharData();
},
methods: {
getCaseCharData() {
let caseCharData = [];
this.loadResult.caseData.forEach(item => {
let data = this.caseDataMap.get(item.status);
if (!data) {
data = this.caseDataMap.get('Prepare');
}
data.value = item.count;
caseCharData.push(data);
});
this.caseCharData = caseCharData;
},
}
}
</script>
<style scoped>
</style>

View File

@ -229,7 +229,7 @@
},
}
}
</script>cd
</script>
<style scoped>

View File

@ -10,7 +10,7 @@ const options = function (value, array) {
return value;
};
const timestampFormatDate = function (timestamp, showMs) {
export const timestampFormatDate = function (timestamp, showMs) {
if (!timestamp) {
return timestamp
}

View File

@ -32,6 +32,17 @@ export function getIssuesByCaseId(caseId, page) {
}
}
export function getIssuesByPlanId(planId, callback) {
if (planId) {
return get('/issues/plan/get/' + planId, (response) => {
if (callback) {
callback(response.data);
}
});
}
return {};
}
export function buildPlatformIssue(data) {
data.customFields = JSON.stringify(data.customFields);
return post("issues/get/platform/issue", data).then(response => {

View File

@ -1,8 +1,19 @@
import {post} from "@/common/js/ajax";
import {success} from "@/common/js/message";
import {baseGet} from "@/network/base-network";
export function apiCaseBatchRun(condition) {
return post('/api/testcase/batch/run', condition, () => {
success("执行成功,请稍后刷新查看");
});
}
export function getScenarioReport(reportId, callback) {
return reportId ? baseGet('/api/scenario/report/get/' + reportId, callback) : {};
}
export function getApiReport(reportId, callback) {
return reportId ? baseGet('/api/definition/report/getReport/' + reportId, callback) : {};
}

View File

@ -0,0 +1,9 @@
import {get} from "@/common/js/ajax";
export function baseGet(url, callback) {
return get(url, (response) => {
if (callback) {
callback(response.data);
}
});
}

View File

@ -0,0 +1,36 @@
import {post, get} from "@/common/js/ajax";
import {success} from "@/common/js/message";
import i18n from "@/i18n/i18n";
import {baseGet} from "@/network/base-network";
export function getTestPlanReport(planId, callback) {
if (planId) {
return get('/test/plan/report/' + planId, (response) => {
if (callback) {
callback(response.data);
}
});
}
}
export function editPlanReport(param) {
return post('/test/plan/edit/report', param, () => {
success(i18n.t('commons.save_success'));
});
}
export function getPlanFunctionFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/case/list/failure/' + planId, callback) : {};
}
export function getPlanScenarioFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/scenario/case/list/failure/' + planId, callback) : {};
}
export function getPlanApiFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/api/case/list/failure/' + planId, callback) : {};
}
export function getPlanLoadFailureCase(planId, callback) {
return planId ? baseGet('/test/plan/load/case/list/failure/' + planId, callback) : {};
}