feat: 接口测试关联场景用例

This commit is contained in:
chenjianxing 2020-12-22 17:27:30 +08:00
parent ecfb1cf2fe
commit 471f88aac6
28 changed files with 1349 additions and 112 deletions

View File

@ -7,6 +7,7 @@ import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.dto.automation.APIScenarioReportResult;
import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.service.ApiScenarioReportService;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
@ -45,7 +46,7 @@ public class APIScenarioReportController {
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public String add(@RequestBody APIScenarioReportResult node) {
node.setExecuteType(ExecuteType.Saved.name());
return apiReportService.save(node);
return apiReportService.save(node, ApiRunMode.SCENARIO.name());
}
@PostMapping("/update")

View File

@ -11,6 +11,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.track.request.testcase.ApiCaseRelevanceRequest;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
@ -96,5 +97,9 @@ public class ApiAutomationController {
return apiAutomationService.addScenarioToPlan(request);
}
@PostMapping("/relevance")
public void testPlanRelevance(@RequestBody ApiCaseRelevanceRequest request) {
apiAutomationService.relevance(request);
}
}

View File

@ -18,6 +18,7 @@ public class ApiScenarioRequest {
private String name;
private String workspaceId;
private String userId;
private String planId;
private boolean recent = false;
private List<OrderRequest> orders;
private List<String> filters;

View File

@ -21,5 +21,9 @@ public class RunScenarioRequest {
private String executeType;
private String runMode;
private List<String> planCaseIds;
private List<String> scenarioIds;
}

View File

@ -0,0 +1,27 @@
package io.metersphere.api.dto.automation;
import io.metersphere.controller.request.OrderRequest;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
import java.util.Map;
@Getter
@Setter
public class TestPlanScenarioRequest {
private String id;
private String excludeId;
private String projectId;
private String moduleId;
private List<String> moduleIds;
private String name;
private String workspaceId;
private String userId;
private String planId;
private boolean recent = false;
private List<OrderRequest> orders;
private Map<String, List<String>> filters;
private Map<String, Object> combine;
private List<String> ids;
}

View File

@ -170,12 +170,11 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
} else if (StringUtils.equals(this.runMode, ApiRunMode.API_PLAN.name())) {
apiDefinitionService.addResult(testResult);
apiDefinitionExecResultService.saveApiResult(testResult, ApiRunMode.API_PLAN.name());
} else if (StringUtils.equals(this.runMode, ApiRunMode.SCENARIO.name())) {
} else if (StringUtils.equalsAny(this.runMode, ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name())) {
// 执行报告不需要存储由用户确认后在存储
testResult.setTestId(testId);
apiScenarioReportService.complete(testResult);
}
else {
apiScenarioReportService.complete(testResult, this.runMode);
} else {
apiTestService.changeStatus(testId, APITestStatus.Completed);
report = apiReportService.getRunningReport(testResult.getTestId());
apiReportService.complete(testResult, report);

View File

@ -18,6 +18,7 @@ import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ApiTagMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ReportTriggerMode;
@ -27,6 +28,7 @@ import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.i18n.Translator;
import io.metersphere.track.dto.TestPlanDTO;
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@ -66,13 +68,16 @@ public class ApiAutomationService {
@Resource
SqlSessionFactory sqlSessionFactory;
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
public List<ApiScenarioDTO> list(ApiScenarioRequest request) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
List<ApiScenarioDTO> list = extApiScenarioMapper.list(request);
// buildScenarioInfo(request.getProjectId(), list); todo tag?
return list;
}
public void buildScenarioInfo(String projectId, List<ApiScenarioDTO> list) {
ApiTagExample example = new ApiTagExample();
example.createCriteria().andProjectIdEqualTo(request.getProjectId());
example.createCriteria().andProjectIdEqualTo(projectId);
List<ApiTag> tags = apiTagMapper.selectByExample(example);
Map<String, String> tagMap = tags.stream().collect(Collectors.toMap(ApiTag::getId, ApiTag::getName));
Gson gs = new Gson();
@ -92,7 +97,10 @@ public class ApiAutomationService {
}
}
});
return list;
}
public List<String> selectIdsNotExistsInPlan(String projectId, String planId) {
return extApiScenarioMapper.selectIdsNotExistsInPlan(projectId, planId);
}
public void deleteByIds(List<String> nodeIds) {
@ -277,8 +285,13 @@ public class ApiAutomationService {
}
}
testPlan.toHashTree(jmeterTestPlanHashTree, testPlan.getHashTree(), new ParameterConfig());
String runMode = ApiRunMode.SCENARIO.name();
if (StringUtils.isNotBlank(request.getRunMode()) && StringUtils.equals(request.getRunMode(), ApiRunMode.SCENARIO_PLAN.name())) {
runMode = ApiRunMode.SCENARIO_PLAN.name();
}
// 调用执行方法
jMeterService.runDefinition(request.getId(), jmeterTestPlanHashTree, request.getReportId(), ApiRunMode.SCENARIO.name());
jMeterService.runDefinition(request.getId(), jmeterTestPlanHashTree, request.getReportId(), runMode);
createAPIScenarioReportResult(request.getId(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
request.getExecuteType(), request.getProjectId());
@ -382,4 +395,27 @@ public class ApiAutomationService {
public List<ApiDataCountResult> countRunResultByProjectID(String projectId) {
return extApiScenarioMapper.countRunResultByProjectID(projectId);
}
public void relevance(ApiCaseRelevanceRequest request) {
List<String> ids = request.getSelectIds();
if (CollectionUtils.isEmpty(ids)) {
return;
}
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(ids);
List<ApiScenario> apiScenarios = apiScenarioMapper.selectByExample(example);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ExtTestPlanScenarioCaseMapper batchMapper = sqlSession.getMapper(ExtTestPlanScenarioCaseMapper.class);
apiScenarios.forEach(scenario -> {
TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario();
testPlanApiScenario.setId(UUID.randomUUID().toString());
testPlanApiScenario.setApiScenarioId(scenario.getId());
testPlanApiScenario.setTestPlanId(request.getPlanId());
testPlanApiScenario.setCreateTime(System.currentTimeMillis());
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
batchMapper.insertIfNotExists(testPlanApiScenario);
});
sqlSession.flushStatements();
}
}

View File

@ -6,13 +6,16 @@ import io.metersphere.api.dto.DeleteAPIReportRequest;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.dto.automation.APIScenarioReportResult;
import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.jmeter.ScenarioResult;
import io.metersphere.api.jmeter.TestResult;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.ApiScenarioReportDetailMapper;
import io.metersphere.base.mapper.ApiScenarioReportMapper;
import io.metersphere.base.mapper.TestPlanApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.ServiceUtils;
@ -43,8 +46,10 @@ public class ApiScenarioReportService {
private ApiScenarioReportDetailMapper apiScenarioReportDetailMapper;
@Resource
private ApiScenarioMapper apiScenarioMapper;
@Resource
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
public void complete(TestResult result) {
public void complete(TestResult result, String runMode) {
Object obj = cache.get(result.getTestId());
if (obj == null) {
MSException.throwException(Translator.get("api_report_is_null"));
@ -66,7 +71,7 @@ public class ApiScenarioReportService {
}
}
report.setContent(new String(detail.getContent(), StandardCharsets.UTF_8));
this.save(report);
this.save(report, runMode);
cache.put(report.getId(), report);
}
@ -149,29 +154,17 @@ public class ApiScenarioReportService {
}
public String save(APIScenarioReportResult test) {
public String save(APIScenarioReportResult test, String runModel) {
ApiScenarioReport report = createReport(test);
ApiScenarioReportDetail detail = new ApiScenarioReportDetail();
TestResult result = JSON.parseObject(test.getContent(), TestResult.class);
// 更新场景
if (result != null) {
result.getScenarios().forEach(item -> {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andNameEqualTo(item.getName()).andProjectIdEqualTo(test.getProjectId());
List<ApiScenario> list = apiScenarioMapper.selectByExample(example);
if (list.size() > 0) {
ApiScenario scenario = list.get(0);
if (item.getError() > 0) {
scenario.setLastResult("Fail");
} else {
scenario.setLastResult("Success");
}
String passRate = new DecimalFormat("0%").format((float) item.getSuccess() / (item.getSuccess() + item.getError()));
scenario.setPassRate(passRate);
scenario.setReportId(report.getId());
apiScenarioMapper.updateByPrimaryKey(scenario);
}
});
if (StringUtils.equals(runModel, ApiRunMode.SCENARIO_PLAN.name())) {
updatePlanCase(result, test, report);
} else {
updateScenario(result.getScenarios(), test.getProjectId(), report.getId());
}
}
detail.setContent(test.getContent().getBytes(StandardCharsets.UTF_8));
detail.setReportId(report.getId());
@ -180,6 +173,41 @@ public class ApiScenarioReportService {
return report.getId();
}
public void updatePlanCase(TestResult result, APIScenarioReportResult test, ApiScenarioReport report) {
TestPlanApiScenario testPlanApiScenario = new TestPlanApiScenario();
testPlanApiScenario.setId(test.getId());
ScenarioResult scenarioResult = result.getScenarios().get(0);
if (scenarioResult.getError() > 0) {
testPlanApiScenario.setLastResult("Fail");
} else {
testPlanApiScenario.setLastResult("Success");
}
String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError()));
testPlanApiScenario.setPassRate(passRate);
testPlanApiScenario.setReportId(report.getId());
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
}
public void updateScenario(List<ScenarioResult> Scenarios, String projectId, String reportId) {
Scenarios.forEach(item -> {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andNameEqualTo(item.getName()).andProjectIdEqualTo(projectId);
List<ApiScenario> list = apiScenarioMapper.selectByExample(example);
if (list.size() > 0) {
ApiScenario scenario = list.get(0);
if (item.getError() > 0) {
scenario.setLastResult("Fail");
} else {
scenario.setLastResult("Success");
}
String passRate = new DecimalFormat("0%").format((float) item.getSuccess() / (item.getSuccess() + item.getError()));
scenario.setPassRate(passRate);
scenario.setReportId(reportId);
apiScenarioMapper.updateByPrimaryKey(scenario);
}
});
}
public String update(APIScenarioReportResult test) {
ApiScenarioReport report = updateReport(test);
ApiScenarioReportDetail detail = apiScenarioReportDetailMapper.selectByPrimaryKey(test.getId());

View File

@ -19,5 +19,11 @@ public class TestPlanApiScenario implements Serializable {
private Long updateTime;
private String passRate;
private String lastResult;
private String reportId;
private static final long serialVersionUID = 1L;
}

View File

@ -573,6 +573,216 @@ public class TestPlanApiScenarioExample {
addCriterion("update_time not between", value1, value2, "updateTime");
return (Criteria) this;
}
public Criteria andPassRateIsNull() {
addCriterion("pass_rate is null");
return (Criteria) this;
}
public Criteria andPassRateIsNotNull() {
addCriterion("pass_rate is not null");
return (Criteria) this;
}
public Criteria andPassRateEqualTo(String value) {
addCriterion("pass_rate =", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateNotEqualTo(String value) {
addCriterion("pass_rate <>", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateGreaterThan(String value) {
addCriterion("pass_rate >", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateGreaterThanOrEqualTo(String value) {
addCriterion("pass_rate >=", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateLessThan(String value) {
addCriterion("pass_rate <", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateLessThanOrEqualTo(String value) {
addCriterion("pass_rate <=", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateLike(String value) {
addCriterion("pass_rate like", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateNotLike(String value) {
addCriterion("pass_rate not like", value, "passRate");
return (Criteria) this;
}
public Criteria andPassRateIn(List<String> values) {
addCriterion("pass_rate in", values, "passRate");
return (Criteria) this;
}
public Criteria andPassRateNotIn(List<String> values) {
addCriterion("pass_rate not in", values, "passRate");
return (Criteria) this;
}
public Criteria andPassRateBetween(String value1, String value2) {
addCriterion("pass_rate between", value1, value2, "passRate");
return (Criteria) this;
}
public Criteria andPassRateNotBetween(String value1, String value2) {
addCriterion("pass_rate not between", value1, value2, "passRate");
return (Criteria) this;
}
public Criteria andLastResultIsNull() {
addCriterion("last_result is null");
return (Criteria) this;
}
public Criteria andLastResultIsNotNull() {
addCriterion("last_result is not null");
return (Criteria) this;
}
public Criteria andLastResultEqualTo(String value) {
addCriterion("last_result =", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultNotEqualTo(String value) {
addCriterion("last_result <>", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultGreaterThan(String value) {
addCriterion("last_result >", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultGreaterThanOrEqualTo(String value) {
addCriterion("last_result >=", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultLessThan(String value) {
addCriterion("last_result <", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultLessThanOrEqualTo(String value) {
addCriterion("last_result <=", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultLike(String value) {
addCriterion("last_result like", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultNotLike(String value) {
addCriterion("last_result not like", value, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultIn(List<String> values) {
addCriterion("last_result in", values, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultNotIn(List<String> values) {
addCriterion("last_result not in", values, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultBetween(String value1, String value2) {
addCriterion("last_result between", value1, value2, "lastResult");
return (Criteria) this;
}
public Criteria andLastResultNotBetween(String value1, String value2) {
addCriterion("last_result not between", value1, value2, "lastResult");
return (Criteria) this;
}
public Criteria andReportIdIsNull() {
addCriterion("report_id is null");
return (Criteria) this;
}
public Criteria andReportIdIsNotNull() {
addCriterion("report_id is not null");
return (Criteria) this;
}
public Criteria andReportIdEqualTo(String value) {
addCriterion("report_id =", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdNotEqualTo(String value) {
addCriterion("report_id <>", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdGreaterThan(String value) {
addCriterion("report_id >", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdGreaterThanOrEqualTo(String value) {
addCriterion("report_id >=", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdLessThan(String value) {
addCriterion("report_id <", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdLessThanOrEqualTo(String value) {
addCriterion("report_id <=", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdLike(String value) {
addCriterion("report_id like", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdNotLike(String value) {
addCriterion("report_id not like", value, "reportId");
return (Criteria) this;
}
public Criteria andReportIdIn(List<String> values) {
addCriterion("report_id in", values, "reportId");
return (Criteria) this;
}
public Criteria andReportIdNotIn(List<String> values) {
addCriterion("report_id not in", values, "reportId");
return (Criteria) this;
}
public Criteria andReportIdBetween(String value1, String value2) {
addCriterion("report_id between", value1, value2, "reportId");
return (Criteria) this;
}
public Criteria andReportIdNotBetween(String value1, String value2) {
addCriterion("report_id not between", value1, value2, "reportId");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -9,6 +9,9 @@
<result column="environment_id" jdbcType="VARCHAR" property="environmentId" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
<result column="pass_rate" jdbcType="VARCHAR" property="passRate" />
<result column="last_result" jdbcType="VARCHAR" property="lastResult" />
<result column="report_id" jdbcType="VARCHAR" property="reportId" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -69,7 +72,8 @@
</where>
</sql>
<sql id="Base_Column_List">
id, test_plan_id, api_scenario_id, `status`, environment_id, create_time, update_time
id, test_plan_id, api_scenario_id, `status`, environment_id, create_time, update_time,
pass_rate, last_result, report_id
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.TestPlanApiScenarioExample" resultMap="BaseResultMap">
select
@ -104,10 +108,12 @@
<insert id="insert" parameterType="io.metersphere.base.domain.TestPlanApiScenario">
insert into test_plan_api_scenario (id, test_plan_id, api_scenario_id,
`status`, environment_id, create_time,
update_time)
update_time, pass_rate, last_result,
report_id)
values (#{id,jdbcType=VARCHAR}, #{testPlanId,jdbcType=VARCHAR}, #{apiScenarioId,jdbcType=VARCHAR},
#{status,jdbcType=VARCHAR}, #{environmentId,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT},
#{updateTime,jdbcType=BIGINT})
#{updateTime,jdbcType=BIGINT}, #{passRate,jdbcType=VARCHAR}, #{lastResult,jdbcType=VARCHAR},
#{reportId,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestPlanApiScenario">
insert into test_plan_api_scenario
@ -133,6 +139,15 @@
<if test="updateTime != null">
update_time,
</if>
<if test="passRate != null">
pass_rate,
</if>
<if test="lastResult != null">
last_result,
</if>
<if test="reportId != null">
report_id,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -156,6 +171,15 @@
<if test="updateTime != null">
#{updateTime,jdbcType=BIGINT},
</if>
<if test="passRate != null">
#{passRate,jdbcType=VARCHAR},
</if>
<if test="lastResult != null">
#{lastResult,jdbcType=VARCHAR},
</if>
<if test="reportId != null">
#{reportId,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.TestPlanApiScenarioExample" resultType="java.lang.Long">
@ -188,6 +212,15 @@
<if test="record.updateTime != null">
update_time = #{record.updateTime,jdbcType=BIGINT},
</if>
<if test="record.passRate != null">
pass_rate = #{record.passRate,jdbcType=VARCHAR},
</if>
<if test="record.lastResult != null">
last_result = #{record.lastResult,jdbcType=VARCHAR},
</if>
<if test="record.reportId != null">
report_id = #{record.reportId,jdbcType=VARCHAR},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -201,7 +234,10 @@
`status` = #{record.status,jdbcType=VARCHAR},
environment_id = #{record.environmentId,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT}
update_time = #{record.updateTime,jdbcType=BIGINT},
pass_rate = #{record.passRate,jdbcType=VARCHAR},
last_result = #{record.lastResult,jdbcType=VARCHAR},
report_id = #{record.reportId,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -227,6 +263,15 @@
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=BIGINT},
</if>
<if test="passRate != null">
pass_rate = #{passRate,jdbcType=VARCHAR},
</if>
<if test="lastResult != null">
last_result = #{lastResult,jdbcType=VARCHAR},
</if>
<if test="reportId != null">
report_id = #{reportId,jdbcType=VARCHAR},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
@ -237,7 +282,10 @@
`status` = #{status,jdbcType=VARCHAR},
environment_id = #{environmentId,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT}
update_time = #{updateTime,jdbcType=BIGINT},
pass_rate = #{passRate,jdbcType=VARCHAR},
last_result = #{lastResult,jdbcType=VARCHAR},
report_id = #{reportId,jdbcType=VARCHAR}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -6,7 +6,6 @@ import io.metersphere.api.dto.dataCount.ApiDataCountResult;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@ -28,4 +27,6 @@ public interface ExtApiScenarioMapper {
long countByProjectIDAndCreatInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp);
List<ApiDataCountResult> countRunResultByProjectID(String projectId);
List<String> selectIdsNotExistsInPlan(String projectId, String planId);
}

View File

@ -40,6 +40,12 @@
<if test="request.projectId != null">
AND api_scenario.project_id = #{request.projectId}
</if>
<if test="request.ids != null and request.ids.size() > 0">
AND api_scenario.id in
<foreach collection="request.ids" item="itemId" separator="," open="(" close=")">
#{itemId}
</foreach>
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
AND api_scenario.api_scenario_module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
@ -123,4 +129,14 @@
GROUP BY groupField
</select>
<select id="selectIdsNotExistsInPlan" resultType="java.lang.String">
select c.id
from api_scenario c
where c.project_id = #{projectId} and c.id not in (
select pc.api_scenario_id
from test_plan_api_scenario pc
where pc.test_plan_id = #{planId}
)
</select>
</mapper>

View File

@ -0,0 +1,18 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.TestPlanScenarioRequest;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.base.domain.TestPlanApiScenario;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtTestPlanScenarioCaseMapper {
void insertIfNotExists(@Param("request") TestPlanApiScenario request);
List<ApiScenarioDTO> list(@Param("request") TestPlanScenarioRequest request);
}

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper">
<insert id="insertIfNotExists" parameterType="io.metersphere.base.domain.TestPlanApiScenario">
-- 查询没有数据再插入
INSERT INTO test_plan_api_scenario(id, test_plan_id, api_scenario_id, create_time, update_time)
SELECT #{request.id}, #{request.testPlanId}, #{request.apiScenarioId}, #{request.createTime}, #{request.updateTime}
FROM DUAL
WHERE NOT EXISTS(
SELECT id FROM
test_plan_api_scenario
WHERE test_plan_id = #{request.testPlanId} and api_scenario_id = #{request.apiScenarioId}
)
</insert>
<select id="list" resultType="io.metersphere.api.dto.automation.ApiScenarioDTO">
select
t.id, t.environment_id, t.create_time, t.update_time, t.last_result, t.pass_rate, t.report_id,
c.id as case_id, c.project_id, c.user_id,c.api_scenario_module_id, c.module_path, c.name, c.level,
c.status, c.principal, c.step_total, c.follow_people, c.schedule, c.description,
p.name as project_name, p.id as project_id, u.name as user_name
from
test_plan_api_scenario t
inner join
api_scenario c
on t.api_scenario_id = c.id and c.status != 'Trash'
left join project p
on c.project_id = p.id
left join user u
on c.user_id = u.id
where 1
<if test="request.ids != null and request.ids.size() > 0">
<if test="request.projectId != null and request.projectId!=''">
and
</if>
t.id in
<foreach collection="request.ids" item="caseId" separator="," open="(" close=")">
#{caseId}
</foreach>
</if>
<if test="request.name != null and request.name!=''">
and c.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and c.api_scenario_module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key == 'priority'">
and c.priority in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
</choose>
</if>
</foreach>
</if>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
<choose>
<when test="order.name == 'update_time'">
t.${order.name} ${order.type}
</when>
<otherwise>
${order.name} ${order.type}
</otherwise>
</choose>
</foreach>
</if>
</select>
</mapper>

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants;
public enum ApiRunMode {
RUN, DEBUG,DELIMIT,SCENARIO, API_PLAN
RUN, DEBUG,DELIMIT,SCENARIO, API_PLAN, SCENARIO_PLAN
}

View File

@ -0,0 +1,56 @@
package io.metersphere.track.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.automation.*;
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.track.request.testcase.TestPlanApiCaseBatchRequest;
import io.metersphere.track.service.TestPlanScenarioCaseService;
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;
@RequestMapping("/test/plan/scenario/case")
@RestController
public class TestPlanScenarioCaseController {
@Resource
TestPlanScenarioCaseService testPlanScenarioCaseService;
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<ApiScenarioDTO>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody TestPlanScenarioRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, testPlanScenarioCaseService.list(request));
}
@PostMapping("/relevance/list/{goPage}/{pageSize}")
public Pager<List<ApiScenarioDTO>> relevanceList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody ApiScenarioRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
return PageUtils.setPageInfo(page, testPlanScenarioCaseService.relevanceList(request));
}
@GetMapping("/delete/{planId}/{id}")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public int deleteTestCase(@PathVariable String planId, @PathVariable String id) {
return testPlanScenarioCaseService.delete(planId, id);
}
@PostMapping("/batch/delete")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public void deleteApiCaseBath(@RequestBody TestPlanApiCaseBatchRequest request) {
testPlanScenarioCaseService.deleteApiCaseBath(request);
}
@PostMapping(value = "/run")
public String run(@RequestBody RunScenarioRequest request) {
request.setExecuteType(ExecuteType.Completed.name());
return testPlanScenarioCaseService.run(request);
}
}

View File

@ -0,0 +1,91 @@
package io.metersphere.track.service;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.dto.automation.TestPlanScenarioRequest;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.base.domain.TestPlanApiScenario;
import io.metersphere.base.domain.TestPlanApiScenarioExample;
import io.metersphere.base.mapper.TestPlanApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanScenarioCaseService {
@Resource
ApiAutomationService apiAutomationService;
@Resource
TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@Resource
ExtTestPlanScenarioCaseMapper extTestPlanScenarioCaseMapper;
public List<ApiScenarioDTO> list(TestPlanScenarioRequest request) {
request.setProjectId(null);
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
List<ApiScenarioDTO> apiTestCases = extTestPlanScenarioCaseMapper.list(request);
if (CollectionUtils.isEmpty(apiTestCases)) {
return apiTestCases;
}
List<String> projectIds = apiTestCases.stream()
.map(ApiScenarioDTO::getProjectId)
.distinct()
.collect(Collectors.toList());
projectIds.forEach(projectId -> {
// apiAutomationService.buildScenarioInfo(projectId, apiTestCases);
});
return apiTestCases;
}
public List<ApiScenarioDTO> relevanceList(ApiScenarioRequest request) {
List<String> ids = apiAutomationService.selectIdsNotExistsInPlan(request.getProjectId(), request.getPlanId());
if (CollectionUtils.isEmpty(ids)) {
return new ArrayList<>();
}
request.setIds(ids);
return apiAutomationService.list(request);
}
public int delete(String planId, String id) {
// apiDefinitionExecResultService.deleteByResourceId(id);
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria()
.andTestPlanIdEqualTo(planId)
.andIdEqualTo(id);
return testPlanApiScenarioMapper.deleteByExample(example);
}
public void deleteApiCaseBath(TestPlanApiCaseBatchRequest request) {
// apiDefinitionExecResultService.deleteByResourceIds(request.getIds());
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria()
.andIdIn(request.getIds())
.andTestPlanIdEqualTo(request.getPlanId());
testPlanApiScenarioMapper.deleteByExample(example);
}
public String run(RunScenarioRequest request) {
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
example.createCriteria().andIdIn(request.getPlanCaseIds());
List<String> scenarioIds = testPlanApiScenarioMapper.selectByExample(example).stream()
.map(TestPlanApiScenario::getApiScenarioId)
.collect(Collectors.toList());
scenarioIds.addAll(scenarioIds);
request.setScenarioIds(scenarioIds);
request.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
return apiAutomationService.run(request);
}
}

View File

@ -19,6 +19,9 @@ CREATE TABLE IF NOT EXISTS `test_plan_api_scenario` (
`environment_id` varchar(50) NULL COMMENT 'Relevance environment_id',
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
`pass_rate` varchar(100) DEFAULT NULL,
`last_result` varchar(100) DEFAULT NULL,
`report_id` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `plan_id_scenario_id` (`test_plan_id`, `api_scenario_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

View File

@ -64,7 +64,7 @@
<!--要生成的数据库表 -->
<table tableName="api_definition_exec_result"/>
<table tableName="test_plan_api_scenario"/>
<!--<table tableName="test_plan_api_scenario"/>-->
<!--<table tableName="test_plan"/>-->
<!--<table tableName="api_scenario_report"/>-->

View File

@ -1,10 +1,12 @@
<template>
<div>
<slot name="header"></slot>
<ms-node-tree
v-loading="result.loading"
:tree-nodes="data"
:type="'edit'"
:type="isReadOnly ? 'view' : 'edit'"
@add="add"
@edit="edit"
@drag="drag"
@ -13,13 +15,13 @@
ref="nodeTree">
<template v-slot:header>
<el-input style="width: 275px;" :placeholder="$t('test_track.module.search')" v-model="condition.filterText"
<el-input class="module-input" :placeholder="$t('test_track.module.search')" v-model="condition.filterText"
size="small">
<template v-slot:append>
<el-button icon="el-icon-folder-add" @click="addScenario"/>
</template>
</el-input>
<module-trash-button :condition="condition" :exe="enableTrash"/>
<module-trash-button v-if="!isReadOnly" :condition="condition" :exe="enableTrash"/>
</template>
</ms-node-tree>
@ -48,6 +50,14 @@
MsAddBasisScenario,
SelectMenu,
},
props: {
isReadOnly: {
type: Boolean,
default() {
return false
}
}
},
data() {
return {
result: {},
@ -206,4 +216,8 @@
.ms-api-buttion {
width: 30px;
}
.module-input {
width: 275px;
}
</style>

View File

@ -1,6 +1,8 @@
<template>
<div v-loading="result.loading">
<slot name="header"></slot>
<ms-node-tree
v-loading="result.loading"
:tree-nodes="data"

View File

@ -0,0 +1,140 @@
<template>
<div>
<el-card class="table-card" v-loading="result.loading">
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table" @select-all="handleSelectAll" @select="handleSelect">
<el-table-column type="selection"/>
<el-table-column prop="name" :label="$t('api_test.automation.scenario_name')"
show-overflow-tooltip/>
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag v-if="scope.row.level == 'P0'" type="info" effect="plain" content="P0"/>
<ms-tag v-if="scope.row.level == 'P1'" type="warning" effect="plain" content="P1"/>
<ms-tag v-if="scope.row.level == 'P2'" type="success" effect="plain" content="P2"/>
<ms-tag v-if="scope.row.level == 'P3'" type="danger" effect="plain" content="P3"/>
</template>
</el-table-column>
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px">
<template v-slot:default="scope">
<div v-for="itemName in scope.row.tagNames" :key="itemName">
<ms-tag type="success" effect="plain" :content="itemName"/>
</div>
</template>
</el-table-column>
<el-table-column prop="userId" :label="$t('api_test.automation.creator')" show-overflow-tooltip/>
<el-table-column prop="updateTime" :label="$t('api_test.automation.update_time')" width="180">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="stepTotal" :label="$t('api_test.automation.step')" show-overflow-tooltip/>
<el-table-column prop="lastResult" :label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}">
<el-link type="success" @click="showReport(row)" v-if="row.lastResult === 'Success'">{{ $t('api_test.automation.success') }}</el-link>
<el-link type="danger" @click="showReport(row)" v-if="row.lastResult === 'Fail'">{{ $t('api_test.automation.fail') }}</el-link>
</template>
</el-table-column>
<el-table-column prop="passRate" :label="$t('api_test.automation.passing_rate')"
show-overflow-tooltip/>
</el-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</div>
</template>
<script>
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import MsTag from "../../../../../common/components/MsTag";
import {getUUID, getCurrentProjectID} from "@/common/js/utils";
import MsApiReportDetail from "../../../../../api/automation/report/ApiReportDetail";
import MsTableMoreBtn from "../../../../../api/automation/scenario/TableMoreBtn";
import MsTestPlanList from "../../../../../api/automation/scenario/testplan/TestPlanList";
import TestPlanScenarioListHeader from "./TestPlanScenarioListHeader";
import {_handleSelect, _handleSelectAll} from "../../../../../../../common/js/tableUtils";
export default {
name: "RelevanceScenarioList",
components: {
TestPlanScenarioListHeader,
MsTablePagination, MsTableMoreBtn, ShowMoreBtn, MsTableHeader, MsTag, MsApiReportDetail, MsTestPlanList},
props: {
referenced: {
type: Boolean,
default: false,
},
selectNodeIds: Array,
projectId: String,
planId: String,
},
data() {
return {
result: {},
condition: {},
currentScenario: {},
schedule: {},
selectAll: false,
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
reportId: "",
infoDb: false,
selectRows: new Set()
}
},
created() {
this.search();
},
watch: {
selectNodeIds() {
this.search();
},
projectId() {
this.search();
},
},
methods: {
search() {
this.selectRows = new Set();
this.loading = true;
this.condition.filters = ["Prepare", "Underway", "Completed"];
this.condition.moduleIds = this.selectNodeIds;
if (this.projectId != null) {
this.condition.projectId = this.projectId;
}
if (this.planId != null) {
this.condition.planId = this.planId;
}
let url = "/test/plan/scenario/case/relevance/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
});
},
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.tableData, this.selectRows);
},
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
},
}
}
</script>
<style scoped>
/deep/ .el-drawer__header {
margin-bottom: 0px;
}
</style>

View File

@ -0,0 +1,131 @@
<template>
<test-case-relevance-base
@setProject="setProject"
@save="saveCaseRelevance"
:plan-id="planId"
ref="baseRelevance">
<template v-slot:aside>
<ms-api-scenario-module
@nodeSelectEvent="nodeChange"
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
:is-read-only="true"
ref="nodeTree"/>
</template>
<relevance-scenario-list
:select-node-ids="selectNodeIds"
:trash-enable="trashEnable"
:plan-id="planId"
:project-id="projectId"
ref="apiScenarioList"/>
</test-case-relevance-base>
</template>
<script>
import TestCaseRelevanceBase from "../base/TestCaseRelevanceBase";
import MsApiModule from "../../../../../api/definition/components/module/ApiModule";
import {getCurrentProjectID} from "../../../../../../../common/js/utils";
import ApiList from "../../../../../api/definition/components/list/ApiList";
import ApiCaseSimpleList from "../../../../../api/definition/components/list/ApiCaseSimpleList";
import MsApiScenarioList from "../../../../../api/automation/scenario/ApiScenarioList";
import MsApiScenarioModule from "../../../../../api/automation/scenario/ApiScenarioModule";
import RelevanceScenarioList from "./RelevanceScenarioList";
export default {
name: "TestCaseScenarioRelevance",
components: {
RelevanceScenarioList,
MsApiScenarioModule,
MsApiScenarioList,
ApiCaseSimpleList,
ApiList,
MsApiModule,
TestCaseRelevanceBase,
},
data() {
return {
showCasePage: true,
currentProtocol: null,
currentModule: null,
selectNodeIds: [],
moduleOptions: {},
trashEnable: false,
condition: {},
currentRow: {},
projectId: ""
};
},
props: {
planId: {
type: String
},
},
watch: {
planId() {
this.condition.planId = this.planId;
},
},
methods: {
open() {
this.$refs.baseRelevance.open();
},
setProject(projectId) {
this.projectId = projectId;
},
refresh(data) {
this.$refs.apiScenarioList.search(data);
},
nodeChange(node, nodeIds, pNodes) {
this.selectNodeIds = nodeIds;
},
handleProtocolChange(protocol) {
this.currentProtocol = protocol;
},
setModuleOptions(data) {
this.moduleOptions = data;
},
saveCaseRelevance() {
let param = {};
let url = '';
let selectIds = [];
url = '/api/automation/relevance';
selectIds = Array.from(this.$refs.apiScenarioList.selectRows).map(row => row.id);
param.planId = this.planId;
param.selectIds = selectIds;
this.result = this.$post(url, param, () => {
this.$success(this.$t('commons.save_success'));
this.$emit('refresh');
this.refresh();
this.$refs.baseRelevance.close();
});
},
}
}
</script>
<style scoped>
/deep/ .select-menu {
margin-bottom: 15px;
}
/deep/ .environment-select {
float: right;
margin-right: 10px;
}
/deep/ .module-input {
width: 243px;
}
</style>

View File

@ -3,18 +3,42 @@
<ms-test-plan-common-component>
<template v-slot:aside>
<ms-api-module
v-if="model === 'api'"
@nodeSelectEvent="nodeChange"
@protocolChange="handleProtocolChange"
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
:is-read-only="true"
:type="'edit'"
ref="nodeTree"/>
ref="apiNodeTree">
<template v-slot:header>
<div class="model-change-radio">
<el-radio v-model="model" label="api">接口用例</el-radio>
<el-radio v-model="model" label="scenario">场景用例</el-radio>
</div>
</template>
</ms-api-module>
<ms-api-scenario-module
v-if="model === 'scenario'"
@nodeSelectEvent="nodeChange"
@refreshTable="refresh"
@setModuleOptions="setModuleOptions"
:is-read-only="true"
ref="nodeTree">
<template v-slot:header>
<div class="model-change-radio">
<el-radio v-model="model" label="api">接口用例</el-radio>
<el-radio v-model="model" label="scenario">场景用例</el-radio>
</div>
</template>
</ms-api-scenario-module>
</template>
<template v-slot:main>
<!--测试用例列表-->
<test-plan-api-case-list
v-if="model === 'api'"
:current-protocol="currentProtocol"
:currentRow="currentRow"
:select-node-ids="selectNodeIds"
@ -24,13 +48,29 @@
:plan-id="planId"
@relevanceCase="openTestCaseRelevanceDialog"
ref="apiCaseList"/>
<ms-test-plan-api-scenario-list
v-if="model === 'scenario'"
:select-node-ids="selectNodeIds"
:trash-enable="trashEnable"
:plan-id="planId"
@relevanceCase="openTestCaseRelevanceDialog"
ref="apiScenarioList"/>
</template>
<test-case-api-relevance
@refresh="refresh"
:plan-id="planId"
:model="model"
ref="apiCaseRelevance"/>
<test-case-scenario-relevance
@refresh="refresh"
:plan-id="planId"
:model="model"
ref="scenarioCaseRelevance"/>
</ms-test-plan-common-component>
</template>
@ -43,10 +83,16 @@
import TestCaseApiRelevance from "./TestCaseApiRelevance";
import ApiCaseSimpleList from "../../../../../api/definition/components/list/ApiCaseSimpleList";
import MsApiModule from "../../../../../api/definition/components/module/ApiModule";
import MsApiScenarioModule from "../../../../../api/automation/scenario/ApiScenarioModule";
import MsTestPlanApiScenarioList from "./TestPlanApiScenarioList";
import TestCaseScenarioRelevance from "./TestCaseScenarioRelevance";
export default {
name: "TestPlanApi",
components: {
TestCaseScenarioRelevance,
MsTestPlanApiScenarioList,
MsApiScenarioModule,
MsApiModule,
ApiCaseSimpleList,
TestCaseApiRelevance,
@ -58,7 +104,6 @@
data() {
return {
result: {},
selectParentNodes: [],
treeNodes: [],
currentRow: "",
trashEnable: false,
@ -66,35 +111,32 @@
currentModule: null,
selectNodeIds: [],
moduleOptions: {},
model: 'api'
}
},
props: [
'planId'
],
mounted() {
// this.initData();
// this.openTestCaseEdit(this.$route.path);
},
watch: {
'$route'(to, from) {
// this.openTestCaseEdit(to.path);
},
planId() {
// this.initData();
model() {
this.selectNodeIds = [];
this.moduleOptions = {};
}
},
methods: {
setProject(projectId) {
this.projectId = projectId;
},
// isApiListEnableChange(data) {
// this.isApiListEnable = data;
// },
refresh(data) {
this.$refs.nodeTree.list();
this.$refs.apiCaseList.initTable();
if (this.$refs.apiNodeTree) {
this.$refs.apiNodeTree.list();
}
if (this.$refs.apiCaseList) {
this.$refs.apiCaseList.initTable();
}
if (this.$refs.apiScenarioList) {
this.$refs.apiScenarioList.search();
}
},
nodeChange(node, nodeIds, pNodes) {
@ -110,67 +152,22 @@
saveCaseRelevance() {
let url = '';
let selectIds = [];
// if (this.isApiListEnable) {
// url = '/api/definition/relevance';
// selectIds = Array.from(this.$refs.apiList.selectRows).map(row => row.id);
// } else {
// url = '/api/testcase/relevance';
// selectIds = Array.from(this.$refs.apiCaseList.selectRows).map(row => row.id);
// }
let param = {};
param.planId = this.planId;
param.selectIds = selectIds;
// param.request = this.condition;
//
// if (this.testCases.length === param.testCaseIds.length) {
// param.testCaseIds = ['all'];
// }
this.result = this.$post(url, param, () => {
this.$success(this.$t('commons.save_success'));
this.refresh();
this.$refs.baseRelevance.close();
});
},
// refresh() {
// this.selectNodeIds = [];
// this.selectParentNodes = [];
// this.$refs.testCaseRelevance.search();
// this.getNodeTreeByPlanId();
// },
// initData() {
// this.getNodeTreeByPlanId();
// },
openTestCaseRelevanceDialog() {
this.$refs.apiCaseRelevance.open();
openTestCaseRelevanceDialog(model) {
if (model === 'scenario') {
this.$refs.scenarioCaseRelevance.open();
} else {
this.$refs.apiCaseRelevance.open();
}
},
// nodeChange(nodeIds, pNodes) {
// this.selectNodeIds = nodeIds;
// this.selectParentNodes = pNodes;
// // node
// this.$refs.testPlanTestCaseList.currentPage = 1;
// this.$refs.testPlanTestCaseList.pageSize = 10;
// },
// getNodeTreeByPlanId() {
// if (this.planId) {
// this.result = this.$get("/case/node/list/plan/" + this.planId, response => {
// this.treeNodes = response.data;
// });
// }
// },
// openTestCaseEdit(path) {
// if (path.indexOf("/plan/view/edit") >= 0) {
// let caseId = this.$route.params.caseId;
// this.$get('/test/plan/case/get/' + caseId, response => {
// let testCase = response.data;
// if (testCase) {
// this.$refs.testPlanTestCaseList.handleEdit(testCase);
// this.$router.push('/track/plan/view/' + testCase.planId);
// }
// });
// }
// },
}
}
@ -178,4 +175,10 @@
<style scoped>
.model-change-radio {
height: 25px;
line-height: 25px;
margin: 5px 10px;
}
</style>

View File

@ -0,0 +1,238 @@
<template>
<div>
<el-card class="table-card" v-loading="loading">
<template v-slot:header>
<test-plan-scenario-list-header
:condition="condition"
@refresh="search"
@relevanceCase="$emit('relevanceCase', 'scenario')"/>
</template>
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table" @select-all="handleSelectAll" @select="handleSelect">
<el-table-column type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="{row}">
<show-more-btn :is-show="isSelect(row)" :buttons="buttons" :size="selectRows.length"/>
</template>
</el-table-column>
<el-table-column prop="name" :label="$t('api_test.automation.scenario_name')"
show-overflow-tooltip/>
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag v-if="scope.row.level == 'P0'" type="info" effect="plain" content="P0"/>
<ms-tag v-if="scope.row.level == 'P1'" type="warning" effect="plain" content="P1"/>
<ms-tag v-if="scope.row.level == 'P2'" type="success" effect="plain" content="P2"/>
<ms-tag v-if="scope.row.level == 'P3'" type="danger" effect="plain" content="P3"/>
</template>
</el-table-column>
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px">
<template v-slot:default="scope">
<div v-for="itemName in scope.row.tagNames" :key="itemName">
<ms-tag type="success" effect="plain" :content="itemName"/>
</div>
</template>
</el-table-column>
<el-table-column prop="userId" :label="$t('api_test.automation.creator')" show-overflow-tooltip/>
<el-table-column prop="updateTime" :label="$t('api_test.automation.update_time')" width="180">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="stepTotal" :label="$t('api_test.automation.step')" show-overflow-tooltip/>
<el-table-column prop="lastResult" :label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}">
<el-link type="success" @click="showReport(row)" v-if="row.lastResult === 'Success'">{{ $t('api_test.automation.success') }}</el-link>
<el-link type="danger" @click="showReport(row)" v-if="row.lastResult === 'Fail'">{{ $t('api_test.automation.fail') }}</el-link>
</template>
</el-table-column>
<el-table-column prop="passRate" :label="$t('api_test.automation.passing_rate')"
show-overflow-tooltip/>
<el-table-column :label="$t('commons.operating')" width="200px" v-if="!referenced">
<template v-slot:default="{row}">
<el-button type="text" @click="execute(row)">{{ $t('api_test.automation.execute') }}</el-button>
<el-button type="text" @click="remove(row)">{{ $t('api_test.automation.remove') }}</el-button>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<div>
<!-- 执行结果 -->
<el-drawer :visible.sync="runVisible" :destroy-on-close="true" direction="ltr" :withHeader="true" :modal="false" size="90%">
<ms-api-report-detail @refresh="search" :infoDb="infoDb" :report-id="reportId" :currentProjectId="projectId"/>
</el-drawer>
</div>
</el-card>
</div>
</template>
<script>
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import MsTag from "../../../../../common/components/MsTag";
import {getUUID, getCurrentProjectID} from "@/common/js/utils";
import MsApiReportDetail from "../../../../../api/automation/report/ApiReportDetail";
import MsTableMoreBtn from "../../../../../api/automation/scenario/TableMoreBtn";
import MsScenarioExtendButtons from "@/business/components/api/automation/scenario/ScenarioExtendBtns";
import MsTestPlanList from "../../../../../api/automation/scenario/testplan/TestPlanList";
import TestPlanScenarioListHeader from "./TestPlanScenarioListHeader";
import {_handleSelect, _handleSelectAll} from "../../../../../../../common/js/tableUtils";
export default {
name: "MsTestPlanApiScenarioList",
components: {
TestPlanScenarioListHeader,
MsTablePagination, MsTableMoreBtn, ShowMoreBtn, MsTableHeader, MsTag, MsApiReportDetail, MsScenarioExtendButtons, MsTestPlanList},
props: {
referenced: {
type: Boolean,
default: false,
},
selectNodeIds: Array,
planId: String
},
data() {
return {
loading: false,
condition: {},
currentScenario: {},
schedule: {},
selectAll: false,
tableData: [],
currentPage: 1,
pageSize: 10,
total: 0,
reportId: "",
infoDb: false,
runVisible: false,
projectId: "",
runData: [],
buttons: [
{
name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch
},
// {
// name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute
// }
],
selectRows: new Set()
}
},
created() {
this.projectId = getCurrentProjectID();
this.search();
},
watch: {
selectNodeIds() {
this.search();
},
},
methods: {
search() {
this.selectRows = new Set();
this.loading = true;
this.condition.moduleIds = this.selectNodeIds;
if (this.projectId != null) {
this.condition.projectId = this.projectId;
}
let url = "/test/plan/scenario/case/list/" + this.currentPage + "/" + this.pageSize;
this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.loading = false;
});
},
handleBatchExecute() {
this.infoDb = false;
let url = "/test/plan/scenario/case/run";
let run = {};
run.planCaseIds = Array.from(this.selectRows).map(row => row.id);
run.id = getUUID();
run.projectId = getCurrentProjectID();
this.$post(url, run, response => {
let data = response.data;
this.runVisible = true;
this.reportId = run.id;
});
},
edit(row) {
this.$emit('edit', row);
},
reductionApi(row) {
row.scenarioDefinition = null;
let rows = [row];
this.$post("/api/automation/reduction", rows, response => {
this.$success(this.$t('commons.save_success'));
this.search();
})
},
execute(row) {
this.infoDb = false;
let url = "/test/plan/scenario/case/run";
let run = {};
run.id = row.id;
run.projectId = row.projectId;
run.planCaseIds = [];
run.planCaseIds.push(row.id);
this.$post(url, run, response => {
this.runVisible = true;
this.reportId = response.data;
});
},
copy(row) {
row.copy = true;
this.$emit('edit', row);
},
showReport(row) {
this.runVisible = true;
this.infoDb = true;
this.reportId = row.reportId;
},
remove(row) {
this.$get('/test/plan/scenario/case/delete/' + this.planId + '/' + row.id, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
});
return;
},
isSelect(row) {
return this.selectRows.has(row);
},
handleSelectAll(selection) {
_handleSelectAll(this, selection, this.tableData, this.selectRows);
},
handleSelect(selection, row) {
_handleSelect(this, selection, row, this.selectRows);
},
handleDeleteBatch() {
this.$alert(this.$t('api_test.definition.request.delete_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let param = {};
param.ids = Array.from(this.selectRows).map(row => row.id);
param.planId = this.planId;
this.$post('/test/plan/scenario/case/batch/delete', param, () => {
this.selectRows.clear();
this.search();
this.$success(this.$t('commons.delete_success'));
});
}
}
});
}
}
}
</script>
<style scoped>
/deep/ .el-drawer__header {
margin-bottom: 0px;
}
</style>

View File

@ -0,0 +1,38 @@
<template>
<ms-table-header :is-tester-permission="true"
:condition="condition"
@search="$emit('refresh')"
:show-create="false"
:tip="$t('commons.search_by_name_or_id')">
<template v-slot:title>
场景用例
</template>
<template v-slot:button>
<ms-table-button :is-tester-permission="true" icon="el-icon-connection"
:content="$t('test_track.plan_view.relevance_test_case')"
@click="$emit('relevanceCase')"/>
</template>
</ms-table-header>
</template>
<script>
import MsTableHeader from "../../../../../common/components/MsTableHeader";
import MsTableButton from "../../../../../common/components/MsTableButton";
import MsEnvironmentSelect from "../../../../../api/definition/components/case/MsEnvironmentSelect";
export default {
name: "TestPlanScenarioListHeader",
components: {MsEnvironmentSelect, MsTableButton, MsTableHeader},
props: ['condition', 'isReadOnly'],
methods: {
}
}
</script>
<style scoped>
/deep/ .environment-select {
margin-right: 10px;
}
</style>

View File

@ -0,0 +1,40 @@
export function _handleSelectAll(component, selection, tableData, selectRows) {
if (selection.length > 0) {
if (selection.length === 1) {
selection.hashTree = [];
selectRows.add(selection[0]);
} else {
tableData.forEach(item => {
item.hashTree = [];
component.$set(item, "showMore", true);
selectRows.add(item);
});
}
} else {
selectRows.clear();
tableData.forEach(item => {
component.$set(item, "showMore", false);
})
}
}
export function _handleSelect(component, selection, row, selectRows) {
row.hashTree = [];
if (selectRows.has(row)) {
component.$set(row, "showMore", false);
selectRows.delete(row);
} else {
component.$set(row, "showMore", true);
selectRows.add(row);
}
let arr = Array.from(selectRows);
// 选中1个以上的用例时显示更多操作
if (selectRows.size === 1) {
component.$set(arr[0], "showMore", false);
} else if (selectRows.size === 2) {
arr.forEach(row => {
component.$set(row, "showMore", true);
})
}
}