Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # frontend/src/i18n/en-US.js # frontend/src/i18n/zh-CN.js
This commit is contained in:
commit
2f1c847e87
|
@ -10,11 +10,13 @@ import io.metersphere.commons.constants.RoleConstants;
|
|||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
|
@ -34,6 +36,11 @@ public class APIReportController {
|
|||
return apiReportService.recentTest(request);
|
||||
}
|
||||
|
||||
@GetMapping("/list/{testId}")
|
||||
public List<APIReportResult> listByTestId(@PathVariable String testId) {
|
||||
return apiReportService.listByTestId(testId);
|
||||
}
|
||||
|
||||
@PostMapping("/list/{goPage}/{pageSize}")
|
||||
public Pager<List<APIReportResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryAPIReportRequest request) {
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
|
@ -51,4 +58,10 @@ public class APIReportController {
|
|||
apiReportService.delete(request);
|
||||
}
|
||||
|
||||
@GetMapping("dashboard/tests")
|
||||
public List<DashboardTestDTO> dashboardTests() {
|
||||
return apiReportService.dashboardTests(SessionUtils.getCurrentWorkspaceId());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -88,8 +88,8 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
scenarioResult.addSuccess();
|
||||
testResult.addSuccess();
|
||||
} else {
|
||||
scenarioResult.addError();
|
||||
testResult.addError();
|
||||
scenarioResult.addError(result.getErrorCount());
|
||||
testResult.addError(result.getErrorCount());
|
||||
}
|
||||
|
||||
RequestResult requestResult = getRequestResult(result);
|
||||
|
@ -123,6 +123,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
requestResult.setRequestSize(result.getSentBytes());
|
||||
requestResult.setTotalAssertions(result.getAssertionResults().length);
|
||||
requestResult.setSuccess(result.isSuccessful());
|
||||
requestResult.setError(result.getErrorCount());
|
||||
|
||||
ResponseResult responseResult = requestResult.getResponseResult();
|
||||
responseResult.setBody(result.getResponseDataAsString());
|
||||
|
|
|
@ -13,6 +13,8 @@ public class RequestResult {
|
|||
|
||||
private long requestSize;
|
||||
|
||||
private int error;
|
||||
|
||||
private boolean success;
|
||||
|
||||
private String headers;
|
||||
|
|
|
@ -28,8 +28,8 @@ public class ScenarioResult {
|
|||
this.responseTime += time;
|
||||
}
|
||||
|
||||
public void addError() {
|
||||
this.error++;
|
||||
public void addError(int count) {
|
||||
this.error += count;
|
||||
}
|
||||
|
||||
public void addSuccess() {
|
||||
|
|
|
@ -22,8 +22,8 @@ public class TestResult {
|
|||
|
||||
private final List<ScenarioResult> scenarios = new ArrayList<>();
|
||||
|
||||
public void addError() {
|
||||
this.error++;
|
||||
public void addError(int count) {
|
||||
this.error += count;
|
||||
}
|
||||
|
||||
public void addSuccess() {
|
||||
|
|
|
@ -10,9 +10,12 @@ import io.metersphere.base.domain.ApiTestWithBLOBs;
|
|||
import io.metersphere.base.mapper.ApiTestReportMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtApiTestReportMapper;
|
||||
import io.metersphere.commons.constants.APITestStatus;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -62,4 +65,11 @@ public class APIReportService {
|
|||
report.setStatus(APITestStatus.Completed.name());
|
||||
apiTestReportMapper.insert(report);
|
||||
}
|
||||
|
||||
|
||||
public List<DashboardTestDTO> dashboardTests(String workspaceId) {
|
||||
Instant oneYearAgo = Instant.now().plus(-365, ChronoUnit.DAYS);
|
||||
long startTimestamp = oneYearAgo.toEpochMilli();
|
||||
return extApiTestReportMapper.selectDashboardTests(workspaceId, startTimestamp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,5 @@ public class TestPlanTestCase implements Serializable {
|
|||
|
||||
private Long updateTime;
|
||||
|
||||
private String results;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -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 TestPlanTestCaseWithBLOBs extends TestPlanTestCase implements Serializable {
|
||||
private String results;
|
||||
|
||||
private String issues;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
|
@ -2,6 +2,7 @@ package io.metersphere.base.mapper;
|
|||
|
||||
import io.metersphere.base.domain.TestPlanTestCase;
|
||||
import io.metersphere.base.domain.TestPlanTestCaseExample;
|
||||
import io.metersphere.base.domain.TestPlanTestCaseWithBLOBs;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
|
@ -12,25 +13,25 @@ public interface TestPlanTestCaseMapper {
|
|||
|
||||
int deleteByPrimaryKey(String id);
|
||||
|
||||
int insert(TestPlanTestCase record);
|
||||
int insert(TestPlanTestCaseWithBLOBs record);
|
||||
|
||||
int insertSelective(TestPlanTestCase record);
|
||||
int insertSelective(TestPlanTestCaseWithBLOBs record);
|
||||
|
||||
List<TestPlanTestCase> selectByExampleWithBLOBs(TestPlanTestCaseExample example);
|
||||
List<TestPlanTestCaseWithBLOBs> selectByExampleWithBLOBs(TestPlanTestCaseExample example);
|
||||
|
||||
List<TestPlanTestCase> selectByExample(TestPlanTestCaseExample example);
|
||||
|
||||
TestPlanTestCase selectByPrimaryKey(String id);
|
||||
TestPlanTestCaseWithBLOBs selectByPrimaryKey(String id);
|
||||
|
||||
int updateByExampleSelective(@Param("record") TestPlanTestCase record, @Param("example") TestPlanTestCaseExample example);
|
||||
int updateByExampleSelective(@Param("record") TestPlanTestCaseWithBLOBs record, @Param("example") TestPlanTestCaseExample example);
|
||||
|
||||
int updateByExampleWithBLOBs(@Param("record") TestPlanTestCase record, @Param("example") TestPlanTestCaseExample example);
|
||||
int updateByExampleWithBLOBs(@Param("record") TestPlanTestCaseWithBLOBs record, @Param("example") TestPlanTestCaseExample example);
|
||||
|
||||
int updateByExample(@Param("record") TestPlanTestCase record, @Param("example") TestPlanTestCaseExample example);
|
||||
|
||||
int updateByPrimaryKeySelective(TestPlanTestCase record);
|
||||
int updateByPrimaryKeySelective(TestPlanTestCaseWithBLOBs record);
|
||||
|
||||
int updateByPrimaryKeyWithBLOBs(TestPlanTestCase record);
|
||||
int updateByPrimaryKeyWithBLOBs(TestPlanTestCaseWithBLOBs record);
|
||||
|
||||
int updateByPrimaryKey(TestPlanTestCase record);
|
||||
}
|
|
@ -11,8 +11,9 @@
|
|||
<result column="create_time" jdbcType="BIGINT" property="createTime" />
|
||||
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
|
||||
</resultMap>
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.TestPlanTestCase">
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.TestPlanTestCaseWithBLOBs">
|
||||
<result column="results" jdbcType="LONGVARCHAR" property="results" />
|
||||
<result column="issues" jdbcType="LONGVARCHAR" property="issues" />
|
||||
</resultMap>
|
||||
<sql id="Example_Where_Clause">
|
||||
<where>
|
||||
|
@ -76,7 +77,7 @@
|
|||
id, plan_id, case_id, executor, status, remark, create_time, update_time
|
||||
</sql>
|
||||
<sql id="Blob_Column_List">
|
||||
results
|
||||
results, issues
|
||||
</sql>
|
||||
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.TestPlanTestCaseExample" resultMap="ResultMapWithBLOBs">
|
||||
select
|
||||
|
@ -126,17 +127,17 @@
|
|||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.TestPlanTestCase">
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.TestPlanTestCaseWithBLOBs">
|
||||
insert into test_plan_test_case (id, plan_id, case_id,
|
||||
executor, status, remark,
|
||||
create_time, update_time, results
|
||||
)
|
||||
create_time, update_time, results,
|
||||
issues)
|
||||
values (#{id,jdbcType=VARCHAR}, #{planId,jdbcType=VARCHAR}, #{caseId,jdbcType=VARCHAR},
|
||||
#{executor,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{remark,jdbcType=VARCHAR},
|
||||
#{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{results,jdbcType=LONGVARCHAR}
|
||||
)
|
||||
#{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}, #{results,jdbcType=LONGVARCHAR},
|
||||
#{issues,jdbcType=LONGVARCHAR})
|
||||
</insert>
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestPlanTestCase">
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestPlanTestCaseWithBLOBs">
|
||||
insert into test_plan_test_case
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -166,6 +167,9 @@
|
|||
<if test="results != null">
|
||||
results,
|
||||
</if>
|
||||
<if test="issues != null">
|
||||
issues,
|
||||
</if>
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="id != null">
|
||||
|
@ -195,6 +199,9 @@
|
|||
<if test="results != null">
|
||||
#{results,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
<if test="issues != null">
|
||||
#{issues,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</trim>
|
||||
</insert>
|
||||
<select id="countByExample" parameterType="io.metersphere.base.domain.TestPlanTestCaseExample" resultType="java.lang.Long">
|
||||
|
@ -233,6 +240,9 @@
|
|||
<if test="record.results != null">
|
||||
results = #{record.results,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
<if test="record.issues != null">
|
||||
issues = #{record.issues,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
|
@ -248,7 +258,8 @@
|
|||
remark = #{record.remark,jdbcType=VARCHAR},
|
||||
create_time = #{record.createTime,jdbcType=BIGINT},
|
||||
update_time = #{record.updateTime,jdbcType=BIGINT},
|
||||
results = #{record.results,jdbcType=LONGVARCHAR}
|
||||
results = #{record.results,jdbcType=LONGVARCHAR},
|
||||
issues = #{record.issues,jdbcType=LONGVARCHAR}
|
||||
<if test="_parameter != null">
|
||||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
|
@ -267,7 +278,7 @@
|
|||
<include refid="Update_By_Example_Where_Clause" />
|
||||
</if>
|
||||
</update>
|
||||
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.TestPlanTestCase">
|
||||
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.TestPlanTestCaseWithBLOBs">
|
||||
update test_plan_test_case
|
||||
<set>
|
||||
<if test="planId != null">
|
||||
|
@ -294,10 +305,13 @@
|
|||
<if test="results != null">
|
||||
results = #{results,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
<if test="issues != null">
|
||||
issues = #{issues,jdbcType=LONGVARCHAR},
|
||||
</if>
|
||||
</set>
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.TestPlanTestCase">
|
||||
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.TestPlanTestCaseWithBLOBs">
|
||||
update test_plan_test_case
|
||||
set plan_id = #{planId,jdbcType=VARCHAR},
|
||||
case_id = #{caseId,jdbcType=VARCHAR},
|
||||
|
@ -306,7 +320,8 @@
|
|||
remark = #{remark,jdbcType=VARCHAR},
|
||||
create_time = #{createTime,jdbcType=BIGINT},
|
||||
update_time = #{updateTime,jdbcType=BIGINT},
|
||||
results = #{results,jdbcType=LONGVARCHAR}
|
||||
results = #{results,jdbcType=LONGVARCHAR},
|
||||
issues = #{issues,jdbcType=LONGVARCHAR}
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
</update>
|
||||
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.TestPlanTestCase">
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.metersphere.base.mapper.ext;
|
|||
import io.metersphere.api.dto.APIReportResult;
|
||||
import io.metersphere.api.dto.QueryAPIReportRequest;
|
||||
import io.metersphere.dto.ApiReportDTO;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -15,4 +16,6 @@ public interface ExtApiTestReportMapper {
|
|||
|
||||
APIReportResult get(@Param("id") String id);
|
||||
|
||||
List<DashboardTestDTO> selectDashboardTests(@Param("workspaceId") String workspaceId, @Param("startTimestamp") long startTimestamp);
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</select>
|
||||
|
||||
<select id="listByTestId" resultMap="BaseResultMap">
|
||||
SELECT c,
|
||||
SELECT t.name AS test_name,
|
||||
r.name, r.description, r.id, r.test_id, r.create_time, r.update_time, r.status,
|
||||
project.name AS project_name
|
||||
FROM api_test_report r JOIN api_test t ON r.test_id = t.id
|
||||
|
@ -50,4 +50,16 @@
|
|||
ORDER BY r.update_time DESC
|
||||
</select>
|
||||
|
||||
<select id="selectDashboardTests" resultType="io.metersphere.dto.DashboardTestDTO">
|
||||
SELECT create_time AS date, count(api_test_report.id) AS count,
|
||||
date_format(from_unixtime(create_time / 1000), '%Y-%m-%d') AS x
|
||||
FROM api_test_report
|
||||
WHERE test_id IN (SELECT api_test.id
|
||||
FROM api_test
|
||||
JOIN project ON api_test.project_id = project.id
|
||||
WHERE workspace_id = #{workspaceId,jdbcType=VARCHAR})
|
||||
AND create_time > #{startTimestamp}
|
||||
GROUP BY x
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -25,9 +25,7 @@
|
|||
AND load_test.id = #{request.id}
|
||||
</if>
|
||||
</where>
|
||||
<if test="request.recent">
|
||||
order by load_test.update_time desc
|
||||
</if>
|
||||
order by load_test.update_time desc
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -1,6 +1,7 @@
|
|||
package io.metersphere.base.mapper.ext;
|
||||
|
||||
import io.metersphere.base.domain.LoadTestReport;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import io.metersphere.dto.ReportDTO;
|
||||
import io.metersphere.performance.controller.request.ReportRequest;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
@ -16,4 +17,6 @@ public interface ExtLoadTestReportMapper {
|
|||
int appendLine(@Param("testId") String id, @Param("line") String line);
|
||||
|
||||
LoadTestReport selectByPrimaryKey(String id);
|
||||
|
||||
List<DashboardTestDTO> selectDashboardTests(@Param("workspaceId") String workspaceId, @Param("startTimestamp") long startTimestamp);
|
||||
}
|
||||
|
|
|
@ -48,10 +48,22 @@
|
|||
</update>
|
||||
|
||||
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
|
||||
select
|
||||
<include refid="Base_Column_List" />
|
||||
from load_test_report
|
||||
where id = #{id,jdbcType=VARCHAR}
|
||||
SELECT
|
||||
<include refid="Base_Column_List"/>
|
||||
FROM load_test_report
|
||||
WHERE id = #{id,jdbcType=VARCHAR}
|
||||
</select>
|
||||
|
||||
<select id="selectDashboardTests" resultType="io.metersphere.dto.DashboardTestDTO">
|
||||
SELECT create_time AS date, count(load_test_report.id) AS count,
|
||||
date_format(from_unixtime(create_time / 1000), '%Y-%m-%d') AS x
|
||||
FROM load_test_report
|
||||
WHERE test_id IN (SELECT load_test.id
|
||||
FROM load_test
|
||||
JOIN project ON load_test.project_id = project.id
|
||||
WHERE workspace_id = #{workspaceId,jdbcType=VARCHAR})
|
||||
AND create_time > #{startTimestamp}
|
||||
GROUP BY x
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -9,4 +9,6 @@ import java.util.List;
|
|||
public interface ExtProjectMapper {
|
||||
|
||||
List<ProjectDTO> getProjectWithWorkspace(@Param("proRequest") ProjectRequest request);
|
||||
|
||||
List<String> getProjectIdByWorkspaceId(String workspaceId);
|
||||
}
|
||||
|
|
|
@ -16,5 +16,10 @@
|
|||
</if>
|
||||
</where>
|
||||
</select>
|
||||
<select id="getProjectIdByWorkspaceId" resultType="java.lang.String">
|
||||
select id
|
||||
from project
|
||||
where workspace_id = #{workspaceId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -1,5 +1,6 @@
|
|||
package io.metersphere.base.mapper.ext;
|
||||
|
||||
import io.metersphere.track.dto.TestPlanDTOWithMetric;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
@ -8,4 +9,6 @@ import java.util.List;
|
|||
|
||||
public interface ExtTestPlanMapper {
|
||||
List<TestPlanDTO> list(@Param("request") QueryTestPlanRequest params);
|
||||
|
||||
List<TestPlanDTOWithMetric> listRelate(@Param("request") QueryTestPlanRequest params);
|
||||
}
|
||||
|
|
|
@ -30,4 +30,20 @@
|
|||
</if>
|
||||
</select>
|
||||
|
||||
<select id="listRelate" resultType="io.metersphere.track.dto.TestPlanDTOWithMetric">
|
||||
select test_plan.*, project.name as project_name
|
||||
from test_plan
|
||||
left join project on test_plan.project_id = project.id
|
||||
where test_plan.workspace_id = #{request.workspaceId}
|
||||
and (test_plan.principal = #{request.principal}
|
||||
<if test="request.planIds != null and request.planIds.size() > 0">
|
||||
or test_plan.id in
|
||||
<foreach collection="request.planIds" item="planId" open="(" close=")" separator=",">
|
||||
#{planId}
|
||||
</foreach>
|
||||
</if>
|
||||
)
|
||||
order by test_plan.update_time desc
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -15,4 +15,9 @@ public interface ExtTestPlanTestCaseMapper {
|
|||
|
||||
List<TestPlanCaseDTO> list(@Param("request") QueryTestPlanCaseRequest request);
|
||||
|
||||
List<String> findRelateTestPlanId(String userId);
|
||||
|
||||
List<TestPlanCaseDTO> getRecentTestedTestCase(@Param("request") QueryTestPlanCaseRequest request);
|
||||
|
||||
List<TestPlanCaseDTO> getPendingTestCases(@Param("request") QueryTestPlanCaseRequest request);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@
|
|||
<if test="request.name != null">
|
||||
and test_case.name like CONCAT('%', #{request.name},'%')
|
||||
</if>
|
||||
<if test="request.id != null">
|
||||
and test_case.id = #{request.id}
|
||||
</if>
|
||||
<if test="request.nodeIds != null and request.nodeIds.size() > 0">
|
||||
and test_case.node_id in
|
||||
<foreach collection="request.nodeIds" item="nodeId" separator="," open="(" close=")">
|
||||
|
@ -58,5 +61,32 @@
|
|||
</if>
|
||||
</select>
|
||||
|
||||
|
||||
<select id="findRelateTestPlanId" resultType="java.lang.String">
|
||||
select distinct plan_id from test_plan_test_case where executor = #{userId}
|
||||
</select>
|
||||
<select id="getRecentTestedTestCase" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
|
||||
select test_plan_test_case.*, test_case.*
|
||||
from test_plan_test_case
|
||||
inner join test_case on test_plan_test_case.case_id = test_case.id
|
||||
where status != 'Prepare'
|
||||
and status != 'Underway'
|
||||
and test_plan_test_case.Executor = #{request.executor}
|
||||
and test_plan_test_case.plan_id in
|
||||
<foreach collection="request.planIds" item="planId" separator="," open="(" close=")">
|
||||
#{planId}
|
||||
</foreach>
|
||||
order by test_plan_test_case.update_time desc
|
||||
</select>
|
||||
<select id="getPendingTestCases" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
|
||||
select test_plan_test_case.*, test_case.*
|
||||
from test_plan_test_case
|
||||
inner join test_case on test_plan_test_case.case_id = test_case.id
|
||||
where (status = 'Prepare' or status = 'Underway')
|
||||
and test_plan_test_case.Executor = #{request.executor}
|
||||
and test_plan_test_case.plan_id in
|
||||
<foreach collection="request.planIds" item="planId" separator="," open="(" close=")">
|
||||
#{planId}
|
||||
</foreach>
|
||||
order by test_plan_test_case.update_time asc
|
||||
</select>
|
||||
</mapper>
|
|
@ -3,8 +3,12 @@ package io.metersphere.config;
|
|||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
import javax.validation.Validator;
|
||||
|
||||
@Configuration
|
||||
public class I18nConfig {
|
||||
|
@ -20,4 +24,16 @@ public class I18nConfig {
|
|||
public CommonBeanFactory commonBeanFactory() {
|
||||
return new CommonBeanFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSR-303校验国际化
|
||||
* @param messageSource
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
public LocalValidatorFactoryBean localValidatorFactoryBean(MessageSource messageSource) {
|
||||
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
|
||||
localValidatorFactoryBean.setValidationMessageSource(messageSource);
|
||||
return localValidatorFactoryBean;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,19 +13,17 @@ import io.metersphere.controller.request.UserRequest;
|
|||
import io.metersphere.controller.request.member.AddMemberRequest;
|
||||
import io.metersphere.controller.request.member.EditPassWordRequest;
|
||||
import io.metersphere.controller.request.member.QueryMemberRequest;
|
||||
import io.metersphere.controller.request.member.SetAdminRequest;
|
||||
import io.metersphere.controller.request.organization.AddOrgMemberRequest;
|
||||
import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
|
||||
import io.metersphere.dto.UserDTO;
|
||||
import io.metersphere.dto.UserRoleDTO;
|
||||
import io.metersphere.service.OrganizationService;
|
||||
import io.metersphere.service.UserService;
|
||||
import io.metersphere.service.WorkspaceService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -54,6 +52,12 @@ public class UserController {
|
|||
return PageUtils.setPageInfo(page, userService.getUserListWithRequest(request));
|
||||
}
|
||||
|
||||
@GetMapping("/special/user/role/{userId}")
|
||||
@RequiresRoles(RoleConstants.ADMIN)
|
||||
public UserRoleDTO getUserRole(@PathVariable("userId") String userId) {
|
||||
return userService.getUserRole(userId);
|
||||
}
|
||||
|
||||
@GetMapping("/special/delete/{userId}")
|
||||
@RequiresRoles(RoleConstants.ADMIN)
|
||||
public void deleteUser(@PathVariable(value = "userId") String userId) {
|
||||
|
@ -265,10 +269,4 @@ public class UserController {
|
|||
return userService.updateUserPassword(request);
|
||||
}
|
||||
|
||||
@PostMapping("/set/admin")
|
||||
@RequiresRoles(RoleConstants.ADMIN)
|
||||
public void setAdmin(@RequestBody SetAdminRequest request) {
|
||||
userService.setAdmin(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
package io.metersphere.controller.request.member;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SetAdminRequest {
|
||||
private String id;
|
||||
private String adminId;
|
||||
private String password;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package io.metersphere.dto;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class DashboardTestDTO {
|
||||
private Long date;
|
||||
private Integer count;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package io.metersphere.dto;
|
||||
|
||||
import io.metersphere.base.domain.Role;
|
||||
import io.metersphere.base.domain.UserRole;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class UserRoleDTO {
|
||||
|
||||
private String userId;
|
||||
private List<Role> roles;
|
||||
private List<UserRole> userRoles;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
|
@ -2,144 +2,64 @@ package io.metersphere.excel.domain;
|
|||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.alibaba.excel.annotation.write.style.ColumnWidth;
|
||||
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
@Data
|
||||
@ColumnWidth(15)
|
||||
public class TestCaseExcelData {
|
||||
|
||||
@NotBlank
|
||||
@Length(max=50)
|
||||
@ExcelProperty("用例名称")
|
||||
@ExcelProperty("{test_case_name}")
|
||||
private String name;
|
||||
|
||||
@NotBlank
|
||||
@Length(max=1000)
|
||||
@ExcelProperty("所属模块")
|
||||
@ExcelProperty("{test_case_module}")
|
||||
@ColumnWidth(30)
|
||||
@Pattern(regexp = "^(?!.*//).*$", message = "格式不正确")
|
||||
@Pattern(regexp = "^(?!.*//).*$", message = "{incorrect_format}")
|
||||
private String nodePath;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("用例类型")
|
||||
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "必须为functional、performance、api")
|
||||
@ExcelProperty("{test_case_type}")
|
||||
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "{test_case_type_validate}")
|
||||
private String type;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("维护人")
|
||||
@ExcelProperty("{test_case_maintainer}")
|
||||
private String maintainer;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("优先级")
|
||||
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "必须为P0、P1、P2、P3")
|
||||
@ExcelProperty("{test_case_priority}")
|
||||
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "{test_case_priority_validate}")
|
||||
private String priority;
|
||||
|
||||
@NotBlank
|
||||
@ExcelProperty("测试方式")
|
||||
@Pattern(regexp = "(^manual$)|(^auto$)", message = "必须为manual、auto")
|
||||
@ExcelProperty("{test_case_method}")
|
||||
@Pattern(regexp = "(^manual$)|(^auto$)", message = "{test_case_method_validate}")
|
||||
private String method;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("前置条件")
|
||||
@ExcelProperty("{test_case_prerequisite}")
|
||||
@Length(min=0, max=1000)
|
||||
private String prerequisite;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("备注")
|
||||
@ExcelProperty("{test_case_remark}")
|
||||
@Length(max=1000)
|
||||
private String remark;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("步骤描述")
|
||||
@ExcelProperty("{test_case_step_desc}")
|
||||
@Length(max=1000)
|
||||
private String stepDesc;
|
||||
|
||||
@ColumnWidth(50)
|
||||
@ExcelProperty("预期结果")
|
||||
@ExcelProperty("{test_case_step_result}")
|
||||
@Length(max=1000)
|
||||
private String stepResult;
|
||||
|
||||
public String getNodePath() {
|
||||
return nodePath;
|
||||
}
|
||||
|
||||
public void setNodePath(String nodePath) {
|
||||
this.nodePath = nodePath;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getMaintainer() {
|
||||
return maintainer;
|
||||
}
|
||||
|
||||
public void setMaintainer(String maintainer) {
|
||||
this.maintainer = maintainer;
|
||||
}
|
||||
|
||||
public String getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(String priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public String getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
||||
public void setMethod(String method) {
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
public String getPrerequisite() {
|
||||
return prerequisite;
|
||||
}
|
||||
|
||||
public void setPrerequisite(String prerequisite) {
|
||||
this.prerequisite = prerequisite;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public String getStepDesc() {
|
||||
return stepDesc;
|
||||
}
|
||||
|
||||
public void setStepDesc(String stepDesc) {
|
||||
this.stepDesc = stepDesc;
|
||||
}
|
||||
|
||||
public String getStepResult() {
|
||||
return stepResult;
|
||||
}
|
||||
|
||||
public void setStepResult(String stepResult) {
|
||||
this.stepResult = stepResult;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,19 +6,22 @@ import com.alibaba.excel.event.AnalysisEventListener;
|
|||
import com.alibaba.excel.exception.ExcelAnalysisException;
|
||||
import com.alibaba.excel.util.StringUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.excel.utils.ExcelValidateHelper;
|
||||
import io.metersphere.excel.domain.ExcelErrData;
|
||||
import io.metersphere.excel.utils.EasyExcelI18nTranslator;
|
||||
import io.metersphere.excel.utils.ExcelValidateHelper;
|
||||
import io.metersphere.i18n.Translator;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
||||
|
||||
protected List<ExcelErrData<T>> errList = new ArrayList<>();
|
||||
|
||||
protected List<T> list = new ArrayList<>();
|
||||
|
||||
protected EasyExcelI18nTranslator easyExcelI18nTranslator;
|
||||
|
||||
/**
|
||||
* 每隔2000条存储数据库,然后清理list ,方便内存回收
|
||||
*/
|
||||
|
@ -26,13 +29,15 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
|
||||
protected Class<T> clazz;
|
||||
|
||||
|
||||
public EasyExcelListener(Class<T> clazz){
|
||||
this.clazz = clazz;
|
||||
public EasyExcelListener(){
|
||||
Type type = getClass().getGenericSuperclass();
|
||||
this.clazz = (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||
this.easyExcelI18nTranslator = new EasyExcelI18nTranslator(this.clazz);
|
||||
this.easyExcelI18nTranslator.translateExcelProperty();
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个每一条数据解析都会来调用
|
||||
* 每条数据解析都会调用
|
||||
*
|
||||
* @param t
|
||||
* @param analysisContext
|
||||
|
@ -47,12 +52,14 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
//自定义校验规则
|
||||
errMsg = validate(t, errMsg);
|
||||
} catch (NoSuchFieldException e) {
|
||||
errMsg = "解析数据出错";
|
||||
errMsg = Translator.get("parse_data_error");
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
if (!StringUtils.isEmpty(errMsg)) {
|
||||
ExcelErrData excelErrData = new ExcelErrData(t, rowIndex, "第" + rowIndex + "行出错:" + errMsg);
|
||||
ExcelErrData excelErrData = new ExcelErrData(t, rowIndex,
|
||||
Translator.get("number") + rowIndex + Translator.get("row") + Translator.get("error")
|
||||
+ ":" + errMsg);
|
||||
errList.add(excelErrData);
|
||||
} else {
|
||||
list.add(t);
|
||||
|
@ -94,20 +101,20 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
*/
|
||||
@Override
|
||||
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
|
||||
super.invokeHeadMap(headMap, context);
|
||||
if (clazz != null){
|
||||
try {
|
||||
Set<String> fieldNameSet = getFieldNameSet(clazz);
|
||||
Collection<String> values = headMap.values();
|
||||
for (String key : fieldNameSet) {
|
||||
if (!values.contains(key)){
|
||||
throw new ExcelAnalysisException("缺少头部信息:" + key);
|
||||
throw new ExcelAnalysisException(Translator.get("missing_header_information") + ":" + key);
|
||||
}
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
super.invokeHeadMap(headMap, context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -132,9 +139,11 @@ public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
public List<ExcelErrData<T>> getErrList() {
|
||||
return errList;
|
||||
}
|
||||
|
||||
public void close () {
|
||||
this.easyExcelI18nTranslator.resetExcelProperty();
|
||||
}
|
||||
}
|
|
@ -2,10 +2,11 @@ package io.metersphere.excel.listener;
|
|||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
import io.metersphere.commons.constants.TestCaseConstants;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
|
@ -26,9 +27,7 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
|
||||
Set<String> userIds;
|
||||
|
||||
public TestCaseDataListener(TestCaseService testCaseService, String projectId,
|
||||
Set<String> testCaseNames, Set<String> userIds, Class<TestCaseExcelData> clazz) {
|
||||
super(clazz);
|
||||
public TestCaseDataListener(TestCaseService testCaseService, String projectId, Set<String> testCaseNames, Set<String> userIds) {
|
||||
this.testCaseService = testCaseService;
|
||||
this.projectId = projectId;
|
||||
this.testCaseNames = testCaseNames;
|
||||
|
@ -39,24 +38,26 @@ public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
|||
public String validate(TestCaseExcelData data, String errMsg) {
|
||||
String nodePath = data.getNodePath();
|
||||
StringBuilder stringBuilder = new StringBuilder(errMsg);
|
||||
String[] nodes = nodePath.split("/");
|
||||
|
||||
if ( nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||
stringBuilder.append("节点最多为" + TestCaseConstants.MAX_NODE_DEPTH + "层;");
|
||||
}
|
||||
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
||||
stringBuilder.append("所属模块不能为空格");
|
||||
break;
|
||||
if (nodePath != null) {
|
||||
String[] nodes = nodePath.split("/");
|
||||
if ( nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||
stringBuilder.append(Translator.get("test_case_node_level_tip") +
|
||||
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level"));
|
||||
}
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
||||
stringBuilder.append(Translator.get("module_not_null"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!userIds.contains(data.getMaintainer())) {
|
||||
stringBuilder.append("该工作空间下无该用户:" + data.getMaintainer() + ";");
|
||||
stringBuilder.append(Translator.get("user_not_exists") + ":" + data.getMaintainer() + "; ");
|
||||
}
|
||||
if (testCaseNames.contains(data.getName())) {
|
||||
stringBuilder.append("该项目下已存在该测试用例:" + data.getName() + ";");
|
||||
stringBuilder.append(Translator.get("test_case_already_exists") + ":" + data.getName() + "; ");
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
package io.metersphere.excel.utils;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.exception.ExcelException;
|
||||
import io.metersphere.i18n.Translator;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class EasyExcelExporter {
|
||||
|
||||
EasyExcelI18nTranslator easyExcelI18nTranslator;
|
||||
|
||||
public EasyExcelExporter() {
|
||||
easyExcelI18nTranslator = new EasyExcelI18nTranslator(TestCaseExcelData.class);
|
||||
easyExcelI18nTranslator.translateExcelProperty();
|
||||
}
|
||||
|
||||
public void export(HttpServletResponse response, Class clazz, List data, String fileName, String sheetName) {
|
||||
response.setContentType("application/vnd.ms-excel");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
try {
|
||||
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
|
||||
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
throw new ExcelException("Utf-8 encoding is not supported");
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
throw new ExcelException("IO exception");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void close() {
|
||||
easyExcelI18nTranslator.resetExcelProperty();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package io.metersphere.excel.utils;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.exception.ExcelException;
|
||||
import io.metersphere.i18n.Translator;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 表头国际化
|
||||
* 先调用 saveOriginalExcelProperty 存储原表头注解值
|
||||
* 再调用 translateExcelProperty 获取国际化的值
|
||||
* 最后调用 resetExcelProperty 重置为原来值,防止切换语言后无法国际化
|
||||
*/
|
||||
public class EasyExcelI18nTranslator {
|
||||
|
||||
private Map<String, List<String>> excelPropertyMap = new HashMap<>();
|
||||
|
||||
private Class clazz;
|
||||
|
||||
public EasyExcelI18nTranslator(Class clazz) {
|
||||
this.clazz = clazz;
|
||||
saveOriginalExcelProperty();
|
||||
}
|
||||
|
||||
private void readExcelProperty(Class clazz, BiConsumer<String, Map<String, Object>> operate) {
|
||||
Field field;
|
||||
Field[] fields = clazz.getDeclaredFields();
|
||||
for (int i = 0; i < fields.length ; i++) {
|
||||
try {
|
||||
field = clazz.getDeclaredField(fields[i].getName());
|
||||
field.setAccessible(true);
|
||||
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||
if(excelProperty != null){
|
||||
InvocationHandler invocationHandler = Proxy.getInvocationHandler(excelProperty);
|
||||
Field fieldValue = invocationHandler.getClass().getDeclaredField("memberValues");
|
||||
fieldValue.setAccessible(true);
|
||||
Map<String, Object> memberValues = null;
|
||||
try {
|
||||
memberValues = (Map<String, Object>) fieldValue.get(invocationHandler);
|
||||
} catch (IllegalAccessException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
throw new ExcelException(e.getMessage());
|
||||
}
|
||||
|
||||
operate.accept(field.getName(), memberValues);
|
||||
|
||||
}
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void saveOriginalExcelProperty() {
|
||||
readExcelProperty(clazz, (fieldName, memberValues) -> {
|
||||
List<String> values = Arrays.asList((String [])memberValues.get("value"));
|
||||
List<String> copyValues = new ArrayList<>();
|
||||
values.forEach(value -> {
|
||||
copyValues.add(value);
|
||||
});
|
||||
excelPropertyMap.put(fieldName, copyValues);
|
||||
});
|
||||
}
|
||||
|
||||
public void translateExcelProperty() {
|
||||
readExcelProperty(TestCaseExcelData.class, (fieldName, memberValues) -> {
|
||||
String [] values = (String[]) memberValues.get("value");
|
||||
for (int j = 0; j < values.length; j++) {
|
||||
if (Pattern.matches("^\\{.+\\}$", values[j])) {
|
||||
values[j] = Translator.get(values[j].substring(1, values[j].length() - 1));
|
||||
}
|
||||
}
|
||||
memberValues.put("value", values);
|
||||
});
|
||||
}
|
||||
|
||||
public void resetExcelProperty() {
|
||||
readExcelProperty(clazz, (fieldName, memberValues) -> {
|
||||
String [] values = (String[]) memberValues.get("value");
|
||||
List<String> list = excelPropertyMap.get(fieldName);
|
||||
for (int j = 0; j < values.length; j++) {
|
||||
values[j] = list.get(j);
|
||||
}
|
||||
memberValues.put("value", values);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package io.metersphere.excel.utils;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.exception.ExcelException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
|
||||
public class EasyExcelUtil {
|
||||
|
||||
public static void export(HttpServletResponse response, Class clazz, List data, String fileName, String sheetName) {
|
||||
response.setContentType("application/vnd.ms-excel");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
try {
|
||||
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8") + ".xlsx");
|
||||
EasyExcel.write(response.getOutputStream(), clazz).sheet(sheetName).doWrite(data);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
throw new ExcelException("不支持UTF-8编码");
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
throw new ExcelException("IO异常");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,32 +1,41 @@
|
|||
package io.metersphere.excel.utils;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import javax.validation.groups.Default;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
@Component
|
||||
public class ExcelValidateHelper {
|
||||
|
||||
private ExcelValidateHelper(){}
|
||||
private static ExcelValidateHelper excelValidateHelper;
|
||||
|
||||
private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
@Resource
|
||||
LocalValidatorFactoryBean localValidatorFactoryBean;
|
||||
|
||||
public static <T> String validateEntity(T obj) throws NoSuchFieldException {
|
||||
public static <T> String validateEntity(T obj) throws NoSuchFieldException {
|
||||
StringBuilder result = new StringBuilder();
|
||||
Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class);
|
||||
Set<ConstraintViolation<T>> set = excelValidateHelper.localValidatorFactoryBean.getValidator().validate(obj, Default.class);
|
||||
if (set != null && !set.isEmpty()) {
|
||||
for (ConstraintViolation<T> cv : set) {
|
||||
Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString());
|
||||
ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class);
|
||||
//拼接错误信息,包含当前出错数据的标题名字+错误信息
|
||||
result.append(annotation.value()[0]+cv.getMessage()).append(";");
|
||||
result.append(annotation.value()[0]+cv.getMessage()).append("; ");
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initialize() {
|
||||
excelValidateHelper = this;
|
||||
excelValidateHelper.localValidatorFactoryBean = this.localValidatorFactoryBean;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import io.metersphere.commons.constants.RoleConstants;
|
|||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import io.metersphere.dto.LoadTestDTO;
|
||||
import io.metersphere.performance.service.PerformanceTestService;
|
||||
import io.metersphere.service.FileService;
|
||||
|
@ -101,4 +102,9 @@ public class PerformanceTestController {
|
|||
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileOperationRequest.getName() + "\"")
|
||||
.body(bytes);
|
||||
}
|
||||
|
||||
@GetMapping("dashboard/tests")
|
||||
public List<DashboardTestDTO> dashboardTests() {
|
||||
return performanceTestService.dashboardTests(SessionUtils.getCurrentWorkspaceId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,14 @@ import io.metersphere.base.mapper.ext.ExtLoadTestReportMapper;
|
|||
import io.metersphere.commons.constants.PerformanceTestStatus;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.track.request.testplan.*;
|
||||
import io.metersphere.dto.DashboardTestDTO;
|
||||
import io.metersphere.dto.LoadTestDTO;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.performance.engine.Engine;
|
||||
import io.metersphere.performance.engine.EngineFactory;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.service.TestResourceService;
|
||||
import io.metersphere.track.request.testplan.*;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -23,6 +24,8 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
@ -247,7 +250,6 @@ public class PerformanceTestService {
|
|||
|
||||
public List<LoadTestDTO> recentTestPlans(QueryTestPlanRequest request) {
|
||||
// 查询最近的测试计划
|
||||
request.setRecent(true);
|
||||
return extLoadTestMapper.list(request);
|
||||
}
|
||||
|
||||
|
@ -277,4 +279,10 @@ public class PerformanceTestService {
|
|||
return loadTestMapper.selectByExampleWithBLOBs(example);
|
||||
}
|
||||
|
||||
public List<DashboardTestDTO> dashboardTests(String workspaceId) {
|
||||
Instant oneYearAgo = Instant.now().plus(-365, ChronoUnit.DAYS);
|
||||
long startTimestamp = oneYearAgo.toEpochMilli();
|
||||
return extLoadTestReportMapper.selectDashboardTests(workspaceId, startTimestamp);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import io.metersphere.base.domain.*;
|
|||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.ExtUserMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtUserRoleMapper;
|
||||
import io.metersphere.commons.constants.RoleConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
import io.metersphere.commons.utils.CodingUtil;
|
||||
|
@ -13,17 +12,16 @@ import io.metersphere.controller.request.UserRequest;
|
|||
import io.metersphere.controller.request.member.AddMemberRequest;
|
||||
import io.metersphere.controller.request.member.EditPassWordRequest;
|
||||
import io.metersphere.controller.request.member.QueryMemberRequest;
|
||||
import io.metersphere.controller.request.member.SetAdminRequest;
|
||||
import io.metersphere.controller.request.organization.AddOrgMemberRequest;
|
||||
import io.metersphere.controller.request.organization.QueryOrgMemberRequest;
|
||||
import io.metersphere.dto.UserDTO;
|
||||
import io.metersphere.dto.UserRoleDTO;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
@ -100,16 +98,24 @@ public class UserService {
|
|||
}
|
||||
UserDTO userDTO = new UserDTO();
|
||||
BeanUtils.copyProperties(user, userDTO);
|
||||
UserRoleDTO userRole = getUserRole(userId);
|
||||
userDTO.setUserRoles(userRole.getUserRoles());
|
||||
userDTO.setRoles(userRole.getRoles());
|
||||
return userDTO;
|
||||
}
|
||||
|
||||
public UserRoleDTO getUserRole(String userId) {
|
||||
UserRoleDTO userRoleDTO = new UserRoleDTO();
|
||||
//
|
||||
UserRoleExample userRoleExample = new UserRoleExample();
|
||||
userRoleExample.createCriteria().andUserIdEqualTo(userId);
|
||||
List<UserRole> userRoleList = userRoleMapper.selectByExample(userRoleExample);
|
||||
|
||||
if (CollectionUtils.isEmpty(userRoleList)) {
|
||||
return userDTO;
|
||||
return userRoleDTO;
|
||||
}
|
||||
// 设置 user_role
|
||||
userDTO.setUserRoles(userRoleList);
|
||||
userRoleDTO.setUserRoles(userRoleList);
|
||||
|
||||
List<String> roleIds = userRoleList.stream().map(UserRole::getRoleId).collect(Collectors.toList());
|
||||
|
||||
|
@ -117,9 +123,9 @@ public class UserService {
|
|||
roleExample.createCriteria().andIdIn(roleIds);
|
||||
|
||||
List<Role> roleList = roleMapper.selectByExample(roleExample);
|
||||
userDTO.setRoles(roleList);
|
||||
userRoleDTO.setRoles(roleList);
|
||||
|
||||
return userDTO;
|
||||
return userRoleDTO;
|
||||
}
|
||||
|
||||
public List<User> getUserList() {
|
||||
|
@ -328,23 +334,6 @@ public class UserService {
|
|||
return extUserMapper.updatePassword(user);
|
||||
}
|
||||
|
||||
public void setAdmin(SetAdminRequest request) {
|
||||
String adminId = request.getAdminId();
|
||||
String password = request.getPassword();
|
||||
if (!checkUserPassword(adminId, password)) {
|
||||
MSException.throwException("verification failed!");
|
||||
}
|
||||
UserRole userRole = new UserRole();
|
||||
userRole.setId(UUID.randomUUID().toString());
|
||||
userRole.setUserId(request.getId());
|
||||
// TODO 修改admin sourceId
|
||||
userRole.setSourceId("adminSourceId");
|
||||
userRole.setRoleId(RoleConstants.ADMIN);
|
||||
userRole.setCreateTime(System.currentTimeMillis());
|
||||
userRole.setUpdateTime(System.currentTimeMillis());
|
||||
userRoleMapper.insertSelective(userRole);
|
||||
}
|
||||
|
||||
public String getDefaultLanguage() {
|
||||
final String key = "default.language";
|
||||
return extUserMapper.getDefaultLanguage(key);
|
||||
|
|
|
@ -77,7 +77,7 @@ public class TestCaseController {
|
|||
}
|
||||
|
||||
@PostMapping("/import/{projectId}")
|
||||
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId){
|
||||
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId) throws NoSuchFieldException {
|
||||
return testCaseService.testCaseImport(file, projectId);
|
||||
}
|
||||
|
||||
|
@ -91,4 +91,9 @@ public class TestCaseController {
|
|||
testCaseService.editTestCaseBath(request);
|
||||
}
|
||||
|
||||
@PostMapping("/batch/delete")
|
||||
public void deleteTestCaseBath(@RequestBody TestCaseBatchRequest request){
|
||||
testCaseService.deleteTestCaseBath(request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,8 +41,4 @@ public class TestCaseReportController {
|
|||
return testCaseReportService.deleteTestCaseReport(id);
|
||||
}
|
||||
|
||||
@GetMapping("/get/metric/{planId}")
|
||||
public TestCaseReportMetricDTO getMetric(@PathVariable String planId){
|
||||
return testCaseReportService.getMetric(planId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,9 @@ import io.metersphere.base.domain.TestPlan;
|
|||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.track.dto.TestCaseReportMetricDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTOWithMetric;
|
||||
import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
import io.metersphere.track.service.TestPlanService;
|
||||
|
@ -36,6 +38,10 @@ public class TestPlanController {
|
|||
return testPlanService.listTestAllPlan(currentWorkspaceId);
|
||||
}
|
||||
|
||||
@PostMapping("/list/all/relate")
|
||||
public List<TestPlanDTOWithMetric> listRelateAll() {
|
||||
return testPlanService.listRelateAllPlan();
|
||||
}
|
||||
|
||||
@GetMapping("recent/{count}")
|
||||
public List<TestPlan> recentTestPlans(@PathVariable int count) {
|
||||
|
@ -69,5 +75,8 @@ public class TestPlanController {
|
|||
testPlanService.testPlanRelevance(request);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/get/metric/{planId}")
|
||||
public TestCaseReportMetricDTO getMetric(@PathVariable String planId){
|
||||
return testPlanService.getMetric(planId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.metersphere.track.controller;
|
|||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.TestPlanTestCase;
|
||||
import io.metersphere.base.domain.TestPlanTestCaseWithBLOBs;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.track.request.testcase.TestPlanCaseBatchRequest;
|
||||
|
@ -24,16 +25,31 @@ public class TestPlanTestCaseController {
|
|||
@PostMapping("/list/{goPage}/{pageSize}")
|
||||
public Pager<List<TestPlanCaseDTO>> getTestPlanCases(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryTestPlanCaseRequest request){
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
return PageUtils.setPageInfo(page, testPlanTestCaseService.getTestPlanCases(request));
|
||||
return PageUtils.setPageInfo(page, testPlanTestCaseService.list(request));
|
||||
}
|
||||
|
||||
@GetMapping("/get/{caseId}")
|
||||
public TestPlanCaseDTO getTestPlanCases(@PathVariable String caseId){
|
||||
return testPlanTestCaseService.get(caseId);
|
||||
}
|
||||
|
||||
@PostMapping("recent/{count}")
|
||||
public List<TestPlanCaseDTO> getRecentTestCases(@PathVariable int count, @RequestBody QueryTestPlanCaseRequest request){
|
||||
return testPlanTestCaseService.getRecentTestCases(request, count);
|
||||
}
|
||||
|
||||
@PostMapping("pending/{count}")
|
||||
public List<TestPlanCaseDTO> getPrepareTestCases(@PathVariable int count, @RequestBody QueryTestPlanCaseRequest request){
|
||||
return testPlanTestCaseService.getPendingTestCases(request, count);
|
||||
}
|
||||
|
||||
@PostMapping("/list/all")
|
||||
public List<TestPlanCaseDTO> getTestPlanCases(@RequestBody QueryTestPlanCaseRequest request){
|
||||
return testPlanTestCaseService.getTestPlanCases(request);
|
||||
return testPlanTestCaseService.list(request);
|
||||
}
|
||||
|
||||
@PostMapping("/edit")
|
||||
public void editTestCase(@RequestBody TestPlanTestCase testPlanTestCase){
|
||||
public void editTestCase(@RequestBody TestPlanTestCaseWithBLOBs testPlanTestCase){
|
||||
testPlanTestCaseService.editTestCase(testPlanTestCase);
|
||||
}
|
||||
|
||||
|
@ -42,6 +58,11 @@ public class TestPlanTestCaseController {
|
|||
testPlanTestCaseService.editTestCaseBath(request);
|
||||
}
|
||||
|
||||
@PostMapping("/batch/delete")
|
||||
public void deleteTestCaseBath(@RequestBody TestPlanCaseBatchRequest request){
|
||||
testPlanTestCaseService.deleteTestCaseBath(request);
|
||||
}
|
||||
|
||||
@PostMapping("/delete/{id}")
|
||||
public int deleteTestCase(@PathVariable String id){
|
||||
return testPlanTestCaseService.deleteTestCase(id);
|
||||
|
|
|
@ -11,5 +11,5 @@ public class TestCaseReportModuleResultDTO {
|
|||
private Integer caseCount;
|
||||
private Integer passCount;
|
||||
private Double passRate;
|
||||
private Integer flawCount;
|
||||
private Integer issuesCount;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,12 @@ import lombok.Setter;
|
|||
@Getter
|
||||
@Setter
|
||||
public class TestPlanCaseDTO extends TestCaseWithBLOBs {
|
||||
|
||||
private String executor;
|
||||
private String executorName;
|
||||
private String status;
|
||||
private String results;
|
||||
private String planId;
|
||||
private String planName;
|
||||
private String caseId;
|
||||
private String issues;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.track.dto;
|
||||
|
||||
import io.metersphere.base.domain.TestPlan;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class TestPlanDTOWithMetric extends TestPlanDTO {
|
||||
private Double passRate;
|
||||
private Double testRate;
|
||||
private Integer passed;
|
||||
private Integer tested;
|
||||
private Integer total;
|
||||
}
|
|
@ -4,9 +4,12 @@ import io.metersphere.base.domain.TestPlan;
|
|||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class QueryTestPlanRequest extends TestPlan {
|
||||
|
||||
private boolean recent = false;
|
||||
private List<String> planIds;
|
||||
}
|
||||
|
|
|
@ -7,5 +7,4 @@ import lombok.Setter;
|
|||
@Setter
|
||||
public class QueryTestPlanRequest extends TestPlanRequest {
|
||||
private String workspaceId;
|
||||
private boolean recent = false; // 表示查询最近的测试
|
||||
}
|
||||
|
|
|
@ -18,6 +18,10 @@ public class QueryTestPlanCaseRequest extends TestPlanTestCase {
|
|||
|
||||
private Map<String, List<String>> filters;
|
||||
|
||||
private List<String> planIds;
|
||||
|
||||
private List<String> projectIds;
|
||||
|
||||
private String workspaceId;
|
||||
|
||||
private String name;
|
||||
|
|
|
@ -8,7 +8,9 @@ import io.metersphere.base.mapper.TestPlanMapper;
|
|||
import io.metersphere.base.mapper.TestPlanTestCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
||||
import io.metersphere.commons.constants.TestCaseConstants;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.dto.TestCaseNodeDTO;
|
||||
import io.metersphere.exception.ExcelException;
|
||||
|
@ -46,7 +48,8 @@ public class TestCaseNodeService {
|
|||
public String addNode(TestCaseNode node) {
|
||||
|
||||
if(node.getLevel() > TestCaseConstants.MAX_NODE_DEPTH){
|
||||
throw new RuntimeException("模块树最大深度为" + TestCaseConstants.MAX_NODE_DEPTH + "层!");
|
||||
throw new RuntimeException(Translator.get("test_case_node_level_tip")
|
||||
+ TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level"));
|
||||
}
|
||||
node.setCreateTime(System.currentTimeMillis());
|
||||
node.setUpdateTime(System.currentTimeMillis());
|
||||
|
@ -122,11 +125,11 @@ public class TestCaseNodeService {
|
|||
|
||||
testCases.forEach(testCase -> {
|
||||
StringBuilder path = new StringBuilder(testCase.getNodePath());
|
||||
List<String> list = Arrays.asList(path.toString().split("/"));
|
||||
list.set(request.getLevel(), request.getName());
|
||||
List<String> pathLists = Arrays.asList(path.toString().split("/"));
|
||||
pathLists.set(request.getLevel(), request.getName());
|
||||
path.delete( 0, path.length());
|
||||
for (int i = 1; i < list.size(); i++) {
|
||||
path = path.append("/").append(list.get(i));
|
||||
for (int i = 1; i < pathLists.size(); i++) {
|
||||
path = path.append("/").append(pathLists.get(i));
|
||||
}
|
||||
testCase.setNodePath(path.toString());
|
||||
});
|
||||
|
@ -243,7 +246,7 @@ public class TestCaseNodeService {
|
|||
nodePaths.forEach(path -> {
|
||||
|
||||
if (path == null) {
|
||||
throw new ExcelException("所属模块不能为空!");
|
||||
throw new ExcelException(Translator.get("test_case_module_not_null"));
|
||||
}
|
||||
List<String> nodeNameList = new ArrayList<>(Arrays.asList(path.split("/")));
|
||||
Iterator<String> pathIterator = nodeNameList.iterator();
|
||||
|
@ -252,7 +255,7 @@ public class TestCaseNodeService {
|
|||
String rootNodeName = null;
|
||||
|
||||
if (nodeNameList.size() <= 1) {
|
||||
throw new ExcelException("创建模块失败:" + path);
|
||||
throw new ExcelException(Translator.get("test_case_create_module_fail") + ":" + path);
|
||||
} else {
|
||||
pathIterator.next();
|
||||
pathIterator.remove();
|
||||
|
@ -371,7 +374,7 @@ public class TestCaseNodeService {
|
|||
|
||||
public void dragNode(DragNodeRequest request) {
|
||||
|
||||
editNode(request);
|
||||
// editNode(request);
|
||||
|
||||
List<String> nodeIds = request.getNodeIds();
|
||||
|
||||
|
@ -381,7 +384,7 @@ public class TestCaseNodeService {
|
|||
|
||||
List<TestCaseNode> updateNodes = new ArrayList<>();
|
||||
|
||||
buildUpdateTestCase(nodeTree, testCases, updateNodes, "/", 1);
|
||||
buildUpdateTestCase(nodeTree, testCases, updateNodes, "/", null, 1);
|
||||
|
||||
updateNodes = updateNodes.stream()
|
||||
.filter(item -> nodeIds.contains(item.getId()))
|
||||
|
@ -417,13 +420,18 @@ public class TestCaseNodeService {
|
|||
}
|
||||
|
||||
private void buildUpdateTestCase(TestCaseNodeDTO rootNode, List<TestCaseDTO> testCases,
|
||||
List<TestCaseNode> updateNodes, String rootPath, int level) {
|
||||
List<TestCaseNode> updateNodes, String rootPath, String pId, int level) {
|
||||
|
||||
rootPath = rootPath + rootNode.getName();
|
||||
|
||||
if (level > 5) {
|
||||
MSException.throwException(Translator.get("node_deep_limit"));
|
||||
}
|
||||
|
||||
TestCaseNode testCaseNode = new TestCaseNode();
|
||||
testCaseNode.setId(rootNode.getId());
|
||||
testCaseNode.setLevel(level);
|
||||
testCaseNode.setParentId(pId);
|
||||
updateNodes.add(testCaseNode);
|
||||
|
||||
for (TestCaseDTO item : testCases) {
|
||||
|
@ -435,7 +443,7 @@ public class TestCaseNodeService {
|
|||
List<TestCaseNodeDTO> children = rootNode.getChildren();
|
||||
if (children != null && children.size() > 0){
|
||||
for (int i = 0; i < children.size(); i++) {
|
||||
buildUpdateTestCase(children.get(i), testCases, updateNodes, rootPath + '/', level + 1);
|
||||
buildUpdateTestCase(children.get(i), testCases, updateNodes, rootPath + '/', rootNode.getId(), level + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,21 +35,6 @@ public class TestCaseReportService {
|
|||
@Resource
|
||||
TestCaseReportTemplateMapper testCaseReportTemplateMapper;
|
||||
|
||||
@Resource
|
||||
TestCaseNodeService testCaseNodeService;
|
||||
|
||||
@Resource
|
||||
TestCaseNodeMapper testCaseNodeMapper;
|
||||
|
||||
@Resource
|
||||
ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
|
||||
|
||||
@Resource
|
||||
TestPlanTestCaseMapper testPlanTestCaseMapper;
|
||||
|
||||
@Resource
|
||||
ExtTestCaseMapper extTestCaseMapper;
|
||||
|
||||
public List<TestCaseReport> listTestCaseReport(TestCaseReport request) {
|
||||
TestCaseReportExample example = new TestCaseReportExample();
|
||||
if ( StringUtils.isNotBlank(request.getName()) ) {
|
||||
|
@ -87,111 +72,4 @@ public class TestCaseReportService {
|
|||
testPlanMapper.updateByPrimaryKeySelective(testPlan);
|
||||
return report.getId();
|
||||
}
|
||||
|
||||
public TestCaseReportMetricDTO getMetric(String planId) {
|
||||
|
||||
QueryTestPlanRequest queryTestPlanRequest = new QueryTestPlanRequest();
|
||||
queryTestPlanRequest.setId(planId);
|
||||
TestPlanDTO testPlan = extTestPlanMapper.list(queryTestPlanRequest).get(0);
|
||||
|
||||
Set<String> executors = new HashSet<>();
|
||||
Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap = new HashMap<>();
|
||||
|
||||
TestCaseNodeExample testCaseNodeExample = new TestCaseNodeExample();
|
||||
testCaseNodeExample.createCriteria().andProjectIdEqualTo(testPlan.getProjectId());
|
||||
List<TestCaseNode> nodes = testCaseNodeMapper.selectByExample(testCaseNodeExample);
|
||||
|
||||
List<TestCaseNodeDTO> nodeTrees = testCaseNodeService.getNodeTrees(nodes);
|
||||
|
||||
Map<String, Set<String>> childIdMap = new HashMap<>();
|
||||
nodeTrees.forEach(item -> {
|
||||
Set<String> childIds = new HashSet<>();
|
||||
getChildIds(item, childIds);
|
||||
childIdMap.put(item.getId(), childIds);
|
||||
});
|
||||
|
||||
QueryTestPlanCaseRequest request = new QueryTestPlanCaseRequest();
|
||||
request.setPlanId(planId);
|
||||
List<TestPlanCaseDTO> testPlanTestCases = extTestPlanTestCaseMapper.list(request);
|
||||
|
||||
Map<String, TestCaseReportModuleResultDTO> moduleResultMap = new HashMap<>();
|
||||
|
||||
for (TestPlanCaseDTO testCase: testPlanTestCases) {
|
||||
executors.add(testCase.getExecutor());
|
||||
getStatusResultMap(reportStatusResultMap, testCase);
|
||||
getModuleResultMap(childIdMap, moduleResultMap, testCase, nodeTrees);
|
||||
}
|
||||
|
||||
|
||||
nodeTrees.forEach(rootNode -> {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNode.getId());
|
||||
if (moduleResult != null) {
|
||||
moduleResult.setModuleName(rootNode.getName());
|
||||
}
|
||||
});
|
||||
|
||||
for (TestCaseReportModuleResultDTO moduleResult : moduleResultMap.values()) {
|
||||
moduleResult.setPassRate(new BigDecimal(moduleResult.getPassCount()*1.0f/moduleResult.getCaseCount())
|
||||
.setScale(2, BigDecimal.ROUND_HALF_UP)
|
||||
.doubleValue() * 100);
|
||||
if (moduleResult.getCaseCount() <= 0) {
|
||||
moduleResultMap.remove(moduleResult.getModuleId());
|
||||
}
|
||||
}
|
||||
|
||||
TestCaseReportMetricDTO testCaseReportMetricDTO = new TestCaseReportMetricDTO();
|
||||
testCaseReportMetricDTO.setProjectName(testPlan.getProjectName());
|
||||
testCaseReportMetricDTO.setPrincipal(testPlan.getPrincipal());
|
||||
testCaseReportMetricDTO.setExecutors(new ArrayList<>(executors));
|
||||
testCaseReportMetricDTO.setExecuteResult(new ArrayList<>(reportStatusResultMap.values()));
|
||||
testCaseReportMetricDTO.setModuleExecuteResult(new ArrayList<>(moduleResultMap.values()));
|
||||
|
||||
return testCaseReportMetricDTO;
|
||||
}
|
||||
|
||||
private void getStatusResultMap(Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap, TestPlanCaseDTO testCase) {
|
||||
TestCaseReportStatusResultDTO statusResult = reportStatusResultMap.get(testCase.getStatus());
|
||||
if (statusResult == null) {
|
||||
statusResult = new TestCaseReportStatusResultDTO();
|
||||
statusResult.setStatus(testCase.getStatus());
|
||||
statusResult.setCount(0);
|
||||
}
|
||||
statusResult.setCount(statusResult.getCount() + 1);
|
||||
reportStatusResultMap.put(testCase.getStatus(), statusResult);
|
||||
}
|
||||
|
||||
private void getModuleResultMap(Map<String, Set<String>> childIdMap, Map<String, TestCaseReportModuleResultDTO> moduleResultMap, TestPlanCaseDTO testCase, List<TestCaseNodeDTO> nodeTrees) {
|
||||
childIdMap.forEach((rootNodeId, childIds) -> {
|
||||
if (childIds.contains(testCase.getNodeId())) {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNodeId);
|
||||
if (moduleResult == null) {
|
||||
moduleResult = new TestCaseReportModuleResultDTO();
|
||||
moduleResult.setCaseCount(0);
|
||||
moduleResult.setPassCount(0);
|
||||
moduleResult.setModuleId(rootNodeId);
|
||||
}
|
||||
moduleResult.setCaseCount(moduleResult.getCaseCount() + 1);
|
||||
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Pass.name())) {
|
||||
moduleResult.setPassCount(moduleResult.getPassCount() + 1);
|
||||
}
|
||||
moduleResultMap.put(rootNodeId, moduleResult);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getChildIds(TestCaseNodeDTO rootNode, Set<String> childIds) {
|
||||
|
||||
childIds.add(rootNode.getId());
|
||||
|
||||
List<TestCaseNodeDTO> children = rootNode.getChildren();
|
||||
|
||||
if(children != null) {
|
||||
Iterator<TestCaseNodeDTO> iterator = children.iterator();
|
||||
while(iterator.hasNext()){
|
||||
getChildIds(iterator.next(), childIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,14 +9,13 @@ import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
|||
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.SessionUtils;
|
||||
import io.metersphere.excel.domain.ExcelErrData;
|
||||
import io.metersphere.excel.domain.ExcelResponse;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.excel.listener.EasyExcelListener;
|
||||
import io.metersphere.excel.listener.TestCaseDataListener;
|
||||
import io.metersphere.excel.utils.EasyExcelUtil;
|
||||
import io.metersphere.excel.utils.EasyExcelExporter;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
|
@ -31,7 +30,6 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -170,43 +168,40 @@ public class TestCaseService {
|
|||
|
||||
public ExcelResponse testCaseImport(MultipartFile file, String projectId) {
|
||||
|
||||
ExcelResponse excelResponse = new ExcelResponse();
|
||||
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
|
||||
queryTestCaseRequest.setProjectId(projectId);
|
||||
List<TestCase> testCases = extTestCaseMapper.getTestCaseNames(queryTestCaseRequest);
|
||||
Set<String> testCaseNames = testCases.stream()
|
||||
.map(TestCase::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
UserExample userExample = new UserExample();
|
||||
userExample.createCriteria().andLastWorkspaceIdEqualTo(currentWorkspaceId);
|
||||
List<User> users = userMapper.selectByExample(userExample);
|
||||
Set<String> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
|
||||
|
||||
EasyExcelListener easyExcelListener = null;
|
||||
List<ExcelErrData<TestCaseExcelData>> errList = null;
|
||||
try {
|
||||
|
||||
ExcelResponse excelResponse = new ExcelResponse();
|
||||
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
|
||||
queryTestCaseRequest.setProjectId(projectId);
|
||||
List<TestCase> testCases = extTestCaseMapper.getTestCaseNames(queryTestCaseRequest);
|
||||
Set<String> testCaseNames = testCases.stream()
|
||||
.map(TestCase::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
UserExample userExample = new UserExample();
|
||||
userExample.createCriteria().andLastWorkspaceIdEqualTo(currentWorkspaceId);
|
||||
List<User> users = userMapper.selectByExample(userExample);
|
||||
Set<String> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
|
||||
|
||||
EasyExcelListener easyExcelListener = new TestCaseDataListener(this, projectId,
|
||||
testCaseNames, userIds, TestCaseExcelData.class);
|
||||
easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds);
|
||||
EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
|
||||
|
||||
List<ExcelErrData<TestCaseExcelData>> errList = easyExcelListener.getErrList();
|
||||
//如果包含错误信息就导出错误信息
|
||||
if (!errList.isEmpty()) {
|
||||
excelResponse.setSuccess(false);
|
||||
excelResponse.setErrList(errList);
|
||||
} else {
|
||||
excelResponse.setSuccess(true);
|
||||
}
|
||||
return excelResponse;
|
||||
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
e.printStackTrace();
|
||||
errList = easyExcelListener.getErrList();
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e.getMessage());
|
||||
} finally {
|
||||
easyExcelListener.close();
|
||||
}
|
||||
|
||||
return null;
|
||||
//如果包含错误信息就导出错误信息
|
||||
if (!errList.isEmpty()) {
|
||||
excelResponse.setSuccess(false);
|
||||
excelResponse.setErrList(errList);
|
||||
} else {
|
||||
excelResponse.setSuccess(true);
|
||||
}
|
||||
return excelResponse;
|
||||
}
|
||||
|
||||
public void saveImportData(List<TestCaseWithBLOBs> testCases, String projectId) {
|
||||
|
@ -224,7 +219,16 @@ public class TestCaseService {
|
|||
}
|
||||
|
||||
public void testCaseTemplateExport(HttpServletResponse response) {
|
||||
EasyExcelUtil.export(response, TestCaseExcelData.class, generateExportTemplate(), "测试用例模版", "模版");
|
||||
EasyExcelExporter easyExcelExporter = null;
|
||||
try {
|
||||
easyExcelExporter = new EasyExcelExporter();
|
||||
easyExcelExporter.export(response, TestCaseExcelData.class, generateExportTemplate(),
|
||||
Translator.get("test_case_import_template_name"), Translator.get("test_case_import_template_sheet"));
|
||||
} catch (Exception e) {
|
||||
MSException.throwException(e);
|
||||
} finally {
|
||||
easyExcelExporter.close();
|
||||
}
|
||||
}
|
||||
|
||||
private List<TestCaseExcelData> generateExportTemplate() {
|
||||
|
@ -235,28 +239,29 @@ public class TestCaseService {
|
|||
SessionUser user = SessionUtils.getUser();
|
||||
for (int i = 1; i <= 5; i++) {
|
||||
TestCaseExcelData data = new TestCaseExcelData();
|
||||
data.setName("测试用例" + i);
|
||||
path.append("/" + "模块" + i);
|
||||
data.setName(Translator.get("test_case") + i);
|
||||
path.append("/" + Translator.get("module") + i);
|
||||
data.setNodePath(path.toString());
|
||||
data.setPriority("P" + i%4);
|
||||
data.setType(types.get(i%3));
|
||||
data.setMethod(methods.get(i%2));
|
||||
data.setPrerequisite("前置条件选填");
|
||||
data.setStepDesc("1. 每个步骤以换行分隔\n2. 步骤前可标序号\n3. 测试步骤和结果选填");
|
||||
data.setStepResult("1. 每条结果以换行分隔\n2. 结果前可标序号\n3. 测试步骤和结果选填");
|
||||
data.setPrerequisite(Translator.get("preconditions_optional"));
|
||||
data.setStepDesc("1. " + Translator.get("step_tip_separate") +
|
||||
"\n2. " + Translator.get("step_tip_order") + "\n3. " + Translator.get("step_tip_optional"));
|
||||
data.setStepResult("1. " + Translator.get("step_tip_order") + "\n2. " + Translator.get("result_tip_order") + "\n3. " + Translator.get("result_tip_optional"));
|
||||
data.setMaintainer(user.getId());
|
||||
data.setRemark("备注选填");
|
||||
data.setRemark(Translator.get("remark_optional"));
|
||||
list.add(data);
|
||||
}
|
||||
|
||||
list.add(new TestCaseExcelData());
|
||||
TestCaseExcelData explain = new TestCaseExcelData();
|
||||
explain.setName("同一项目下测试用例名称不能重复!");
|
||||
explain.setNodePath("模块名称请按照'/模块1/模块2'的格式书写; 错误格式示例:('/', '/tes//test'); 若无该模块,则自动创建模块");
|
||||
explain.setType("用例类型必须为:functional、performance、api");
|
||||
explain.setMethod("测试方式必须为:manual、auto");
|
||||
explain.setPriority("优先级必须为:P0、P1、P2、P3");
|
||||
explain.setMaintainer("维护人必须为该工作空间相关人员");
|
||||
explain.setName(Translator.get("do_not_modify_header_order"));
|
||||
explain.setNodePath(Translator.get("module_created_automatically"));
|
||||
explain.setType(Translator.get("options") + "(functional、performance、api)");
|
||||
explain.setMethod(Translator.get("options") + "(manual、auto)");
|
||||
explain.setPriority(Translator.get("options") + "(P0、P1、P2、P3)");
|
||||
explain.setMaintainer(Translator.get("please_input_workspace_member"));
|
||||
|
||||
list.add(explain);
|
||||
return list;
|
||||
|
@ -273,4 +278,10 @@ public class TestCaseService {
|
|||
testCaseExample);
|
||||
|
||||
}
|
||||
|
||||
public void deleteTestCaseBath(TestCaseBatchRequest request) {
|
||||
TestCaseExample example = new TestCaseExample();
|
||||
example.createCriteria().andIdIn(request.getIds());
|
||||
testCaseMapper.deleteByExample(example);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,17 +1,27 @@
|
|||
package io.metersphere.track.service;
|
||||
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.TestCaseMapper;
|
||||
import io.metersphere.base.mapper.TestCaseNodeMapper;
|
||||
import io.metersphere.base.mapper.TestPlanMapper;
|
||||
import io.metersphere.base.mapper.TestPlanTestCaseMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtProjectMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtTestPlanTestCaseMapper;
|
||||
import io.metersphere.commons.constants.TestPlanStatus;
|
||||
import io.metersphere.commons.constants.TestPlanTestCaseStatus;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.controller.request.ProjectRequest;
|
||||
import io.metersphere.dto.ProjectDTO;
|
||||
import io.metersphere.track.dto.*;
|
||||
import io.metersphere.track.request.testcase.PlanCaseRelevanceRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
|
@ -20,6 +30,7 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -33,6 +44,9 @@ public class TestPlanService {
|
|||
@Resource
|
||||
ExtTestPlanMapper extTestPlanMapper;
|
||||
|
||||
@Resource
|
||||
ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
|
||||
|
||||
@Resource
|
||||
TestCaseMapper testCaseMapper;
|
||||
|
||||
|
@ -42,6 +56,15 @@ public class TestPlanService {
|
|||
@Resource
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
|
||||
@Resource
|
||||
TestCaseNodeMapper testCaseNodeMapper;
|
||||
|
||||
@Resource
|
||||
TestCaseNodeService testCaseNodeService;
|
||||
|
||||
@Resource
|
||||
ExtProjectMapper extProjectMapper;
|
||||
|
||||
public void addTestPlan(TestPlan testPlan) {
|
||||
testPlan.setId(UUID.randomUUID().toString());
|
||||
testPlan.setStatus(TestPlanStatus.Prepare.name());
|
||||
|
@ -95,7 +118,7 @@ public class TestPlanService {
|
|||
if (!testCaseIds.isEmpty()) {
|
||||
testCaseIds.forEach(caseId -> {
|
||||
TestCaseWithBLOBs testCase = testCaseMap.get(caseId);
|
||||
TestPlanTestCase testPlanTestCase = new TestPlanTestCase();
|
||||
TestPlanTestCaseWithBLOBs testPlanTestCase = new TestPlanTestCaseWithBLOBs();
|
||||
testPlanTestCase.setId(UUID.randomUUID().toString());
|
||||
testPlanTestCase.setExecutor(testCase.getMaintainer());
|
||||
testPlanTestCase.setCaseId(caseId);
|
||||
|
@ -132,4 +155,183 @@ public class TestPlanService {
|
|||
testPlanExample.createCriteria().andWorkspaceIdEqualTo(currentWorkspaceId);
|
||||
return testPlanMapper.selectByExample(testPlanExample);
|
||||
}
|
||||
|
||||
public List<TestPlanDTOWithMetric> listRelateAllPlan() {
|
||||
SessionUser user = SessionUtils.getUser();
|
||||
|
||||
QueryTestPlanRequest request = new QueryTestPlanRequest();
|
||||
request.setPrincipal(user.getId());
|
||||
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
request.setPlanIds(extTestPlanTestCaseMapper.findRelateTestPlanId(user.getId()));
|
||||
|
||||
List<String> projectIds = extProjectMapper.getProjectIdByWorkspaceId(SessionUtils.getCurrentOrganizationId());
|
||||
|
||||
List<TestPlanDTOWithMetric> testPlans = extTestPlanMapper.listRelate(request);
|
||||
|
||||
Map<String, List<TestPlanCaseDTO>> testCaseMap = new HashMap<>();
|
||||
listTestCaseByProjectIds(projectIds).forEach(testCase -> {
|
||||
List<TestPlanCaseDTO> list = testCaseMap.get(testCase.getPlanId());
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
list.add(testCase);
|
||||
testCaseMap.put(testCase.getPlanId(), list);
|
||||
} else {
|
||||
list.add(testCase);
|
||||
}
|
||||
});
|
||||
|
||||
testPlans.forEach(testPlan -> {
|
||||
List<TestPlanCaseDTO> testCases = testCaseMap.get(testPlan.getId());
|
||||
testPlan.setTested(0);
|
||||
testPlan.setPassed(0);
|
||||
testPlan.setTotal(0);
|
||||
if (testCases != null) {
|
||||
testPlan.setTotal(testCases.size());
|
||||
testCases.forEach(testCase -> {
|
||||
if (!StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Prepare.name())
|
||||
&& !StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Underway.name())) {
|
||||
testPlan.setTested(testPlan.getTested() + 1);
|
||||
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Pass.name())) {
|
||||
testPlan.setPassed(testPlan.getPassed() + 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
testPlan.setPassRate(getPercentWithTwoDecimals(testPlan.getTested() == 0 ? 0 : testPlan.getPassed()*1.0/testPlan.getTested()));
|
||||
testPlan.setTestRate(getPercentWithTwoDecimals(testPlan.getTotal() == 0 ? 0 : testPlan.getTested()*1.0/testPlan.getTotal()));
|
||||
});
|
||||
|
||||
return testPlans;
|
||||
}
|
||||
|
||||
private double getPercentWithTwoDecimals(double value) {
|
||||
return new BigDecimal(value)
|
||||
.setScale(4, BigDecimal.ROUND_HALF_UP)
|
||||
.doubleValue() * 100;
|
||||
}
|
||||
|
||||
public List<TestPlanCaseDTO> listTestCaseByPlanId(String planId) {
|
||||
QueryTestPlanCaseRequest request = new QueryTestPlanCaseRequest();
|
||||
request.setPlanId(planId);
|
||||
return extTestPlanTestCaseMapper.list(request);
|
||||
}
|
||||
|
||||
public List<TestPlanCaseDTO> listTestCaseByProjectIds(List<String> projectIds) {
|
||||
QueryTestPlanCaseRequest request = new QueryTestPlanCaseRequest();
|
||||
request.setProjectIds(projectIds);
|
||||
return extTestPlanTestCaseMapper.list(request);
|
||||
}
|
||||
|
||||
public TestCaseReportMetricDTO getMetric(String planId) {
|
||||
|
||||
QueryTestPlanRequest queryTestPlanRequest = new QueryTestPlanRequest();
|
||||
queryTestPlanRequest.setId(planId);
|
||||
TestPlanDTO testPlan = extTestPlanMapper.list(queryTestPlanRequest).get(0);
|
||||
|
||||
Set<String> executors = new HashSet<>();
|
||||
Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap = new HashMap<>();
|
||||
|
||||
TestCaseNodeExample testCaseNodeExample = new TestCaseNodeExample();
|
||||
testCaseNodeExample.createCriteria().andProjectIdEqualTo(testPlan.getProjectId());
|
||||
List<TestCaseNode> nodes = testCaseNodeMapper.selectByExample(testCaseNodeExample);
|
||||
|
||||
List<TestCaseNodeDTO> nodeTrees = testCaseNodeService.getNodeTrees(nodes);
|
||||
|
||||
Map<String, Set<String>> childIdMap = new HashMap<>();
|
||||
nodeTrees.forEach(item -> {
|
||||
Set<String> childIds = new HashSet<>();
|
||||
getChildIds(item, childIds);
|
||||
childIdMap.put(item.getId(), childIds);
|
||||
});
|
||||
|
||||
List<TestPlanCaseDTO> testPlanTestCases = listTestCaseByPlanId(planId);
|
||||
|
||||
Map<String, TestCaseReportModuleResultDTO> moduleResultMap = new HashMap<>();
|
||||
|
||||
for (TestPlanCaseDTO testCase: testPlanTestCases) {
|
||||
executors.add(testCase.getExecutor());
|
||||
getStatusResultMap(reportStatusResultMap, testCase);
|
||||
getModuleResultMap(childIdMap, moduleResultMap, testCase, nodeTrees);
|
||||
}
|
||||
|
||||
nodeTrees.forEach(rootNode -> {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNode.getId());
|
||||
if (moduleResult != null) {
|
||||
moduleResult.setModuleName(rootNode.getName());
|
||||
}
|
||||
});
|
||||
|
||||
for (TestCaseReportModuleResultDTO moduleResult : moduleResultMap.values()) {
|
||||
moduleResult.setPassRate(getPercentWithTwoDecimals(moduleResult.getPassCount()*1.0f/moduleResult.getCaseCount()));
|
||||
if (moduleResult.getCaseCount() <= 0) {
|
||||
moduleResultMap.remove(moduleResult.getModuleId());
|
||||
}
|
||||
}
|
||||
|
||||
TestCaseReportMetricDTO testCaseReportMetricDTO = new TestCaseReportMetricDTO();
|
||||
testCaseReportMetricDTO.setProjectName(testPlan.getProjectName());
|
||||
testCaseReportMetricDTO.setPrincipal(testPlan.getPrincipal());
|
||||
testCaseReportMetricDTO.setExecutors(new ArrayList<>(executors));
|
||||
testCaseReportMetricDTO.setExecuteResult(new ArrayList<>(reportStatusResultMap.values()));
|
||||
testCaseReportMetricDTO.setModuleExecuteResult(new ArrayList<>(moduleResultMap.values()));
|
||||
|
||||
return testCaseReportMetricDTO;
|
||||
}
|
||||
|
||||
private void getStatusResultMap(Map<String, TestCaseReportStatusResultDTO> reportStatusResultMap, TestPlanCaseDTO testCase) {
|
||||
TestCaseReportStatusResultDTO statusResult = reportStatusResultMap.get(testCase.getStatus());
|
||||
if (statusResult == null) {
|
||||
statusResult = new TestCaseReportStatusResultDTO();
|
||||
statusResult.setStatus(testCase.getStatus());
|
||||
statusResult.setCount(0);
|
||||
}
|
||||
statusResult.setCount(statusResult.getCount() + 1);
|
||||
reportStatusResultMap.put(testCase.getStatus(), statusResult);
|
||||
}
|
||||
|
||||
private void getModuleResultMap(Map<String, Set<String>> childIdMap, Map<String, TestCaseReportModuleResultDTO> moduleResultMap, TestPlanCaseDTO testCase, List<TestCaseNodeDTO> nodeTrees) {
|
||||
childIdMap.forEach((rootNodeId, childIds) -> {
|
||||
if (childIds.contains(testCase.getNodeId())) {
|
||||
TestCaseReportModuleResultDTO moduleResult = moduleResultMap.get(rootNodeId);
|
||||
if (moduleResult == null) {
|
||||
moduleResult = new TestCaseReportModuleResultDTO();
|
||||
moduleResult.setCaseCount(0);
|
||||
moduleResult.setPassCount(0);
|
||||
moduleResult.setIssuesCount(0);
|
||||
moduleResult.setModuleId(rootNodeId);
|
||||
}
|
||||
moduleResult.setCaseCount(moduleResult.getCaseCount() + 1);
|
||||
if (StringUtils.equals(testCase.getStatus(), TestPlanTestCaseStatus.Pass.name())) {
|
||||
moduleResult.setPassCount(moduleResult.getPassCount() + 1);
|
||||
}
|
||||
if (StringUtils.isNotBlank(testCase.getIssues())) {
|
||||
if (JSON.parseObject(testCase.getIssues()).getBoolean("hasIssues")) {
|
||||
moduleResult.setIssuesCount(moduleResult.getIssuesCount() + 1);
|
||||
};
|
||||
}
|
||||
moduleResultMap.put(rootNodeId, moduleResult);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getChildIds(TestCaseNodeDTO rootNode, Set<String> childIds) {
|
||||
|
||||
childIds.add(rootNode.getId());
|
||||
|
||||
List<TestCaseNodeDTO> children = rootNode.getChildren();
|
||||
|
||||
if(children != null) {
|
||||
Iterator<TestCaseNodeDTO> iterator = children.iterator();
|
||||
while(iterator.hasNext()){
|
||||
getChildIds(iterator.next(), childIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<TestPlan> getTestPlanByTestIds(List<String> planIds) {
|
||||
TestPlanExample example = new TestPlanExample();
|
||||
example.createCriteria().andIdIn(planIds);
|
||||
return testPlanMapper.selectByExample(example);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
package io.metersphere.track.service;
|
||||
|
||||
import io.metersphere.base.domain.TestPlanTestCase;
|
||||
import io.metersphere.base.domain.TestPlanTestCaseExample;
|
||||
import io.metersphere.base.domain.User;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.TestPlanTestCaseMapper;
|
||||
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.SessionUtils;
|
||||
import io.metersphere.controller.request.member.QueryMemberRequest;
|
||||
import io.metersphere.service.UserService;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanDTO;
|
||||
import io.metersphere.track.request.testcase.TestPlanCaseBatchRequest;
|
||||
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -18,6 +19,7 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -32,10 +34,13 @@ public class TestPlanTestCaseService {
|
|||
@Resource
|
||||
UserService userService;
|
||||
|
||||
@Resource
|
||||
TestPlanService testPlanService;
|
||||
|
||||
@Resource
|
||||
ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
|
||||
|
||||
public List<TestPlanCaseDTO> getTestPlanCases(QueryTestPlanCaseRequest request) {
|
||||
public List<TestPlanCaseDTO> list(QueryTestPlanCaseRequest request) {
|
||||
List<TestPlanCaseDTO> list = extTestPlanTestCaseMapper.list(request);
|
||||
QueryMemberRequest queryMemberRequest = new QueryMemberRequest();
|
||||
queryMemberRequest.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
|
||||
|
@ -47,7 +52,7 @@ public class TestPlanTestCaseService {
|
|||
return list;
|
||||
}
|
||||
|
||||
public void editTestCase(TestPlanTestCase testPlanTestCase) {
|
||||
public void editTestCase(TestPlanTestCaseWithBLOBs testPlanTestCase) {
|
||||
if (StringUtils.equals(TestPlanTestCaseStatus.Prepare.name(), testPlanTestCase.getStatus())) {
|
||||
testPlanTestCase.setStatus(TestPlanTestCaseStatus.Underway.name());
|
||||
}
|
||||
|
@ -63,10 +68,55 @@ public class TestPlanTestCaseService {
|
|||
TestPlanTestCaseExample testPlanTestCaseExample = new TestPlanTestCaseExample();
|
||||
testPlanTestCaseExample.createCriteria().andIdIn(request.getIds());
|
||||
|
||||
TestPlanTestCase testPlanTestCase = new TestPlanTestCase();
|
||||
TestPlanTestCaseWithBLOBs testPlanTestCase = new TestPlanTestCaseWithBLOBs();
|
||||
BeanUtils.copyBean(testPlanTestCase, request);
|
||||
testPlanTestCaseMapper.updateByExampleSelective(
|
||||
testPlanTestCase,
|
||||
testPlanTestCaseExample);
|
||||
}
|
||||
|
||||
public List<TestPlanCaseDTO> getRecentTestCases(QueryTestPlanCaseRequest request, int count) {
|
||||
buildQueryRequest(request, count);
|
||||
if (request.getPlanIds().isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
List<TestPlanCaseDTO> recentTestedTestCase = extTestPlanTestCaseMapper.getRecentTestedTestCase(request);
|
||||
List<String> planIds = recentTestedTestCase.stream().map(TestPlanCaseDTO::getPlanId).collect(Collectors.toList());
|
||||
|
||||
Map<String, String> testPlanMap = testPlanService.getTestPlanByTestIds(planIds).stream()
|
||||
.collect(Collectors.toMap(TestPlan::getId, TestPlan::getName));
|
||||
|
||||
recentTestedTestCase.forEach(testCase -> {
|
||||
testCase.setPlanName(testPlanMap.get(testCase.getPlanId()));
|
||||
});
|
||||
return recentTestedTestCase;
|
||||
}
|
||||
|
||||
public List<TestPlanCaseDTO> getPendingTestCases(QueryTestPlanCaseRequest request, int count) {
|
||||
buildQueryRequest(request, count);
|
||||
if (request.getPlanIds().isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return extTestPlanTestCaseMapper.getPendingTestCases(request);
|
||||
}
|
||||
|
||||
public void buildQueryRequest(QueryTestPlanCaseRequest request, int count) {
|
||||
SessionUser user = SessionUtils.getUser();
|
||||
List<String> relateTestPlanIds = extTestPlanTestCaseMapper.findRelateTestPlanId(user.getId());
|
||||
PageHelper.startPage(1, count, true);
|
||||
request.setPlanIds(relateTestPlanIds);
|
||||
request.setExecutor(user.getId());
|
||||
}
|
||||
|
||||
public TestPlanCaseDTO get(String caseId) {
|
||||
QueryTestPlanCaseRequest request = new QueryTestPlanCaseRequest();
|
||||
request.setId(caseId);
|
||||
return extTestPlanTestCaseMapper.list(request).get(0);
|
||||
}
|
||||
|
||||
public void deleteTestCaseBath(TestPlanCaseBatchRequest request) {
|
||||
TestPlanTestCaseExample example = new TestPlanTestCaseExample();
|
||||
example.createCriteria().andIdIn(request.getIds());
|
||||
testPlanTestCaseMapper.deleteByExample(example);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -317,6 +317,7 @@ CREATE TABLE IF NOT EXISTS `test_plan_test_case` (
|
|||
`executor` varchar(64) NOT NULL COMMENT 'Test case executor',
|
||||
`status` varchar(15) NULL COMMENT 'Test case status',
|
||||
`results` longtext COMMENT 'Test case result',
|
||||
`issues` longtext COMMENT 'Test case result issues',
|
||||
`remark` varchar(255) DEFAULT NULL COMMENT 'Test case remark',
|
||||
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
|
||||
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
|
||||
|
|
|
@ -4,10 +4,18 @@ VALUES ('admin', 'Administrator', 'admin@fit2cloud.com', md5('fit2cloud'), '1',
|
|||
INSERT INTO user_role (id, user_id, role_id, source_id, create_time, update_time)
|
||||
VALUES (uuid(), 'admin', 'admin', '1', 1581576575948, 1581576575948);
|
||||
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time) VALUES ('admin', '系统管理员', null, null, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time) VALUES ('org_admin', '组织管理员', null, null, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time) VALUES ('test_manager', '测试经理', null, null, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time) VALUES ('test_user', '测试人员', null, null, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time) VALUES ('test_viewer', 'Viewer', null, null, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time)
|
||||
VALUES ('admin', '系统管理员', NULL, NULL, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time)
|
||||
VALUES ('org_admin', '组织管理员', NULL, NULL, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time)
|
||||
VALUES ('test_manager', '测试经理', NULL, NULL, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time)
|
||||
VALUES ('test_user', '测试人员', NULL, NULL, 1581576575948, 1581576575948);
|
||||
INSERT INTO role (id, name, description, type, create_time, update_time)
|
||||
VALUES ('test_viewer', 'Viewer', NULL, NULL, 1581576575948, 1581576575948);
|
||||
|
||||
INSERT INTO test_case_report_template (id,name,content) VALUES (uuid(), 'default','{\"components\": [1,2,3,4,5]}');
|
||||
INSERT INTO test_case_report_template (id, name, content)
|
||||
VALUES (uuid(), 'default', '{\"components\": [1,2,3,4,5]}');
|
||||
INSERT INTO system_parameter (param_key, param_value, type, sort)
|
||||
VALUES ('default.language', 'zh_CN', 'text', 5);
|
|
@ -59,21 +59,7 @@
|
|||
|
||||
<!--要生成的数据库表 -->
|
||||
|
||||
<table tableName="test_plan"/>
|
||||
<table tableName="test_case_node"/>
|
||||
<table tableName="test_case"/>
|
||||
<table tableName="test_plan_test_case"/>
|
||||
<table tableName="test_case_report_template"/>
|
||||
<table tableName="test_case_report"/>
|
||||
|
||||
|
||||
<!--<table tableName="test_case_node">-->
|
||||
<!--<generatedKey column="id" sqlStatement="MySql" identity="true"/>-->
|
||||
<!--</table>-->
|
||||
|
||||
<!--<table tableName="test_case_report_template">-->
|
||||
<!--<generatedKey column="id" sqlStatement="MySql" identity="true"/>-->
|
||||
<!--</table>-->
|
||||
|
||||
</context>
|
||||
</generatorConfiguration>
|
|
@ -1,2 +1,44 @@
|
|||
test_case_exist=
|
||||
before_delete_plan=
|
||||
before_delete_plan=
|
||||
test_case_node_level_tip=
|
||||
test_case_node_level=
|
||||
test_case_module_not_null=
|
||||
test_case_create_module_fail=
|
||||
test_case_import_template_name=
|
||||
test_case_import_template_sheet=
|
||||
module_not_null=
|
||||
user_not_exists=
|
||||
test_case_already_exists=
|
||||
parse_data_error=
|
||||
missing_header_information=
|
||||
number=
|
||||
row=
|
||||
error=
|
||||
incorrect_format=
|
||||
test_case_type_validate=
|
||||
test_case_priority_validate=
|
||||
test_case_method_validate=
|
||||
test_case_name=
|
||||
test_case_module=
|
||||
test_case_type=
|
||||
test_case_maintainer=
|
||||
test_case_priority=
|
||||
test_case_method=
|
||||
test_case_prerequisite=
|
||||
test_case_remark=
|
||||
test_case_step_desc=
|
||||
test_case_step_result=
|
||||
test_case=
|
||||
module=
|
||||
preconditions_optional=
|
||||
step_tip_separate=
|
||||
step_tip_order=
|
||||
step_tip_optional=
|
||||
result_tip_separate=
|
||||
result_tip_order=
|
||||
result_tip_optional=
|
||||
remark_optional=
|
||||
do_not_modify_header_order=
|
||||
module_created_automatically=
|
||||
options=
|
||||
please_input_workspace_member=
|
|
@ -1,36 +1,85 @@
|
|||
test_case_exist=A test case already exists under this project:
|
||||
#commons
|
||||
error_lang_invalid=Invalid language parameter
|
||||
load_test_already_exists=Duplicate load test name
|
||||
project_name_is_null=Project name cannot be null
|
||||
project_name_already_exists=The project name already exists
|
||||
workspace_name_is_null=Workspace name cannot be null
|
||||
workspace_name_already_exists=The workspace name already exists
|
||||
workspace_does_not_belong_to_user=The current workspace does not belong to the current user
|
||||
organization_does_not_belong_to_user=The current organization does not belong to the current user
|
||||
file_cannot_be_null=File cannot be empty!
|
||||
#user related
|
||||
user_email_already_exists=User email already exists
|
||||
user_name_is_null=User name cannot be null
|
||||
user_email_is_null=User email cannot be null
|
||||
password_is_null=Password cannot be null
|
||||
user_id_already_exists=User ID already exists
|
||||
password_modification_failed=Password modification failed
|
||||
cannot_delete_current_user=Cannot delete the user currently logged in
|
||||
#load test
|
||||
edit_load_test_not_found=Cannot edit test, test not found=
|
||||
run_load_test_not_found=Cannot run test, test not found=
|
||||
run_load_test_file_not_found=Unable to run test, unable to get test file meta information, test ID=
|
||||
run_load_test_file_content_not_found=Cannot run test, cannot get test file content, test ID=
|
||||
run_load_test_file_init_error=Failed to run test, failed to initialize run environment, test ID=
|
||||
load_test_is_running=Load test is running, please wait.
|
||||
node_deep_limit=The node depth does not exceed 5 layers!
|
||||
no_nodes_message=No node message
|
||||
duplicate_node_ip=Duplicate IPs
|
||||
only_one_k8s=Only one K8s can be added
|
||||
organization_id_is_null=Organization ID cannot be null
|
||||
max_thread_insufficient=The number of concurrent users exceeds
|
||||
cannot_edit_load_test_running=Cannot modify the running test
|
||||
test_not_found=Test cannot be found:
|
||||
test_not_running=Test is not running
|
||||
before_delete_plan=There is an associated test case under this plan, please unlink it first!
|
||||
user_email_already_exists=User email already exists
|
||||
user_name_is_null=User name cannot be null
|
||||
user_email_is_null=User email cannot be null
|
||||
password_is_null=Password cannot be null
|
||||
load_test_already_exists=Duplicate load test name
|
||||
no_nodes_message=No node message
|
||||
duplicate_node_ip=Duplicate IPs
|
||||
only_one_k8s=Only one K8s can be added
|
||||
max_thread_insufficient=The number of concurrent users exceeds
|
||||
#workspace
|
||||
workspace_name_is_null=Workspace name cannot be null
|
||||
workspace_name_already_exists=The workspace name already exists
|
||||
workspace_does_not_belong_to_user=The current workspace does not belong to the current user
|
||||
workspace_not_exists=Workspace is not exists
|
||||
#project
|
||||
project_name_is_null=Project name cannot be null
|
||||
project_name_already_exists=The project name already exists
|
||||
#organization
|
||||
organization_does_not_belong_to_user=The current organization does not belong to the current user
|
||||
organization_id_is_null=Organization ID cannot be null
|
||||
#api
|
||||
api_load_script_error=Load script error
|
||||
user_id_already_exists=User ID already exists
|
||||
password_modification_failed=Password modification failed
|
||||
cannot_delete_current_user=Cannot delete the user currently logged in
|
||||
#test case
|
||||
test_case_node_level=level
|
||||
test_case_node_level_tip=The node tree maximum depth is
|
||||
test_case_module_not_null=The owned module cannot be empty
|
||||
test_case_create_module_fail=Failed to create module
|
||||
test_case_import_template_name=Test case templates
|
||||
test_case_import_template_sheet=Template
|
||||
module_not_null=The module must not be blank
|
||||
user_not_exists=The user in this workspace is not exists
|
||||
test_case_already_exists=The test case in this project is exists
|
||||
parse_data_error=Parse data error
|
||||
missing_header_information=Missing header information
|
||||
number=Number
|
||||
row=row
|
||||
error=error
|
||||
test_case_exist=A test case already exists under this project:
|
||||
node_deep_limit=The node depth does not exceed 5 layers!
|
||||
before_delete_plan=There is an associated test case under this plan, please unlink it first!
|
||||
incorrect_format=Incorrect format
|
||||
test_case_type_validate=must be functional, performance, api
|
||||
test_case_priority_validate=must be P0, P1, P2, P3
|
||||
test_case_method_validate=must be manual, auto
|
||||
test_case_name=Name
|
||||
test_case_type=Type
|
||||
test_case_maintainer=Maintainer
|
||||
test_case_priority=Priority
|
||||
test_case_method=method
|
||||
test_case_prerequisite=Prerequisite
|
||||
test_case_remark=Remark
|
||||
test_case_step_desc=Step description
|
||||
test_case_step_result=Step result
|
||||
test_case_module=Module
|
||||
test_case=Test case
|
||||
module=Module
|
||||
preconditions_optional=Preconditions optional
|
||||
step_tip_separate=Each step is separated by a new line
|
||||
step_tip_order=The serial number can be marked before the step
|
||||
step_tip_optional=Test steps and results optional
|
||||
result_tip_separate=Each result is separated by a new line
|
||||
result_tip_order=The sequence number can be marked before the result
|
||||
result_tip_optional=Test steps and results optional
|
||||
remark_optional=Remark optional
|
||||
do_not_modify_header_order=Do not modify the header order
|
||||
module_created_automatically=If there is no such module, will be created automatically
|
||||
options=options
|
||||
please_input_workspace_member=Please input workspace merber
|
|
@ -1,36 +1,85 @@
|
|||
test_case_exist=该项目下已存在用例:
|
||||
#commons
|
||||
error_lang_invalid=语言参数错误
|
||||
load_test_already_exists=测试名称不能重复
|
||||
project_name_is_null=项目名称不能为空
|
||||
project_name_already_exists=项目名称已存在
|
||||
workspace_name_is_null=工作空间名不能为空
|
||||
workspace_name_already_exists=工作空间名已存在
|
||||
workspace_does_not_belong_to_user=当前工作空间不属于当前用户
|
||||
organization_does_not_belong_to_user=当前组织不属于当前用户
|
||||
file_cannot_be_null=文件不能为空!
|
||||
#user related
|
||||
user_email_already_exists=用户邮箱已存在
|
||||
user_name_is_null=用户名不能为空
|
||||
user_email_is_null=用户邮箱不能为空
|
||||
password_is_null=密码不能为空
|
||||
user_id_already_exists=用户id已存在
|
||||
password_modification_failed=密码修改失败
|
||||
cannot_delete_current_user=无法删除当前登录用户
|
||||
#load test
|
||||
edit_load_test_not_found=无法编辑测试,未找到测试:
|
||||
run_load_test_not_found=无法运行测试,未找到测试:
|
||||
run_load_test_file_not_found=无法运行测试,无法获取测试文件元信息,测试ID:
|
||||
run_load_test_file_content_not_found=无法运行测试,无法获取测试文件内容,测试ID:
|
||||
run_load_test_file_init_error=无法运行测试,初始化运行环境失败,测试ID:
|
||||
load_test_is_running=测试正在运行, 请等待
|
||||
node_deep_limit=节点深度不超过5层!
|
||||
no_nodes_message=没有节点信息
|
||||
duplicate_node_ip=节点 IP 重复
|
||||
only_one_k8s=只能添加一个 K8s
|
||||
organization_id_is_null=组织 ID 不能为空
|
||||
max_thread_insufficient=并发用户数超额
|
||||
cannot_edit_load_test_running=不能修改正在运行的测试
|
||||
test_not_found=测试不存在:
|
||||
test_not_running=测试未运行
|
||||
before_delete_plan=该计划下存在关联测试用例,请先取消关联!
|
||||
user_email_already_exists=用户邮箱已存在
|
||||
user_name_is_null=用户名不能为空
|
||||
user_email_is_null=用户邮箱不能为空
|
||||
password_is_null=密码不能为空
|
||||
load_test_already_exists=测试名称不能重复
|
||||
no_nodes_message=没有节点信息
|
||||
duplicate_node_ip=节点 IP 重复
|
||||
only_one_k8s=只能添加一个 K8s
|
||||
max_thread_insufficient=并发用户数超额
|
||||
#workspace
|
||||
workspace_name_is_null=工作空间名不能为空
|
||||
workspace_name_already_exists=工作空间名已存在
|
||||
workspace_does_not_belong_to_user=当前工作空间不属于当前用户
|
||||
workspace_not_exists=工作空间不存在
|
||||
#project
|
||||
project_name_is_null=项目名称不能为空
|
||||
project_name_already_exists=项目名称已存在
|
||||
#organization
|
||||
organization_does_not_belong_to_user=当前组织不属于当前用户
|
||||
organization_id_is_null=组织 ID 不能为空
|
||||
#api
|
||||
api_load_script_error=读取脚本失败
|
||||
user_id_already_exists=用户id已存在
|
||||
password_modification_failed=密码修改失败
|
||||
cannot_delete_current_user=无法删除当前登录用户
|
||||
#test case
|
||||
test_case_node_level=层
|
||||
test_case_node_level_tip=模块树最大深度为
|
||||
test_case_module_not_null=所属模块不能为空
|
||||
test_case_create_module_fail=创建模块失败
|
||||
test_case_import_template_name=测试用例模版
|
||||
test_case_import_template_sheet=模版
|
||||
module_not_null=所属模块不能为空格
|
||||
user_not_exists=该工作空间下无该用户
|
||||
test_case_already_exists=该项目下已存在该测试用例
|
||||
parse_data_error=解析数据出错
|
||||
missing_header_information=缺少头部信息
|
||||
number=第
|
||||
row=行
|
||||
error=出错
|
||||
test_case_exist=该项目下已存在用例:
|
||||
node_deep_limit=节点深度不超过5层!
|
||||
before_delete_plan=该计划下存在关联测试用例,请先取消关联!
|
||||
incorrect_format=格式错误
|
||||
test_case_type_validate=必须为functional、performance、api
|
||||
test_case_priority_validate=必须为P0、P1、P2、P3
|
||||
test_case_method_validate=必须为manual、auto
|
||||
test_case_name=用例名称
|
||||
test_case_type=用例类型
|
||||
test_case_maintainer=维护人
|
||||
test_case_priority=优先级
|
||||
test_case_method=测试方式
|
||||
test_case_prerequisite=前置条件
|
||||
test_case_remark=备注
|
||||
test_case_step_desc=步骤描述
|
||||
test_case_step_result=预期结果
|
||||
test_case_module=所属模块
|
||||
test_case=测试用例
|
||||
module=模块
|
||||
preconditions_optional=前置条件选填
|
||||
step_tip_separate=每个步骤以换行分隔
|
||||
step_tip_order=步骤前可标序号
|
||||
step_tip_optional=步骤前可标序号
|
||||
result_tip_separate=每条结果以换行分隔
|
||||
result_tip_order=结果前可标序号
|
||||
result_tip_optional=测试步骤和结果选填
|
||||
remark_optional=备注选填
|
||||
do_not_modify_header_order=请勿修改表头顺序
|
||||
module_created_automatically=若无该模块将自动创建
|
||||
options=选项
|
||||
please_input_workspace_member=请填写该工作空间相关人员
|
||||
|
|
|
@ -1,36 +1,85 @@
|
|||
test_case_exist=該項目下已存在用例:
|
||||
#commons
|
||||
error_lang_invalid=語言參數錯誤
|
||||
load_test_already_exists=測試名稱不能重複
|
||||
project_name_is_null=項目名稱不能為空
|
||||
project_name_already_exists=項目名稱已存在
|
||||
workspace_name_is_null=工作空間名不能為空
|
||||
workspace_name_already_exists=工作空間名已存在
|
||||
workspace_does_not_belong_to_user=當前工作空間不屬於當前用戶
|
||||
organization_does_not_belong_to_user=當前組織不屬於當前用戶
|
||||
file_cannot_be_null=文件不能為空!
|
||||
#user related
|
||||
user_email_already_exists=用戶郵箱已存在
|
||||
user_name_is_null=用戶名不能為空
|
||||
user_email_is_null=用戶郵箱不能為空
|
||||
password_is_null=密碼不能為空
|
||||
user_id_already_exists=用戶id已存在
|
||||
password_modification_failed=密碼修改失敗
|
||||
cannot_delete_current_user=無法刪除當前登錄用戶
|
||||
#load test
|
||||
edit_load_test_not_found=無法編輯測試,未找到測試:
|
||||
run_load_test_not_found=無法運行測試,未找到測試:
|
||||
run_load_test_file_not_found=無法運行測試,無法獲取測試文件元信息,測試ID:
|
||||
run_load_test_file_content_not_found=無法運行測試,無法獲取測試文件內容,測試ID:
|
||||
run_load_test_file_init_error=無法運行測試,初始化運行環境失敗,測試ID:
|
||||
load_test_is_running=測試正在運行, 請等待
|
||||
node_deep_limit=節點深度不超過5層!
|
||||
no_nodes_message=沒有節點信息
|
||||
duplicate_node_ip=節點 IP 重複
|
||||
only_one_k8s=只能添加一個 K8s
|
||||
organization_id_is_null=組織 ID 不能為空
|
||||
max_thread_insufficient=並髮用戶數超額
|
||||
cannot_edit_load_test_running=不能修改正在運行的測試
|
||||
test_not_found=測試不存在:
|
||||
test_not_running=測試未運行
|
||||
before_delete_plan=該計劃下存在關聯測試用例,請先取消關聯!
|
||||
user_email_already_exists=用戶郵箱已存在
|
||||
user_name_is_null=用戶名不能為空
|
||||
user_email_is_null=用戶郵箱不能為空
|
||||
password_is_null=密碼不能為空
|
||||
load_test_already_exists=測試名稱不能重復
|
||||
no_nodes_message=沒有節點信息
|
||||
duplicate_node_ip=節點 IP 重復
|
||||
only_one_k8s=只能添加壹個 K8s
|
||||
max_thread_insufficient=並發用戶數超額
|
||||
#workspace
|
||||
workspace_name_is_null=工作空間名不能為空
|
||||
workspace_name_already_exists=工作空間名已存在
|
||||
workspace_does_not_belong_to_user=當前工作空間不屬於當前用戶
|
||||
workspace_not_exists=工作空間不存在
|
||||
#project
|
||||
project_name_is_null=項目名稱不能為空
|
||||
project_name_already_exists=項目名稱已存在
|
||||
#organization
|
||||
organization_does_not_belong_to_user=當前組織不屬於當前用戶
|
||||
organization_id_is_null=組織 ID 不能為空
|
||||
#api
|
||||
api_load_script_error=讀取腳本失敗
|
||||
user_id_already_exists=用戶id已存在
|
||||
password_modification_failed=密碼修改失敗
|
||||
cannot_delete_current_user=無法刪除當前登錄用戶
|
||||
#test case
|
||||
test_case_node_level=層
|
||||
test_case_node_level_tip=模塊樹最大深度為
|
||||
test_case_module_not_null=所屬模塊不能為空
|
||||
test_case_create_module_fail=創建模塊失敗
|
||||
test_case_import_template_name=測試用例模版
|
||||
test_case_import_template_sheet=模版
|
||||
module_not_null=所屬模塊不能為空格
|
||||
user_not_exists=該工作空間下無該用戶
|
||||
test_case_already_exists=該項目下已存在該測試用例
|
||||
parse_data_error=解析數據出錯
|
||||
missing_header_information=缺少頭部信息
|
||||
number=第
|
||||
row=行
|
||||
error=出錯
|
||||
test_case_exist=該項目下已存在用例:
|
||||
node_deep_limit=節點深度不超過5層!
|
||||
before_delete_plan=該計劃下存在關聯測試用例,請先取消關聯!
|
||||
incorrect_format=格式錯誤
|
||||
test_case_type_validate=必須為functional、performance、api
|
||||
test_case_priority_validate=必須為P0、P1、P2、P3
|
||||
test_case_method_validate=必須為manual、auto
|
||||
test_case_name=用例名稱
|
||||
test_case_type=用例類型
|
||||
test_case_maintainer=維護人
|
||||
test_case_priority=優先級
|
||||
test_case_method=測試方式
|
||||
test_case_prerequisite=前置條件
|
||||
test_case_remark=備註
|
||||
test_case_step_desc=步驟描述
|
||||
test_case_step_result=預期結果
|
||||
test_case_module=所屬模塊
|
||||
test_case=測試用例
|
||||
module=模塊
|
||||
preconditions_optional=前置條件選填
|
||||
step_tip_separate=每個步驟以換行分隔
|
||||
step_tip_order=步驟前可標序號
|
||||
step_tip_optional=步驟前可標序號
|
||||
result_tip_separate=每條結果以換行分隔
|
||||
result_tip_order=結果前可標序號
|
||||
result_tip_optional=測試步驟和結果選填
|
||||
remark_optional=備註選填
|
||||
do_not_modify_header_order=請勿修改表頭順序
|
||||
module_created_automatically=若無該模塊將自動創建
|
||||
options=選項
|
||||
please_input_workspace_member=請填寫該工作空間相關人員
|
|
@ -23,7 +23,8 @@
|
|||
"vue-i18n": "^8.15.3",
|
||||
"vue-router": "^3.1.3",
|
||||
"vuedraggable": "^2.23.2",
|
||||
"vuex": "^3.1.2"
|
||||
"vuex": "^3.1.2",
|
||||
"vue-calendar-heatmap": "^0.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^4.1.0",
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
return '/api/' + item.id;
|
||||
},
|
||||
router: function (item) {
|
||||
return {name: 'fucPlan', params: {projectId: item.id, projectName: item.name}}
|
||||
return {name: 'ApiTestList', params: {projectId: item.id, projectName: item.name}}
|
||||
}
|
||||
},
|
||||
testRecent: {
|
||||
|
@ -88,6 +88,7 @@
|
|||
},
|
||||
reportRecent: {
|
||||
title: this.$t('report.recent'),
|
||||
showTime: true,
|
||||
url: "/api/report/recent/5",
|
||||
index: function (item) {
|
||||
return '/api/report/view/' + item.id;
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<span class="title">{{$t('api_report.title')}}</span>
|
||||
</template>
|
||||
<el-table :data="tableData" class="table-content">
|
||||
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<ms-api-report-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiReportStatus from "../report/ApiReportStatus";
|
||||
|
||||
export default {
|
||||
name: "MsApiReportRecentList",
|
||||
|
||||
components: {MsApiReportStatus},
|
||||
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
tableData: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
search() {
|
||||
this.result = this.$get("/api/report/recent/5", response => {
|
||||
this.tableData = response.data;
|
||||
});
|
||||
},
|
||||
link(row) {
|
||||
this.$router.push({
|
||||
path: '/api/report/view/' + row.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,16 +1,57 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1>API测试首页</h1>
|
||||
</div>
|
||||
<ms-container>
|
||||
<ms-main-container v-loading="result.loading">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<ms-api-test-recent-list/>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<ms-api-report-recent-list/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<ms-test-heatmap :values="values"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ApiTestHome"
|
||||
}
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import MsApiTestRecentList from "./ApiTestRecentList";
|
||||
import MsApiReportRecentList from "./ApiReportRecentList";
|
||||
import MsTestHeatmap from "../../common/components/MsTestHeatmap";
|
||||
|
||||
export default {
|
||||
name: "ApiTestHome",
|
||||
|
||||
components: {MsTestHeatmap, MsApiReportRecentList, MsApiTestRecentList, MsMainContainer, MsContainer},
|
||||
|
||||
data() {
|
||||
return {
|
||||
values: [],
|
||||
result: {},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.result = this.$get('/api/report/dashboard/tests', response => {
|
||||
this.values = response.data;
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<template>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<span class="title">{{$t('api_test.title')}}</span>
|
||||
</template>
|
||||
<el-table :data="tableData" class="table-content">
|
||||
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="150" show-overflow-tooltip/>
|
||||
<el-table-column width="250" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<ms-api-test-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiTestStatus from "../test/ApiTestStatus";
|
||||
|
||||
export default {
|
||||
name: "MsApiTestRecentList",
|
||||
|
||||
components: {MsApiTestStatus},
|
||||
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
tableData: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
search() {
|
||||
this.result = this.$get("/api/recent/5", response => {
|
||||
this.tableData = response.data;
|
||||
});
|
||||
},
|
||||
link(row) {
|
||||
this.$router.push({
|
||||
path: '/api/test/edit?id=' + row.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,72 +1,34 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
<div class="main-content">
|
||||
<el-card class="table-card">
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<ms-table-header :condition.sync="condition" @search="search" :title="$t('commons.test')"
|
||||
<ms-table-header :condition.sync="condition" @search="search" :title="$t('api_report.title')"
|
||||
:show-create="false"/>
|
||||
</template>
|
||||
<el-table :data="tableData" class="table-content">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
<el-table-column :label="$t('commons.name')" width="200" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-link type="info" @click="handleView(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column-->
|
||||
<!-- prop="description"-->
|
||||
<!-- :label="$t('commons.description')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column
|
||||
width="250"
|
||||
:label="$t('commons.create_time')">
|
||||
<el-table-column prop="testName" :label="$t('api_report.test_name')" width="200" show-overflow-tooltip/>
|
||||
<el-table-column width="250" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="250"
|
||||
:label="$t('commons.update_time')">
|
||||
<el-table-column width="250" :label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
:label="$t('commons.status')">
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
{{ row.status }}
|
||||
</span>
|
||||
<ms-api-report-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="150"
|
||||
:label="$t('commons.operating')">
|
||||
<el-table-column width="150" :label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<el-button @click="handleView(scope.row)" type="primary" icon="el-icon-s-data" size="mini" circle/>
|
||||
<el-button @click="handleDelete(scope.row)" type="danger" icon="el-icon-delete" size="mini" circle/>
|
||||
|
@ -76,21 +38,23 @@
|
|||
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTablePagination from "../../common/pagination/TablePagination";
|
||||
import MsTableHeader from "../../common/components/MsTableHeader";
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import MsApiReportStatus from "./ApiReportStatus";
|
||||
|
||||
export default {
|
||||
components: {MsTableHeader, MsTablePagination},
|
||||
components: {MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
condition: {name: ""},
|
||||
projectId: null,
|
||||
tableData: [],
|
||||
multipleSelection: [],
|
||||
currentPage: 1,
|
||||
|
@ -100,11 +64,8 @@
|
|||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(self => {
|
||||
self.testId = to.params.testId;
|
||||
self.search();
|
||||
});
|
||||
watch: {
|
||||
'$route': 'init',
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -144,7 +105,15 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.testId = this.$route.params.testId;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<el-tag v-else size="mini" type="info">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsApiReportStatus",
|
||||
|
||||
props: {
|
||||
row: Object
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -2,7 +2,7 @@
|
|||
<el-table :data="assertions" :row-style="getRowStyle" :header-cell-style="getRowStyle">
|
||||
<el-table-column prop="name" :label="$t('api_report.assertions_name')" width="300">
|
||||
</el-table-column>
|
||||
<el-table-column prop="message" :label="$t('api_report.assertions_message')">
|
||||
<el-table-column prop="message" :label="$t('api_report.assertions_error_message')">
|
||||
</el-table-column>
|
||||
<el-table-column prop="pass" :label="$t('api_report.assertions_is_success')" width="180">
|
||||
<template v-slot:default="{row}">
|
||||
|
|
|
@ -105,7 +105,7 @@
|
|||
},
|
||||
|
||||
fail() {
|
||||
return (this.content.error / this.content.total).toFixed(0) + "%";
|
||||
return (this.content.error / this.content.total * 100).toFixed(0) + "%";
|
||||
},
|
||||
assertions() {
|
||||
return this.content.passAssertions + " / " + this.content.totalAssertions;
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</div>
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{error}}
|
||||
{{request.error}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
{{request.passAssertions}} / {{request.totalAssertions}}
|
||||
{{assertion}}
|
||||
</el-col>
|
||||
<el-col :span="2">
|
||||
<el-tag size="mini" type="success" v-if="request.success">
|
||||
|
@ -72,9 +72,9 @@
|
|||
},
|
||||
|
||||
computed: {
|
||||
error() {
|
||||
return this.request.totalAssertions - this.request.passAssertions;
|
||||
}
|
||||
assertion() {
|
||||
return this.request.passAssertions + " / " + this.request.totalAssertions;
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
|
||||
computed: {
|
||||
assertion() {
|
||||
return this.scenario.passAssertions - this.scenario.totalAssertions;
|
||||
return this.scenario.passAssertions + " / " + this.scenario.totalAssertions;
|
||||
},
|
||||
success() {
|
||||
return this.scenario.error === 0;
|
||||
|
@ -75,7 +75,7 @@
|
|||
}
|
||||
|
||||
.scenario-result + .scenario-result {
|
||||
border-top: 1px solid #EBEEF5;
|
||||
border-top: 1px solid #DCDFE6;
|
||||
}
|
||||
|
||||
.scenario-result .info {
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div class="relate_report">
|
||||
<el-button type="success" plain @click="search">{{$t('api_report.title')}}</el-button>
|
||||
|
||||
<el-dialog :title="$t('api_report.title')" :visible.sync="reportVisible">
|
||||
<el-table :data="tableData" v-loading="result.loading">
|
||||
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<ms-api-report-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsApiReportStatus from "../report/ApiReportStatus";
|
||||
|
||||
export default {
|
||||
name: "MsApiReportDialog",
|
||||
|
||||
components: {MsApiReportStatus},
|
||||
|
||||
props: ["testId"],
|
||||
|
||||
data() {
|
||||
return {
|
||||
reportVisible: false,
|
||||
result: {},
|
||||
tableData: [],
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
search() {
|
||||
this.reportVisible = true;
|
||||
|
||||
let url = "/api/report/list/" + this.testId;
|
||||
this.result = this.$get(url, response => {
|
||||
this.tableData = response.data;
|
||||
});
|
||||
},
|
||||
link(row) {
|
||||
this.reportVisible = false;
|
||||
|
||||
this.$router.push({
|
||||
path: '/api/report/view/' + row.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.relate_report {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
|
@ -5,7 +5,8 @@
|
|||
<el-container class="test-container" v-loading="result.loading">
|
||||
<el-header>
|
||||
<el-row type="flex" align="middle">
|
||||
<el-input class="test-name" v-model="test.name" maxlength="64" :placeholder="$t('api_test.input_name')">
|
||||
<el-input class="test-name" v-model="test.name" maxlength="60" :placeholder="$t('api_test.input_name')"
|
||||
show-word-limit>
|
||||
<el-select class="test-project" v-model="test.projectId" slot="prepend"
|
||||
:placeholder="$t('api_test.select_project')">
|
||||
<el-option v-for="project in projects" :key="project.id" :label="project.name" :value="project.id"/>
|
||||
|
@ -23,7 +24,10 @@
|
|||
<el-button type="primary" plain v-if="isShowRun" @click="runTest">
|
||||
{{$t('api_test.run')}}
|
||||
</el-button>
|
||||
|
||||
<el-button type="warning" plain @click="cancel">{{$t('commons.cancel')}}</el-button>
|
||||
|
||||
<ms-api-report-dialog :test-id="id" v-if="test.status === 'Completed'"/>
|
||||
</el-row>
|
||||
</el-header>
|
||||
<ms-api-scenario-config :scenarios="test.scenarioDefinition" ref="config"/>
|
||||
|
@ -36,16 +40,19 @@
|
|||
<script>
|
||||
import MsApiScenarioConfig from "./components/ApiScenarioConfig";
|
||||
import {Test} from "./model/ScenarioModel"
|
||||
import MsApiReportStatus from "../report/ApiReportStatus";
|
||||
import MsApiReportDialog from "./ApiReportDialog";
|
||||
|
||||
export default {
|
||||
name: "MsApiTestConfig",
|
||||
|
||||
components: {MsApiScenarioConfig},
|
||||
components: {MsApiReportDialog, MsApiReportStatus, MsApiScenarioConfig},
|
||||
|
||||
props: ["id"],
|
||||
|
||||
data() {
|
||||
return {
|
||||
reportVisible: false,
|
||||
create: false,
|
||||
result: {},
|
||||
projects: [],
|
||||
|
@ -89,6 +96,7 @@
|
|||
id: item.id,
|
||||
projectId: item.projectId,
|
||||
name: item.name,
|
||||
status: item.status,
|
||||
scenarioDefinition: JSON.parse(item.scenarioDefinition),
|
||||
});
|
||||
this.$refs.config.reset();
|
||||
|
@ -106,6 +114,9 @@
|
|||
saveTest: function () {
|
||||
this.save(() => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$router.push({
|
||||
path: '/api/test/edit?id=' + this.test.id
|
||||
})
|
||||
})
|
||||
},
|
||||
runTest: function () {
|
||||
|
|
|
@ -1,78 +1,35 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
<div class="main-content">
|
||||
<el-card class="table-card">
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<ms-table-header :condition.sync="condition" @search="search" :title="$t('commons.test')"
|
||||
<ms-table-header :condition.sync="condition" @search="search" :title="$t('api_test.title')"
|
||||
@create="create" :createTip="$t('load_test.create')"/>
|
||||
</template>
|
||||
<el-table :data="tableData" class="table-content">
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('commons.name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-link type="info" @click="handleEdit(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- <el-table-column-->
|
||||
<!-- prop="description"-->
|
||||
<!-- :label="$t('commons.description')"-->
|
||||
<!-- show-overflow-tooltip>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column
|
||||
prop="projectName"
|
||||
:label="$t('load_test.project_name')"
|
||||
width="150"
|
||||
show-overflow-tooltip>
|
||||
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="150" show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="250"
|
||||
:label="$t('commons.create_time')">
|
||||
<el-table-column width="250" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="250"
|
||||
:label="$t('commons.update_time')">
|
||||
<el-table-column width="250" :label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
:label="$t('commons.status')">
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
{{ row.status }}
|
||||
</span>
|
||||
<ms-api-test-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
width="150"
|
||||
:label="$t('commons.operating')">
|
||||
<el-table-column width="150" :label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<ms-table-operator @editClick="handleEdit(scope.row)" @deleteClick="handleDelete(scope.row)"/>
|
||||
</template>
|
||||
|
@ -81,17 +38,20 @@
|
|||
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTablePagination from "../../common/pagination/TablePagination";
|
||||
import MsTableHeader from "../../common/components/MsTableHeader";
|
||||
import MsTableOperator from "../../common/components/MsTableOperator";
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import MsApiTestStatus from "./ApiTestStatus";
|
||||
|
||||
export default {
|
||||
components: {MsTableHeader, MsTablePagination, MsTableOperator},
|
||||
components: {MsApiTestStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, MsTableOperator},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
|
@ -106,11 +66,8 @@
|
|||
}
|
||||
},
|
||||
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(self => {
|
||||
self.projectId = to.params.projectId;
|
||||
self.search();
|
||||
});
|
||||
watch: {
|
||||
'$route': 'init'
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
@ -153,7 +110,14 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.projectId = this.$route.params.projectId;
|
||||
this.search();
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<el-tag v-else size="mini" type="info">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsApiTestStatus",
|
||||
|
||||
props: {
|
||||
row: Object
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -13,8 +13,12 @@
|
|||
<el-dropdown trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link el-icon-more"/>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type: 'copy', index: index}">复制请求</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type: 'delete', index: index}">删除请求</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type: 'copy', index: index}">
|
||||
{{$t('api_test.request.copy')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type: 'delete', index: index}">
|
||||
{{$t('api_test.request.delete')}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
|
@ -50,12 +54,12 @@
|
|||
|
||||
methods: {
|
||||
createRequest: function () {
|
||||
let request = new Request({method: "GET"});
|
||||
let request = new Request();
|
||||
this.requests.push(request);
|
||||
},
|
||||
copyRequest: function (index) {
|
||||
let request = this.requests[index];
|
||||
this.requests.push(JSON.parse(JSON.stringify(request)));
|
||||
this.requests.push(request.clone());
|
||||
},
|
||||
deleteRequest: function (index) {
|
||||
this.requests.splice(index, 1);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-form :model="request" :rules="rules" ref="request" label-width="100px">
|
||||
<el-form-item :label="$t('api_test.request.name')" prop="name">
|
||||
<el-input v-model="request.name" maxlength="100"/>
|
||||
<el-input v-model="request.name" maxlength="100" @input="valid"/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('api_test.request.url')" prop="url">
|
||||
|
@ -108,6 +108,10 @@
|
|||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
valid(value) {
|
||||
value = value.replace(/[`~!@#$%^&*()_\-+=<>?:"{}|,./;'\\[\]·!¥…()—\-《》?:“”【】、;‘’,。]/g, '').replace(/\s/g, "");
|
||||
this.request.name = value;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -12,20 +12,23 @@
|
|||
{{$t('api_test.scenario.config')}}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 暂时去掉,将来再加-->
|
||||
<!-- <el-dropdown trigger="click" @command="handleCommand">-->
|
||||
<!-- <span class="el-dropdown-link el-icon-more scenario-btn"/>-->
|
||||
<!-- <el-dropdown-menu slot="dropdown">-->
|
||||
<!-- <el-dropdown-item :command="{type:'delete', index:index}">删除场景</el-dropdown-item>-->
|
||||
<!-- </el-dropdown-menu>-->
|
||||
<!-- </el-dropdown>-->
|
||||
<el-dropdown trigger="click" @command="handleCommand">
|
||||
<span class="el-dropdown-link el-icon-more scenario-btn"/>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type: 'copy', index: index}">
|
||||
{{$t('api_test.scenario.copy')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete', index:index}">
|
||||
{{$t('api_test.scenario.delete')}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<ms-api-request-config :requests="scenario.requests" :open="select"/>
|
||||
</ms-api-collapse-item>
|
||||
</ms-api-collapse>
|
||||
</div>
|
||||
<!-- 暂时去掉,将来再加-->
|
||||
<!-- <el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createScenario"/>-->
|
||||
<el-button class="scenario-create" type="primary" size="mini" icon="el-icon-plus" plain @click="createScenario"/>
|
||||
</el-aside>
|
||||
|
||||
<el-main class="scenario-main">
|
||||
|
@ -72,6 +75,10 @@
|
|||
createScenario: function () {
|
||||
this.scenarios.push(new Scenario());
|
||||
},
|
||||
copyScenario: function (index) {
|
||||
let scenario = this.scenarios[index];
|
||||
this.scenarios.push(scenario.clone());
|
||||
},
|
||||
deleteScenario: function (index) {
|
||||
this.scenarios.splice(index, 1);
|
||||
if (this.scenarios.length === 0) {
|
||||
|
@ -84,6 +91,9 @@
|
|||
},
|
||||
handleCommand: function (command) {
|
||||
switch (command.type) {
|
||||
case "copy":
|
||||
this.copyScenario(command.index);
|
||||
break;
|
||||
case "delete":
|
||||
this.deleteScenario(command.index);
|
||||
break;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<el-form :model="scenario" :rules="rules" ref="scenario" label-width="100px">
|
||||
<el-form-item :label="$t('api_test.scenario.name')" prop="name">
|
||||
<el-input v-model="scenario.name" maxlength="100"/>
|
||||
<el-input v-model="scenario.name" maxlength="100" @input="valid"/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item :label="$t('api_test.scenario.base_url')" prop="url">-->
|
||||
<!-- <el-input :placeholder="$t('api_test.scenario.base_url_description')" v-model="scenario.url" maxlength="100"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item :label="$t('api_test.scenario.base_url')" prop="url">-->
|
||||
<!-- <el-input :placeholder="$t('api_test.scenario.base_url_description')" v-model="scenario.url" maxlength="100"/>-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="$t('api_test.scenario.variables')" name="parameters">
|
||||
|
@ -43,6 +43,13 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
valid(value) {
|
||||
value = value.replace(/[`~!@#$%^&*()\-+=<>?:"{}|,./;'\\[\]·!¥…()—\-《》?:“”【】、;‘’,。]/g, '').replace(/\s/g, "");
|
||||
this.scenario.name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -32,13 +32,12 @@
|
|||
|
||||
methods: {
|
||||
add: function () {
|
||||
this.remove();
|
||||
setTimeout(() => {
|
||||
this.duration.value = this.time;
|
||||
})
|
||||
},
|
||||
remove: function () {
|
||||
this.duration.value = null;
|
||||
this.duration.value = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
</el-select>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<el-input v-model="value" maxlength="255" size="small" show-word-limit
|
||||
<el-input v-model="value" maxlength="200" size="small" show-word-limit
|
||||
:placeholder="$t('api_test.request.assertions.value')"/>
|
||||
</el-col>
|
||||
<el-col class="assertion-btn">
|
||||
|
@ -62,7 +62,7 @@
|
|||
description += " contains: " + this.value;
|
||||
break;
|
||||
case "NOT_CONTAINS":
|
||||
expression = "^((?!" + this.value + ").)*$";
|
||||
expression = "(?s)^((?!" + this.value + ").)*$";
|
||||
description += " not contains: " + this.value;
|
||||
break;
|
||||
case "EQUALS":
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<div>
|
||||
{{$t("api_test.request.assertions.response_time")}}
|
||||
</div>
|
||||
<ms-api-assertion-response-time :response-time="assertions.responseTime" :edit="true"/>
|
||||
<ms-api-assertion-response-time :duration="assertions.duration" :edit="true"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -255,7 +255,7 @@ export class HTTPSamplerProxy extends DefaultTestElement {
|
|||
this.stringProp("HTTPSampler.protocol", this.request.protocol.split(":")[0]);
|
||||
this.stringProp("HTTPSampler.path", this.request.pathname);
|
||||
this.stringProp("HTTPSampler.method", this.request.method);
|
||||
if (this.request.port) {
|
||||
if (!this.request.port) {
|
||||
this.stringProp("HTTPSampler.port", "");
|
||||
} else {
|
||||
this.stringProp("HTTPSampler.port", this.request.port);
|
||||
|
|
|
@ -96,8 +96,8 @@ export class Test extends BaseConfig {
|
|||
super();
|
||||
this.version = '1.0.0';
|
||||
this.id = uuid();
|
||||
this.name = null;
|
||||
this.projectId = null;
|
||||
this.name = undefined;
|
||||
this.projectId = undefined;
|
||||
this.scenarioDefinition = [];
|
||||
|
||||
this.set(options);
|
||||
|
@ -122,8 +122,8 @@ export class Scenario extends BaseConfig {
|
|||
constructor(options) {
|
||||
super();
|
||||
this.id = uuid();
|
||||
this.name = null;
|
||||
this.url = null;
|
||||
this.name = undefined;
|
||||
this.url = undefined;
|
||||
this.variables = [];
|
||||
this.headers = [];
|
||||
this.requests = [];
|
||||
|
@ -137,20 +137,30 @@ export class Scenario extends BaseConfig {
|
|||
options.requests = options.requests || [new Request()];
|
||||
return options;
|
||||
}
|
||||
|
||||
clone() {
|
||||
let scenario = new Scenario(this);
|
||||
scenario.id = uuid();
|
||||
scenario.requests.forEach(function (request) {
|
||||
request.id = uuid();
|
||||
});
|
||||
|
||||
return scenario;
|
||||
}
|
||||
}
|
||||
|
||||
export class Request extends BaseConfig {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.id = uuid();
|
||||
this.name = null;
|
||||
this.url = null;
|
||||
this.method = null;
|
||||
this.name = undefined;
|
||||
this.url = undefined;
|
||||
this.method = undefined;
|
||||
this.parameters = [];
|
||||
this.headers = [];
|
||||
this.body = null;
|
||||
this.assertions = null;
|
||||
this.extract = null;
|
||||
this.body = undefined;
|
||||
this.assertions = undefined;
|
||||
this.extract = undefined;
|
||||
|
||||
this.set(options);
|
||||
this.sets({parameters: KeyValue, headers: KeyValue}, options);
|
||||
|
@ -168,13 +178,19 @@ export class Request extends BaseConfig {
|
|||
isValid() {
|
||||
return !!this.url && !!this.method
|
||||
}
|
||||
|
||||
clone() {
|
||||
let request = new Request(this);
|
||||
request.id = uuid();
|
||||
return request;
|
||||
}
|
||||
}
|
||||
|
||||
export class Body extends BaseConfig {
|
||||
constructor(options) {
|
||||
super();
|
||||
this.type = null;
|
||||
this.raw = null;
|
||||
this.type = undefined;
|
||||
this.raw = undefined;
|
||||
this.kvs = [];
|
||||
|
||||
this.set(options);
|
||||
|
@ -225,7 +241,7 @@ export class Assertions extends BaseConfig {
|
|||
super();
|
||||
this.text = [];
|
||||
this.regex = [];
|
||||
this.duration = null;
|
||||
this.duration = undefined;
|
||||
|
||||
this.set(options);
|
||||
this.sets({text: Text, regex: Regex}, options);
|
||||
|
@ -248,9 +264,9 @@ export class AssertionType extends BaseConfig {
|
|||
export class Text extends AssertionType {
|
||||
constructor(options) {
|
||||
super(ASSERTION_TYPE.TEXT);
|
||||
this.subject = null;
|
||||
this.condition = null;
|
||||
this.value = null;
|
||||
this.subject = undefined;
|
||||
this.condition = undefined;
|
||||
this.value = undefined;
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
|
@ -259,9 +275,9 @@ export class Text extends AssertionType {
|
|||
export class Regex extends AssertionType {
|
||||
constructor(options) {
|
||||
super(ASSERTION_TYPE.REGEX);
|
||||
this.subject = null;
|
||||
this.expression = null;
|
||||
this.description = null;
|
||||
this.subject = undefined;
|
||||
this.expression = undefined;
|
||||
this.description = undefined;
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
|
@ -274,7 +290,7 @@ export class Regex extends AssertionType {
|
|||
export class ResponseTime extends AssertionType {
|
||||
constructor(options) {
|
||||
super(ASSERTION_TYPE.RESPONSE_TIME);
|
||||
this.value = null;
|
||||
this.value = undefined;
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
|
@ -311,10 +327,10 @@ export class ExtractType extends BaseConfig {
|
|||
export class ExtractCommon extends ExtractType {
|
||||
constructor(type, options) {
|
||||
super(type);
|
||||
this.variable = null;
|
||||
this.variable = undefined;
|
||||
this.value = ""; // ${variable}
|
||||
this.expression = null;
|
||||
this.description = null;
|
||||
this.expression = undefined;
|
||||
this.description = undefined;
|
||||
|
||||
this.set(options);
|
||||
}
|
||||
|
@ -386,7 +402,7 @@ class JMeterTestPlan extends Element {
|
|||
|
||||
class JMXGenerator {
|
||||
constructor(test) {
|
||||
if (!test || !(test instanceof Test)) return null;
|
||||
if (!test || !(test instanceof Test)) return undefined;
|
||||
|
||||
if (!test.id) {
|
||||
test.id = "#NULL_TEST_ID#";
|
||||
|
@ -395,7 +411,8 @@ class JMXGenerator {
|
|||
|
||||
let testPlan = new TestPlan(test.name);
|
||||
test.scenarioDefinition.forEach(scenario => {
|
||||
let threadGroup = new ThreadGroup(scenario.name + SPLIT + scenario.id);
|
||||
let testName = scenario.name ? scenario.name + SPLIT + scenario.id : SPLIT + scenario.id;
|
||||
let threadGroup = new ThreadGroup(testName);
|
||||
|
||||
this.addScenarioVariables(threadGroup, scenario);
|
||||
|
||||
|
@ -405,7 +422,7 @@ class JMXGenerator {
|
|||
if (!request.isValid()) return;
|
||||
|
||||
// test.id用于处理结果时区分属于哪个测试
|
||||
let name = request.name + SPLIT + test.id;
|
||||
let name = request.name ? request.name + SPLIT + test.id : SPLIT + test.id;
|
||||
let httpSamplerProxy = new HTTPSamplerProxy(name, new JMXRequest(request));
|
||||
|
||||
this.addRequestHeader(httpSamplerProxy, request);
|
||||
|
@ -496,7 +513,7 @@ class JMXGenerator {
|
|||
|
||||
getAssertion(regex) {
|
||||
let name = regex.description;
|
||||
let type = JMX_ASSERTION_CONDITION.MATCH; // 固定用Match,自己写正则
|
||||
let type = JMX_ASSERTION_CONDITION.CONTAINS; // 固定用Match,自己写正则
|
||||
let value = regex.expression;
|
||||
switch (regex.subject) {
|
||||
case ASSERTION_REGEX_SUBJECT.RESPONSE_CODE:
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
<template>
|
||||
|
||||
<el-aside class="ms-aside-container">
|
||||
<slot></slot>
|
||||
</el-aside>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsAsideContainer"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-aside-container {
|
||||
border: 1px solid #E6E6E6;
|
||||
padding: 10px;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
background-color: #FFF;
|
||||
height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,27 @@
|
|||
<template>
|
||||
|
||||
<el-container class="ms-container">
|
||||
<slot></slot>
|
||||
</el-container>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsContainer"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-container >>> span.title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin-top: 0;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
word-wrap: break-word;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,20 @@
|
|||
<template>
|
||||
<el-main class="ms-main-container">
|
||||
<slot></slot>
|
||||
</el-main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsMainContainer"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-main-container {
|
||||
padding: 15px;
|
||||
height: calc(100vh - 80px);
|
||||
}
|
||||
|
||||
</style>
|
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<el-card>
|
||||
<template v-slot:header>
|
||||
<span class="title">{{$t('commons.calendar_heatmap')}}</span>
|
||||
</template>
|
||||
<calendar-heatmap :end-date="endDate" :values="values" :locale="locale"
|
||||
:tooltip-unit="unit"
|
||||
:range-color="colorRange"/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsTestHeatmap",
|
||||
props: ['values'],
|
||||
data() {
|
||||
return {
|
||||
endDate: new Date(),
|
||||
colorRange: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127'],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
locale() {
|
||||
return {
|
||||
months: [
|
||||
this.$t('commons.months_1'),
|
||||
this.$t('commons.months_2'),
|
||||
this.$t('commons.months_3'),
|
||||
this.$t('commons.months_4'),
|
||||
this.$t('commons.months_5'),
|
||||
this.$t('commons.months_6'),
|
||||
this.$t('commons.months_7'),
|
||||
this.$t('commons.months_8'),
|
||||
this.$t('commons.months_9'),
|
||||
this.$t('commons.months_10'),
|
||||
this.$t('commons.months_11'),
|
||||
this.$t('commons.months_12')
|
||||
],
|
||||
days: [
|
||||
this.$t('commons.weeks_0'),
|
||||
this.$t('commons.weeks_1'),
|
||||
this.$t('commons.weeks_2'),
|
||||
this.$t('commons.weeks_3'),
|
||||
this.$t('commons.weeks_4'),
|
||||
this.$t('commons.weeks_5'),
|
||||
this.$t('commons.weeks_6')
|
||||
],
|
||||
No: 'No',
|
||||
on: ',',
|
||||
less: 'Less',
|
||||
more: 'More'
|
||||
}
|
||||
},
|
||||
unit() {
|
||||
return this.$t('commons.test_unit')
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -34,7 +34,9 @@
|
|||
},
|
||||
watch: {
|
||||
'$route'(to) {
|
||||
this.activeIndex = to.matched[0].path;
|
||||
if (to.matched.length > 0) {
|
||||
this.activeIndex = to.matched[0].path;
|
||||
}
|
||||
this.handleSelect(this.activeIndex);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {TokenKey} from '../../../../common/js/constants';
|
||||
import {getCurrentUser} from "../../../../common/js/utils";
|
||||
|
||||
export default {
|
||||
|
@ -32,7 +31,7 @@
|
|||
break;
|
||||
case "logout":
|
||||
this.$get("/signout", function () {
|
||||
localStorage.removeItem(TokenKey);
|
||||
localStorage.clear();
|
||||
window.location.href = "/login";
|
||||
});
|
||||
break;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {EN_US, TokenKey, ZH_CN, ZH_TW} from '../../../../common/js/constants';
|
||||
import {DEFAULT_LANGUAGE, EN_US, TokenKey, ZH_CN, ZH_TW} from '../../../../common/js/constants';
|
||||
import {getCurrentUser} from "../../../../common/js/utils";
|
||||
|
||||
export default {
|
||||
|
@ -38,7 +38,7 @@
|
|||
let lang = this.currentUser().language;
|
||||
this.currentUserInfo = this.currentUser();
|
||||
if (!lang) {
|
||||
lang = 'zh_CN';
|
||||
lang = localStorage.getItem(DEFAULT_LANGUAGE);
|
||||
}
|
||||
this.checkLanguage(lang)
|
||||
},
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
<i class="el-icon-refresh" @click="recent"/>
|
||||
</div>
|
||||
<el-menu-item :key="i.id" v-for="i in items" :index="getIndex(i)" :route="getRouter(i)">
|
||||
<span class="title">{{ i.name }}</span>
|
||||
<template slot="title">
|
||||
<div class="title">{{ i.name }}</div>
|
||||
<div class="time" v-if="options.showTime && i.updateTime">{{ i.updateTime | timestampFormatDate}}</div>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -81,6 +84,18 @@
|
|||
}
|
||||
|
||||
.title {
|
||||
display: inline-block;
|
||||
padding-left: 20px;
|
||||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #C0C4CC;
|
||||
display: inline-block;
|
||||
padding-left: 20px;
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -224,6 +224,11 @@ const router = new VueRouter({
|
|||
name: "planView",
|
||||
component: TestPlanView
|
||||
},
|
||||
{
|
||||
path: "plan/view/edit/:caseId",
|
||||
name: "planViewEdit",
|
||||
component: TestPlanView
|
||||
},
|
||||
{
|
||||
path: "project/:type",
|
||||
name: "trackProject",
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
<template>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<span class="title">{{$t('api_report.title')}}</span>
|
||||
</template>
|
||||
<el-table :data="tableData" class="table-content">
|
||||
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<ms-performance-report-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MsPerformanceReportStatus from "../report/PerformanceReportStatus";
|
||||
|
||||
export default {
|
||||
name: "MsPerformanceReportRecentList",
|
||||
components: {MsPerformanceReportStatus},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
search() {
|
||||
this.result = this.$get("/performance/report/recent/5", response => {
|
||||
this.tableData = response.data;
|
||||
});
|
||||
},
|
||||
link(row) {
|
||||
this.$router.push({
|
||||
path: '/performance/report/view/' + row.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,16 +1,56 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1>性能测试首页</h1>
|
||||
</div>
|
||||
<ms-container>
|
||||
<ms-main-container v-loading="result.loading">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<ms-performance-report-recent-list/>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<ms-performance-test-recent-list/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<ms-test-heatmap :values="values"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "PerformanceTestHome"
|
||||
}
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import MsPerformanceTestRecentList from "./PerformanceTestRecentList"
|
||||
import MsPerformanceReportRecentList from "./PerformanceReportRecentList"
|
||||
import MsTestHeatmap from "../../common/components/MsTestHeatmap";
|
||||
|
||||
export default {
|
||||
name: "PerformanceTestHome",
|
||||
components: {
|
||||
MsTestHeatmap,
|
||||
MsMainContainer,
|
||||
MsContainer,
|
||||
MsPerformanceTestRecentList,
|
||||
MsPerformanceReportRecentList
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
values: [],
|
||||
result: {},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.result = this.$get('/performance/dashboard/tests', response => {
|
||||
this.values = response.data;
|
||||
});
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.el-row {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<span class="title">{{$t('api_test.title')}}</span>
|
||||
</template>
|
||||
<el-table :data="tableData" class="table-content">
|
||||
<el-table-column :label="$t('commons.name')" width="150" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<el-link type="info" @click="link(scope.row)">{{ scope.row.name }}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="150" show-overflow-tooltip/>
|
||||
<el-table-column width="250" :label="$t('commons.create_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="250" :label="$t('commons.update_time')">
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<ms-performance-test-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MsPerformanceTestStatus from "../test/PerformanceTestStatus";
|
||||
|
||||
export default {
|
||||
name: "MsPerformanceTestRecentList",
|
||||
components: {MsPerformanceTestStatus},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
search() {
|
||||
this.result = this.$get("/performance/recent/5", response => {
|
||||
this.tableData = response.data;
|
||||
});
|
||||
},
|
||||
link(row) {
|
||||
this.$router.push({
|
||||
path: '/performance/test/edit/' + row.id,
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.search();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
{{ row.status }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsPerformanceReportStatus",
|
||||
|
||||
props: {
|
||||
row: Object
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div v-loading="result.loading" class="container">
|
||||
<div class="main-content">
|
||||
<el-card>
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card v-loading="result.loading">
|
||||
<el-row>
|
||||
<el-col :span="16">
|
||||
<el-row>
|
||||
|
@ -51,8 +51,8 @@
|
|||
</el-tabs>
|
||||
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -60,6 +60,8 @@
|
|||
import MsReportLogDetails from './components/LogDetails';
|
||||
import MsReportRequestStatistics from './components/RequestStatistics';
|
||||
import MsReportTestOverview from './components/TestOverview';
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
|
||||
export default {
|
||||
name: "PerformanceReportView",
|
||||
|
@ -67,7 +69,9 @@
|
|||
MsReportErrorLog,
|
||||
MsReportLogDetails,
|
||||
MsReportRequestStatistics,
|
||||
MsReportTestOverview
|
||||
MsReportTestOverview,
|
||||
MsContainer,
|
||||
MsMainContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
|
||||
<div class="main-content">
|
||||
<el-card class="table-card">
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<div>
|
||||
<el-row type="flex" justify="space-between" align="middle">
|
||||
|
@ -47,29 +46,7 @@
|
|||
prop="status"
|
||||
:label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-tag size="mini" type="primary" v-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
{{ row.status }}
|
||||
</span>
|
||||
<ms-performance-report-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
@ -84,17 +61,19 @@
|
|||
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTablePagination from "../../common/pagination/TablePagination";
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import MsPerformanceReportStatus from "./PerformanceReportStatus";
|
||||
|
||||
export default {
|
||||
name: "PerformanceTestReport",
|
||||
components: {MsTablePagination},
|
||||
components: {MsPerformanceReportStatus, MsTablePagination, MsContainer, MsMainContainer},
|
||||
created: function () {
|
||||
this.initTableData();
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
<div class="main-content">
|
||||
<el-card>
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card v-loading="result.loading">
|
||||
<el-row>
|
||||
<el-col :span="10">
|
||||
<el-input :placeholder="$t('load_test.input_name')" v-model="testPlan.name" class="input-with-select">
|
||||
|
@ -37,21 +37,25 @@
|
|||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PerformanceBasicConfig from "./components/PerformanceBasicConfig";
|
||||
import PerformancePressureConfig from "./components/PerformancePressureConfig";
|
||||
import PerformanceAdvancedConfig from "./components/PerformanceAdvancedConfig";
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
|
||||
export default {
|
||||
name: "EditPerformanceTestPlan",
|
||||
components: {
|
||||
PerformancePressureConfig,
|
||||
PerformanceBasicConfig,
|
||||
PerformanceAdvancedConfig
|
||||
PerformanceAdvancedConfig,
|
||||
MsContainer,
|
||||
MsMainContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="container" v-loading="result.loading">
|
||||
<div class="main-content">
|
||||
<el-card class="table-card">
|
||||
<ms-container>
|
||||
<ms-main-container>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<div>
|
||||
<el-row type="flex" justify="space-between" align="middle">
|
||||
|
@ -46,32 +46,7 @@
|
|||
prop="status"
|
||||
:label="$t('commons.status')">
|
||||
<template v-slot:default="{row}">
|
||||
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
{{ row.status }}
|
||||
</span>
|
||||
<ms-performance-test-status :row="row"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
@ -85,16 +60,25 @@
|
|||
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
|
||||
:total="total"/>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</ms-main-container>
|
||||
</ms-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MsTablePagination from "../../common/pagination/TablePagination";
|
||||
import MsTableOperator from "../../common/components/MsTableOperator";
|
||||
import MsContainer from "../../common/components/MsContainer";
|
||||
import MsMainContainer from "../../common/components/MsMainContainer";
|
||||
import MsPerformanceTestStatus from "./PerformanceTestStatus";
|
||||
|
||||
export default {
|
||||
components: {MsTablePagination, MsTableOperator},
|
||||
components: {
|
||||
MsPerformanceTestStatus,
|
||||
MsTablePagination,
|
||||
MsTableOperator,
|
||||
MsContainer,
|
||||
MsMainContainer
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-tag size="mini" type="info" v-if="row.status === 'Saved'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="primary" v-else-if="row.status === 'Starting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="row.status === 'Running'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="row.status === 'Reporting'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tag size="mini" type="info" v-else-if="row.status === 'Completed'">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
<el-tooltip placement="top" v-else-if="row.status === 'Error'" effect="light">
|
||||
<template v-slot:content>
|
||||
<div>{{row.description}}</div>
|
||||
</template>
|
||||
<el-tag size="mini" type="danger">
|
||||
{{ row.status }}
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
<span v-else>
|
||||
{{ row.status }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsPerformanceTestStatus",
|
||||
|
||||
props: {
|
||||
row: Object
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue