Merge remote-tracking branch 'origin/master'

This commit is contained in:
Captain.B 2021-01-07 14:07:42 +08:00
commit 590e6c9a9c
35 changed files with 2044 additions and 25 deletions

View File

@ -265,7 +265,7 @@ public class MsHTTPSamplerProxy extends MsTestElement {
private Arguments httpArguments(List<KeyValue> list) { private Arguments httpArguments(List<KeyValue> list) {
Arguments arguments = new Arguments(); Arguments arguments = new Arguments();
list.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> { list.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue -> {
HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), keyValue.getValue() != null && keyValue.getValue().startsWith("@") ? ScriptEngineUtils.calculate(keyValue.getValue()) : keyValue.getValue()); HTTPArgument httpArgument = new HTTPArgument(keyValue.getName(), StringUtils.isNotEmpty(keyValue.getValue()) && keyValue.getValue().startsWith("@") ? ScriptEngineUtils.calculate(keyValue.getValue()) : keyValue.getValue());
httpArgument.setAlwaysEncoded(keyValue.isEncode()); httpArgument.setAlwaysEncoded(keyValue.isEncode());
if (StringUtils.isNotBlank(keyValue.getContentType())) { if (StringUtils.isNotBlank(keyValue.getContentType())) {
httpArgument.setContentType(keyValue.getContentType()); httpArgument.setContentType(keyValue.getContentType());

View File

@ -1,14 +1,21 @@
package io.metersphere.api.dto.definition.request.sampler; package io.metersphere.api.dto.definition.request.sampler;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType; import com.alibaba.fastjson.annotation.JSONType;
import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.ParameterConfig; import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.dto.scenario.DatabaseConfig; import io.metersphere.api.dto.scenario.DatabaseConfig;
import io.metersphere.api.dto.scenario.KeyValue; import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.service.ApiTestEnvironmentService;
import io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.CommonBeanFactory;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.config.Arguments; import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.protocol.jdbc.config.DataSourceElement; import org.apache.jmeter.protocol.jdbc.config.DataSourceElement;
import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler; import org.apache.jmeter.protocol.jdbc.sampler.JDBCSampler;
@ -17,6 +24,7 @@ import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@ -40,6 +48,8 @@ public class MsJDBCSampler extends MsTestElement {
private String environmentId; private String environmentId;
@JSONField(ordinal = 27) @JSONField(ordinal = 27)
private Object requestResult; private Object requestResult;
@JSONField(ordinal = 28)
private String dataSourceId;
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) { public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
if (!this.isEnable()) { if (!this.isEnable()) {
@ -48,6 +58,12 @@ public class MsJDBCSampler extends MsTestElement {
if (this.getReferenced() != null && this.getReferenced().equals("REF")) { if (this.getReferenced() != null && this.getReferenced().equals("REF")) {
this.getRefElement(this); this.getRefElement(this);
} }
if (StringUtils.isNotEmpty(dataSourceId)) {
initDataSource();
}
if (this.dataSource == null) {
MSException.throwException("数据源为空无法执行");
}
final HashTree samplerHashTree = tree.add(jdbcSampler()); final HashTree samplerHashTree = tree.add(jdbcSampler());
tree.add(jdbcDataSource()); tree.add(jdbcDataSource());
tree.add(arguments(this.getName() + " Variables", this.getVariables())); tree.add(arguments(this.getName() + " Variables", this.getVariables()));
@ -58,6 +74,20 @@ public class MsJDBCSampler extends MsTestElement {
} }
} }
private void initDataSource() {
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
ApiTestEnvironmentWithBLOBs environment = environmentService.get(this.dataSourceId);
if (environment != null && environment.getConfig() != null) {
EnvironmentConfig config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
if (CollectionUtils.isNotEmpty(config.getDatabaseConfigs())) {
List<DatabaseConfig> databaseConfigs = config.getDatabaseConfigs().stream().filter((DatabaseConfig d) -> this.dataSourceId.equals(d.getId())).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(databaseConfigs)) {
this.dataSource = databaseConfigs.get(0);
}
}
}
}
private Arguments arguments(String name, List<KeyValue> variables) { private Arguments arguments(String name, List<KeyValue> variables) {
Arguments arguments = new Arguments(); Arguments arguments = new Arguments();
if (!variables.isEmpty()) { if (!variables.isEmpty()) {

View File

@ -49,7 +49,7 @@ public class KeyValue {
} }
public boolean isValid() { public boolean isValid() {
return (StringUtils.isNotBlank(name) || StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file"); return (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(value)) && !StringUtils.equalsIgnoreCase(type, "file");
} }
public boolean isFile() { public boolean isFile() {

View File

@ -528,15 +528,12 @@ public class ApiAutomationService {
} }
public void createSchedule(Schedule request) { public void createSchedule(Schedule request) {
Schedule schedule = scheduleService.buildApiTestSchedule(request); Schedule schedule = scheduleService.buildApiTestSchedule(request);
schedule.setJob(ApiScenarioTestJob.class.getName()); schedule.setJob(ApiScenarioTestJob.class.getName());
schedule.setGroup(ScheduleGroup.API_SCENARIO_TEST.name()); schedule.setGroup(ScheduleGroup.API_SCENARIO_TEST.name());
schedule.setType(ScheduleType.CRON.name()); schedule.setType(ScheduleType.CRON.name());
scheduleService.addSchedule(schedule); scheduleService.addSchedule(schedule);
this.addOrUpdateApiScenarioCronJob(request); this.addOrUpdateApiScenarioCronJob(request);
} }
public void updateSchedule(Schedule request) { public void updateSchedule(Schedule request) {

View File

@ -2,6 +2,9 @@ package io.metersphere.api.service;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.api.dto.APIReportResult; import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.ApiTestImportRequest; import io.metersphere.api.dto.ApiTestImportRequest;
import io.metersphere.api.dto.automation.ApiScenarioRequest; import io.metersphere.api.dto.automation.ApiScenarioRequest;
@ -9,6 +12,8 @@ import io.metersphere.api.dto.automation.ReferenceDTO;
import io.metersphere.api.dto.datacount.ApiDataCountResult; import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.api.dto.definition.*; import io.metersphere.api.dto.definition.*;
import io.metersphere.api.dto.definition.parse.ApiDefinitionImport; import io.metersphere.api.dto.definition.parse.ApiDefinitionImport;
import io.metersphere.api.dto.definition.request.*;
import io.metersphere.api.dto.scenario.KeyValue;
import io.metersphere.api.dto.scenario.request.RequestType; import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.TestResult; import io.metersphere.api.jmeter.TestResult;
@ -24,6 +29,7 @@ import io.metersphere.base.mapper.ext.ExtApiScenarioMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanMapper; import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode; import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*; import io.metersphere.commons.utils.*;
import io.metersphere.i18n.Translator; import io.metersphere.i18n.Translator;
@ -36,6 +42,7 @@ import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.jorphan.collections.HashTree; import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;
import org.aspectj.util.FileUtil; import org.aspectj.util.FileUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -333,6 +340,55 @@ public class ApiDefinitionService {
return request.getId(); return request.getId();
} }
/**
* 内部构建HashTree 定时任务发起的执行
* @param request
* @return
*/
public String run(RunDefinitionRequest request,ApiTestCaseWithBLOBs item) {
MsTestPlan testPlan = new MsTestPlan();
testPlan.setHashTree(new LinkedList<>());
HashTree jmeterHashTree = new ListedHashTree();
try {
MsThreadGroup group = new MsThreadGroup();
group.setLabel(item.getName());
group.setName(UUID.randomUUID().toString());
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JSONObject element = JSON.parseObject(item.getRequest());
MsScenario scenario = JSONObject.parseObject(item.getRequest(), MsScenario.class);
// 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取
if (element != null && StringUtils.isNotEmpty(element.getString("hashTree"))) {
LinkedList<MsTestElement> elements = mapper.readValue(element.getString("hashTree"),
new TypeReference<LinkedList<MsTestElement>>() {});
scenario.setHashTree(elements);
}
if (StringUtils.isNotEmpty(element.getString("variables"))) {
LinkedList<KeyValue> variables = mapper.readValue(element.getString("variables"),
new TypeReference<LinkedList<KeyValue>>() {});
scenario.setVariables(variables);
}
group.setEnableCookieShare(scenario.isEnableCookieShare());
LinkedList<MsTestElement> scenarios = new LinkedList<>();
scenarios.add(scenario);
group.setHashTree(scenarios);
testPlan.getHashTree().add(group);
} catch (Exception ex) {
MSException.throwException(ex.getMessage());
}
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
String runMode = ApiRunMode.DELIMIT.name();
if (StringUtils.isNotBlank(request.getType()) && StringUtils.equals(request.getType(), ApiRunMode.API_PLAN.name())) {
runMode = ApiRunMode.API_PLAN.name();
}
// 调用执行方法
jMeterService.runDefinition(request.getId(), jmeterHashTree, request.getReportId(), runMode);
return request.getId();
}
public void addResult(TestResult res) { public void addResult(TestResult res) {
if (!res.getScenarios().isEmpty() && !res.getScenarios().get(0).getRequestResults().isEmpty()) { if (!res.getScenarios().isEmpty() && !res.getScenarios().get(0).getRequestResults().isEmpty()) {
cache.put(res.getTestId(), res.getScenarios().get(0).getRequestResults().get(0)); cache.put(res.getTestId(), res.getScenarios().get(0).getRequestResults().get(0));

View File

@ -134,6 +134,7 @@ public class HistoricalDataUpgradeService {
EnvironmentDTO dto = environmentDTOMap.get(request1.getDataSource()); EnvironmentDTO dto = environmentDTOMap.get(request1.getDataSource());
if (dto != null) { if (dto != null) {
((MsJDBCSampler) element).setEnvironmentId(dto.getEnvironmentId()); ((MsJDBCSampler) element).setEnvironmentId(dto.getEnvironmentId());
((MsJDBCSampler) element).setDataSourceId(dto.getDatabaseConfig().getId());
((MsJDBCSampler) element).setDataSource(dto.getDatabaseConfig()); ((MsJDBCSampler) element).setDataSource(dto.getDatabaseConfig());
} }
element.setType("JDBCSampler"); element.setType("JDBCSampler");

View File

@ -32,4 +32,7 @@ public class Schedule implements Serializable {
private String customData; private String customData;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
//定时任务来源 测试计划/测试场景
private String scheduleFrom;
} }

View File

@ -0,0 +1,21 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
@Data
public class TestPlanLoadCase implements Serializable {
private String id;
private String testPlanId;
private String loadCaseId;
private String status;
private Long createTime;
private Long updateTime;
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,600 @@
package io.metersphere.base.domain;
import java.util.ArrayList;
import java.util.List;
public class TestPlanLoadCaseExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
public TestPlanLoadCaseExample() {
oredCriteria = new ArrayList<Criteria>();
}
public void setOrderByClause(String orderByClause) {
this.orderByClause = orderByClause;
}
public String getOrderByClause() {
return orderByClause;
}
public void setDistinct(boolean distinct) {
this.distinct = distinct;
}
public boolean isDistinct() {
return distinct;
}
public List<Criteria> getOredCriteria() {
return oredCriteria;
}
public void or(Criteria criteria) {
oredCriteria.add(criteria);
}
public Criteria or() {
Criteria criteria = createCriteriaInternal();
oredCriteria.add(criteria);
return criteria;
}
public Criteria createCriteria() {
Criteria criteria = createCriteriaInternal();
if (oredCriteria.size() == 0) {
oredCriteria.add(criteria);
}
return criteria;
}
protected Criteria createCriteriaInternal() {
Criteria criteria = new Criteria();
return criteria;
}
public void clear() {
oredCriteria.clear();
orderByClause = null;
distinct = false;
}
protected abstract static class GeneratedCriteria {
protected List<Criterion> criteria;
protected GeneratedCriteria() {
super();
criteria = new ArrayList<Criterion>();
}
public boolean isValid() {
return criteria.size() > 0;
}
public List<Criterion> getAllCriteria() {
return criteria;
}
public List<Criterion> getCriteria() {
return criteria;
}
protected void addCriterion(String condition) {
if (condition == null) {
throw new RuntimeException("Value for condition cannot be null");
}
criteria.add(new Criterion(condition));
}
protected void addCriterion(String condition, Object value, String property) {
if (value == null) {
throw new RuntimeException("Value for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value));
}
protected void addCriterion(String condition, Object value1, Object value2, String property) {
if (value1 == null || value2 == null) {
throw new RuntimeException("Between values for " + property + " cannot be null");
}
criteria.add(new Criterion(condition, value1, value2));
}
public Criteria andIdIsNull() {
addCriterion("id is null");
return (Criteria) this;
}
public Criteria andIdIsNotNull() {
addCriterion("id is not null");
return (Criteria) this;
}
public Criteria andIdEqualTo(String value) {
addCriterion("id =", value, "id");
return (Criteria) this;
}
public Criteria andIdNotEqualTo(String value) {
addCriterion("id <>", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThan(String value) {
addCriterion("id >", value, "id");
return (Criteria) this;
}
public Criteria andIdGreaterThanOrEqualTo(String value) {
addCriterion("id >=", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThan(String value) {
addCriterion("id <", value, "id");
return (Criteria) this;
}
public Criteria andIdLessThanOrEqualTo(String value) {
addCriterion("id <=", value, "id");
return (Criteria) this;
}
public Criteria andIdLike(String value) {
addCriterion("id like", value, "id");
return (Criteria) this;
}
public Criteria andIdNotLike(String value) {
addCriterion("id not like", value, "id");
return (Criteria) this;
}
public Criteria andIdIn(List<String> values) {
addCriterion("id in", values, "id");
return (Criteria) this;
}
public Criteria andIdNotIn(List<String> values) {
addCriterion("id not in", values, "id");
return (Criteria) this;
}
public Criteria andIdBetween(String value1, String value2) {
addCriterion("id between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andIdNotBetween(String value1, String value2) {
addCriterion("id not between", value1, value2, "id");
return (Criteria) this;
}
public Criteria andTestPlanIdIsNull() {
addCriterion("test_plan_id is null");
return (Criteria) this;
}
public Criteria andTestPlanIdIsNotNull() {
addCriterion("test_plan_id is not null");
return (Criteria) this;
}
public Criteria andTestPlanIdEqualTo(String value) {
addCriterion("test_plan_id =", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotEqualTo(String value) {
addCriterion("test_plan_id <>", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdGreaterThan(String value) {
addCriterion("test_plan_id >", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdGreaterThanOrEqualTo(String value) {
addCriterion("test_plan_id >=", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdLessThan(String value) {
addCriterion("test_plan_id <", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdLessThanOrEqualTo(String value) {
addCriterion("test_plan_id <=", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdLike(String value) {
addCriterion("test_plan_id like", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotLike(String value) {
addCriterion("test_plan_id not like", value, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdIn(List<String> values) {
addCriterion("test_plan_id in", values, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotIn(List<String> values) {
addCriterion("test_plan_id not in", values, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdBetween(String value1, String value2) {
addCriterion("test_plan_id between", value1, value2, "testPlanId");
return (Criteria) this;
}
public Criteria andTestPlanIdNotBetween(String value1, String value2) {
addCriterion("test_plan_id not between", value1, value2, "testPlanId");
return (Criteria) this;
}
public Criteria andLoadCaseIdIsNull() {
addCriterion("load_case_id is null");
return (Criteria) this;
}
public Criteria andLoadCaseIdIsNotNull() {
addCriterion("load_case_id is not null");
return (Criteria) this;
}
public Criteria andLoadCaseIdEqualTo(String value) {
addCriterion("load_case_id =", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdNotEqualTo(String value) {
addCriterion("load_case_id <>", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdGreaterThan(String value) {
addCriterion("load_case_id >", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdGreaterThanOrEqualTo(String value) {
addCriterion("load_case_id >=", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdLessThan(String value) {
addCriterion("load_case_id <", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdLessThanOrEqualTo(String value) {
addCriterion("load_case_id <=", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdLike(String value) {
addCriterion("load_case_id like", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdNotLike(String value) {
addCriterion("load_case_id not like", value, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdIn(List<String> values) {
addCriterion("load_case_id in", values, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdNotIn(List<String> values) {
addCriterion("load_case_id not in", values, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdBetween(String value1, String value2) {
addCriterion("load_case_id between", value1, value2, "loadCaseId");
return (Criteria) this;
}
public Criteria andLoadCaseIdNotBetween(String value1, String value2) {
addCriterion("load_case_id not between", value1, value2, "loadCaseId");
return (Criteria) this;
}
public Criteria andStatusIsNull() {
addCriterion("`status` is null");
return (Criteria) this;
}
public Criteria andStatusIsNotNull() {
addCriterion("`status` is not null");
return (Criteria) this;
}
public Criteria andStatusEqualTo(String value) {
addCriterion("`status` =", value, "status");
return (Criteria) this;
}
public Criteria andStatusNotEqualTo(String value) {
addCriterion("`status` <>", value, "status");
return (Criteria) this;
}
public Criteria andStatusGreaterThan(String value) {
addCriterion("`status` >", value, "status");
return (Criteria) this;
}
public Criteria andStatusGreaterThanOrEqualTo(String value) {
addCriterion("`status` >=", value, "status");
return (Criteria) this;
}
public Criteria andStatusLessThan(String value) {
addCriterion("`status` <", value, "status");
return (Criteria) this;
}
public Criteria andStatusLessThanOrEqualTo(String value) {
addCriterion("`status` <=", value, "status");
return (Criteria) this;
}
public Criteria andStatusLike(String value) {
addCriterion("`status` like", value, "status");
return (Criteria) this;
}
public Criteria andStatusNotLike(String value) {
addCriterion("`status` not like", value, "status");
return (Criteria) this;
}
public Criteria andStatusIn(List<String> values) {
addCriterion("`status` in", values, "status");
return (Criteria) this;
}
public Criteria andStatusNotIn(List<String> values) {
addCriterion("`status` not in", values, "status");
return (Criteria) this;
}
public Criteria andStatusBetween(String value1, String value2) {
addCriterion("`status` between", value1, value2, "status");
return (Criteria) this;
}
public Criteria andStatusNotBetween(String value1, String value2) {
addCriterion("`status` not between", value1, value2, "status");
return (Criteria) this;
}
public Criteria andCreateTimeIsNull() {
addCriterion("create_time is null");
return (Criteria) this;
}
public Criteria andCreateTimeIsNotNull() {
addCriterion("create_time is not null");
return (Criteria) this;
}
public Criteria andCreateTimeEqualTo(Long value) {
addCriterion("create_time =", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotEqualTo(Long value) {
addCriterion("create_time <>", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThan(Long value) {
addCriterion("create_time >", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) {
addCriterion("create_time >=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThan(Long value) {
addCriterion("create_time <", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeLessThanOrEqualTo(Long value) {
addCriterion("create_time <=", value, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeIn(List<Long> values) {
addCriterion("create_time in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotIn(List<Long> values) {
addCriterion("create_time not in", values, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeBetween(Long value1, Long value2) {
addCriterion("create_time between", value1, value2, "createTime");
return (Criteria) this;
}
public Criteria andCreateTimeNotBetween(Long value1, Long value2) {
addCriterion("create_time not between", value1, value2, "createTime");
return (Criteria) this;
}
public Criteria andUpdateTimeIsNull() {
addCriterion("update_time is null");
return (Criteria) this;
}
public Criteria andUpdateTimeIsNotNull() {
addCriterion("update_time is not null");
return (Criteria) this;
}
public Criteria andUpdateTimeEqualTo(Long value) {
addCriterion("update_time =", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotEqualTo(Long value) {
addCriterion("update_time <>", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeGreaterThan(Long value) {
addCriterion("update_time >", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeGreaterThanOrEqualTo(Long value) {
addCriterion("update_time >=", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeLessThan(Long value) {
addCriterion("update_time <", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeLessThanOrEqualTo(Long value) {
addCriterion("update_time <=", value, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeIn(List<Long> values) {
addCriterion("update_time in", values, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotIn(List<Long> values) {
addCriterion("update_time not in", values, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeBetween(Long value1, Long value2) {
addCriterion("update_time between", value1, value2, "updateTime");
return (Criteria) this;
}
public Criteria andUpdateTimeNotBetween(Long value1, Long value2) {
addCriterion("update_time not between", value1, value2, "updateTime");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {
protected Criteria() {
super();
}
}
public static class Criterion {
private String condition;
private Object value;
private Object secondValue;
private boolean noValue;
private boolean singleValue;
private boolean betweenValue;
private boolean listValue;
private String typeHandler;
public String getCondition() {
return condition;
}
public Object getValue() {
return value;
}
public Object getSecondValue() {
return secondValue;
}
public boolean isNoValue() {
return noValue;
}
public boolean isSingleValue() {
return singleValue;
}
public boolean isBetweenValue() {
return betweenValue;
}
public boolean isListValue() {
return listValue;
}
public String getTypeHandler() {
return typeHandler;
}
protected Criterion(String condition) {
super();
this.condition = condition;
this.typeHandler = null;
this.noValue = true;
}
protected Criterion(String condition, Object value, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.typeHandler = typeHandler;
if (value instanceof List<?>) {
this.listValue = true;
} else {
this.singleValue = true;
}
}
protected Criterion(String condition, Object value) {
this(condition, value, null);
}
protected Criterion(String condition, Object value, Object secondValue, String typeHandler) {
super();
this.condition = condition;
this.value = value;
this.secondValue = secondValue;
this.typeHandler = typeHandler;
this.betweenValue = true;
}
protected Criterion(String condition, Object value, Object secondValue) {
this(condition, value, secondValue, null);
}
}
}

View File

@ -0,0 +1,30 @@
package io.metersphere.base.mapper;
import io.metersphere.base.domain.TestPlanLoadCase;
import io.metersphere.base.domain.TestPlanLoadCaseExample;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface TestPlanLoadCaseMapper {
long countByExample(TestPlanLoadCaseExample example);
int deleteByExample(TestPlanLoadCaseExample example);
int deleteByPrimaryKey(String id);
int insert(TestPlanLoadCase record);
int insertSelective(TestPlanLoadCase record);
List<TestPlanLoadCase> selectByExample(TestPlanLoadCaseExample example);
TestPlanLoadCase selectByPrimaryKey(String id);
int updateByExampleSelective(@Param("record") TestPlanLoadCase record, @Param("example") TestPlanLoadCaseExample example);
int updateByExample(@Param("record") TestPlanLoadCase record, @Param("example") TestPlanLoadCaseExample example);
int updateByPrimaryKeySelective(TestPlanLoadCase record);
int updateByPrimaryKey(TestPlanLoadCase record);
}

View File

@ -0,0 +1,228 @@
<?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.TestPlanLoadCaseMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.TestPlanLoadCase">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="test_plan_id" jdbcType="VARCHAR" property="testPlanId" />
<result column="load_case_id" jdbcType="VARCHAR" property="loadCaseId" />
<result column="status" jdbcType="VARCHAR" property="status" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="update_time" jdbcType="BIGINT" property="updateTime" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Update_By_Example_Where_Clause">
<where>
<foreach collection="example.oredCriteria" item="criteria" separator="or">
<if test="criteria.valid">
<trim prefix="(" prefixOverrides="and" suffix=")">
<foreach collection="criteria.criteria" item="criterion">
<choose>
<when test="criterion.noValue">
and ${criterion.condition}
</when>
<when test="criterion.singleValue">
and ${criterion.condition} #{criterion.value}
</when>
<when test="criterion.betweenValue">
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when test="criterion.listValue">
and ${criterion.condition}
<foreach close=")" collection="criterion.value" item="listItem" open="(" separator=",">
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql id="Base_Column_List">
id, test_plan_id, load_case_id, `status`, create_time, update_time
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.TestPlanLoadCaseExample" resultMap="BaseResultMap">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
from test_plan_load_case
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
<if test="orderByClause != null">
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from test_plan_load_case
where id = #{id,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
delete from test_plan_load_case
where id = #{id,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.base.domain.TestPlanLoadCaseExample">
delete from test_plan_load_case
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.TestPlanLoadCase">
insert into test_plan_load_case (id, test_plan_id, load_case_id,
`status`, create_time, update_time
)
values (#{id,jdbcType=VARCHAR}, #{testPlanId,jdbcType=VARCHAR}, #{loadCaseId,jdbcType=VARCHAR},
#{status,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestPlanLoadCase">
insert into test_plan_load_case
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="testPlanId != null">
test_plan_id,
</if>
<if test="loadCaseId != null">
load_case_id,
</if>
<if test="status != null">
`status`,
</if>
<if test="createTime != null">
create_time,
</if>
<if test="updateTime != null">
update_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="testPlanId != null">
#{testPlanId,jdbcType=VARCHAR},
</if>
<if test="loadCaseId != null">
#{loadCaseId,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
#{createTime,jdbcType=BIGINT},
</if>
<if test="updateTime != null">
#{updateTime,jdbcType=BIGINT},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.base.domain.TestPlanLoadCaseExample" resultType="java.lang.Long">
select count(*) from test_plan_load_case
<if test="_parameter != null">
<include refid="Example_Where_Clause" />
</if>
</select>
<update id="updateByExampleSelective" parameterType="map">
update test_plan_load_case
<set>
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.testPlanId != null">
test_plan_id = #{record.testPlanId,jdbcType=VARCHAR},
</if>
<if test="record.loadCaseId != null">
load_case_id = #{record.loadCaseId,jdbcType=VARCHAR},
</if>
<if test="record.status != null">
`status` = #{record.status,jdbcType=VARCHAR},
</if>
<if test="record.createTime != null">
create_time = #{record.createTime,jdbcType=BIGINT},
</if>
<if test="record.updateTime != null">
update_time = #{record.updateTime,jdbcType=BIGINT},
</if>
</set>
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update test_plan_load_case
set id = #{record.id,jdbcType=VARCHAR},
test_plan_id = #{record.testPlanId,jdbcType=VARCHAR},
load_case_id = #{record.loadCaseId,jdbcType=VARCHAR},
`status` = #{record.status,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
update_time = #{record.updateTime,jdbcType=BIGINT}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.TestPlanLoadCase">
update test_plan_load_case
<set>
<if test="testPlanId != null">
test_plan_id = #{testPlanId,jdbcType=VARCHAR},
</if>
<if test="loadCaseId != null">
load_case_id = #{loadCaseId,jdbcType=VARCHAR},
</if>
<if test="status != null">
`status` = #{status,jdbcType=VARCHAR},
</if>
<if test="createTime != null">
create_time = #{createTime,jdbcType=BIGINT},
</if>
<if test="updateTime != null">
update_time = #{updateTime,jdbcType=BIGINT},
</if>
</set>
where id = #{id,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.TestPlanLoadCase">
update test_plan_load_case
set test_plan_id = #{testPlanId,jdbcType=VARCHAR},
load_case_id = #{loadCaseId,jdbcType=VARCHAR},
`status` = #{status,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
update_time = #{updateTime,jdbcType=BIGINT}
where id = #{id,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -0,0 +1,12 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtTestPlanLoadCaseMapper {
List<String> selectIdsNotInPlan(@Param("projectId") String projectId, @Param("planId") String planId);
List<TestPlanLoadCaseDTO> selectTestPlanLoadCaseList(@Param("planId") String planId);
}

View File

@ -0,0 +1,21 @@
<?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.ExtTestPlanLoadCaseMapper">
<select id="selectIdsNotInPlan" resultType="java.lang.String">
select load_test.id
from load_test
where load_test.project_id = #{projectId}
and load_test.id not in (
select tplc.load_case_id from test_plan_load_case tplc where tplc.test_plan_id = #{planId}
)
</select>
<select id="selectTestPlanLoadCaseList" resultType="io.metersphere.track.dto.TestPlanLoadCaseDTO">
select tplc.id, u.name as userName, tplc.create_time, tplc.update_time, tplc.test_plan_id, tplc.load_case_id,
lt.status, lt.name as caseName
from test_plan_load_case tplc
inner join load_test lt on tplc.load_case_id = lt.id
inner join user u on lt.user_id = u.id
where tplc.test_plan_id = #{planId}
</select>
</mapper>

View File

@ -1,5 +1,5 @@
package io.metersphere.commons.constants; package io.metersphere.commons.constants;
public enum ScheduleGroup { public enum ScheduleGroup {
API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST API_TEST, PERFORMANCE_TEST, API_SCENARIO_TEST,TEST_PLAN_TEST
} }

View File

@ -2,6 +2,7 @@ package io.metersphere.controller;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.base.domain.Schedule; import io.metersphere.base.domain.Schedule;
import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao; import io.metersphere.dto.ScheduleDao;
@ -16,6 +17,8 @@ import java.util.List;
public class ScheduleController { public class ScheduleController {
@Resource @Resource
private ScheduleService scheduleService; private ScheduleService scheduleService;
@Resource
private ApiAutomationService apiAutomationService;
@PostMapping("/list/{goPage}/{pageSize}") @PostMapping("/list/{goPage}/{pageSize}")
public List<ScheduleDao> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) { public List<ScheduleDao> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryScheduleRequest request) {
@ -28,4 +31,19 @@ public class ScheduleController {
Schedule schedule = scheduleService.getScheduleByResource(testId,group); Schedule schedule = scheduleService.getScheduleByResource(testId,group);
return schedule; return schedule;
} }
@PostMapping(value = "/update")
public void updateSchedule(@RequestBody Schedule request) {
scheduleService.updateSchedule(request);
}
@PostMapping(value = "/create")
public void createSchedule(@RequestBody Schedule request) {
scheduleService.createSchedule(request);
}
@GetMapping(value = "/getTaskInfo")
public Object getTaskInfo() {
return scheduleService.getCurrentlyExecutingJobs();
}
} }

View File

@ -7,7 +7,9 @@ import org.quartz.*;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
@Component @Component
@ -296,4 +298,21 @@ public class ScheduleManager {
return jobDataMap; return jobDataMap;
} }
public Object getCurrentlyExecutingJobs(){
Map<String, String> returnMap = new HashMap<>();
try {
List<JobExecutionContext> currentJobs = scheduler.getCurrentlyExecutingJobs();
for (JobExecutionContext jobCtx : currentJobs) {
String jobName = jobCtx.getJobDetail().getKey().getName();
String groupName = jobCtx.getJobDetail().getJobClass().getName();
returnMap.put("jobName", jobName);
returnMap.put("groupName", groupName);
}
}catch (Exception e){
e.printStackTrace();
}
return returnMap;
}
} }

View File

@ -0,0 +1,130 @@
package io.metersphere.job.sechedule;
import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.base.domain.TestPlanApiScenario;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ReportTriggerMode;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.track.request.testplan.RunTestPlanRequest;
import io.metersphere.track.service.TestPlanApiCaseService;
import io.metersphere.track.service.TestPlanScenarioCaseService;
import org.quartz.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 情景测试Job
* @author song.tianyang
* @Date 2020/12/22 2:59 下午
* @Description
*/
public class TestPlanTestJob extends MsScheduleJob {
private String projectID;
private List<String> scenarioIds;
private List<String> apiTestCaseIds;
private List<String> performanceIds;
private ApiAutomationService apiAutomationService;
private PerformanceTestService performanceTestService;
private TestPlanScenarioCaseService testPlanScenarioCaseService;
private TestPlanApiCaseService testPlanApiCaseService;
private ApiTestCaseService apiTestCaseService;
private ApiDefinitionService apiDefinitionService;
public TestPlanTestJob() {
this.apiAutomationService = CommonBeanFactory.getBean(ApiAutomationService.class);
this.performanceTestService = CommonBeanFactory.getBean(PerformanceTestService.class);
this.testPlanScenarioCaseService = CommonBeanFactory.getBean(TestPlanScenarioCaseService.class);
this.testPlanApiCaseService = CommonBeanFactory.getBean(TestPlanApiCaseService.class);
this.apiTestCaseService = CommonBeanFactory.getBean(ApiTestCaseService.class);
apiDefinitionService = CommonBeanFactory.getBean(ApiDefinitionService.class);
}
/**
* 情景部分的准备工作
* @param context
* @throws JobExecutionException
*/
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobKey jobKey = context.getTrigger().getJobKey();
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
this.userId = jobDataMap.getString("userId");
this.expression = jobDataMap.getString("expression");
this.projectID = jobDataMap.getString("projectId");
scenarioIds = new ArrayList<>();
String testPlanID = jobDataMap.getString("resourceId");
List<TestPlanApiScenario> testPlanApiScenarioList = testPlanScenarioCaseService.getCasesByPlanId(testPlanID);
for (TestPlanApiScenario model:
testPlanApiScenarioList) {
scenarioIds.add(model.getApiScenarioId());
}
List<TestPlanApiCase> testPlanApiCaseList = testPlanApiCaseService.getCasesByPlanId(testPlanID);
for (TestPlanApiCase model :
testPlanApiCaseList) {
apiTestCaseIds.add(model.getApiCaseId());
}
businessExecute(context);
}
@Override
void businessExecute(JobExecutionContext context) {
LogUtil.info("-------------- start testplan schedule ----------");
//执行接口案例任务
for (String apiCaseID:apiTestCaseIds) {
ApiTestCaseWithBLOBs blobs = apiTestCaseService.get(apiCaseID);
String caseReportID = UUID.randomUUID().toString();
RunDefinitionRequest apiCaseReqeust = new RunDefinitionRequest();
apiCaseReqeust.setId(apiCaseID);
apiCaseReqeust.setReportId(caseReportID);
apiCaseReqeust.setProjectId(projectID);
apiCaseReqeust.setExecuteType(ExecuteType.Saved.name());
apiCaseReqeust.setType(ApiRunMode.API_PLAN.name());
apiDefinitionService.run(apiCaseReqeust,blobs);
}
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------");
//执行场景执行任务
RunScenarioRequest scenarioRequest = new RunScenarioRequest();
String senarionReportID = UUID.randomUUID().toString();
scenarioRequest.setId(senarionReportID);
scenarioRequest.setReportId(senarionReportID);
scenarioRequest.setProjectId(projectID);
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
scenarioRequest.setExecuteType(ExecuteType.Saved.name());
scenarioRequest.setScenarioIds(this.scenarioIds);
scenarioRequest.setReportUserID(this.userId);
scenarioRequest.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
String reportID = apiAutomationService.run(scenarioRequest);
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
//执行性能测试任务 --- 保留待功能实现后再继续
// RunTestPlanRequest performanceRequest = new RunTestPlanRequest();
// performanceRequest.setId(resourceId);
// performanceRequest.setUserId(userId);
// performanceRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
// performanceTestService.run(performanceRequest);
}
public static JobKey getJobKey(String testId) {
return new JobKey(testId, ScheduleGroup.TEST_PLAN_TEST.name());
}
public static TriggerKey getTriggerKey(String testId) {
return new TriggerKey(testId, ScheduleGroup.TEST_PLAN_TEST.name());
}
}

View File

@ -481,4 +481,14 @@ public class PerformanceTestService {
quotaService.checkLoadTestQuota(request, create); quotaService.checkLoadTestQuota(request, create);
} }
} }
public List<LoadTest> getLoadTestListByIds(List<String> ids) {
if (CollectionUtils.isEmpty(ids)) {
return new ArrayList<>();
}
LoadTestExample loadTestExample = new LoadTestExample();
loadTestExample.createCriteria().andIdIn(ids);
List<LoadTest> loadTests = loadTestMapper.selectByExample(loadTestExample);
return Optional.ofNullable(loadTests).orElse(new ArrayList<>());
}
} }

View File

@ -9,6 +9,8 @@ import io.metersphere.base.domain.UserExample;
import io.metersphere.base.mapper.ScheduleMapper; import io.metersphere.base.mapper.ScheduleMapper;
import io.metersphere.base.mapper.UserMapper; import io.metersphere.base.mapper.UserMapper;
import io.metersphere.base.mapper.ext.ExtScheduleMapper; import io.metersphere.base.mapper.ext.ExtScheduleMapper;
import io.metersphere.commons.constants.ScheduleGroup;
import io.metersphere.commons.constants.ScheduleType;
import io.metersphere.commons.exception.MSException; import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.DateUtils; import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.LogUtil; import io.metersphere.commons.utils.LogUtil;
@ -17,8 +19,10 @@ import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.controller.request.OrderRequest; import io.metersphere.controller.request.OrderRequest;
import io.metersphere.controller.request.QueryScheduleRequest; import io.metersphere.controller.request.QueryScheduleRequest;
import io.metersphere.dto.ScheduleDao; import io.metersphere.dto.ScheduleDao;
import io.metersphere.job.sechedule.ApiScenarioTestJob;
import io.metersphere.job.sechedule.ApiTestJob; import io.metersphere.job.sechedule.ApiTestJob;
import io.metersphere.job.sechedule.ScheduleManager; import io.metersphere.job.sechedule.ScheduleManager;
import io.metersphere.job.sechedule.TestPlanTestJob;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.quartz.JobKey; import org.quartz.JobKey;
import org.quartz.SchedulerException; import org.quartz.SchedulerException;
@ -188,4 +192,54 @@ public class ScheduleService {
List<TaskInfoResult> runningTaskInfoList = extScheduleMapper.findRunningTaskInfoByProjectID(projectID); List<TaskInfoResult> runningTaskInfoList = extScheduleMapper.findRunningTaskInfoByProjectID(projectID);
return runningTaskInfoList; return runningTaskInfoList;
} }
public void createSchedule(Schedule request) {
Schedule schedule = this.buildApiTestSchedule(request);
schedule.setJob(ApiScenarioTestJob.class.getName());
JobKey jobKey = null;
TriggerKey triggerKey = null;
Class clazz = null;
if("testPlan".equals(request.getScheduleFrom())){
schedule.setGroup(ScheduleGroup.TEST_PLAN_TEST.name());
schedule.setType(ScheduleType.CRON.name());
jobKey = TestPlanTestJob.getJobKey(request.getResourceId());
triggerKey = TestPlanTestJob.getTriggerKey(request.getResourceId());
clazz = TestPlanTestJob.class;
}else {
//默认为情景
schedule.setGroup(ScheduleGroup.API_SCENARIO_TEST.name());
schedule.setType(ScheduleType.CRON.name());
jobKey = ApiScenarioTestJob.getJobKey(request.getResourceId());
triggerKey = ApiScenarioTestJob.getTriggerKey(request.getResourceId());
clazz = ApiScenarioTestJob.class;
}
this.addSchedule(schedule);
this.addOrUpdateCronJob(request,jobKey ,triggerKey , clazz);
}
public void updateSchedule(Schedule request) {
this.editSchedule(request);
JobKey jobKey = null;
TriggerKey triggerKey = null;
Class clazz = null;
if(ScheduleGroup.TEST_PLAN_TEST.name().equals(request.getGroup())){
jobKey = TestPlanTestJob.getJobKey(request.getResourceId());
triggerKey = TestPlanTestJob.getTriggerKey(request.getResourceId());
clazz = TestPlanTestJob.class;
}else {
//默认为情景
jobKey = ApiScenarioTestJob.getJobKey(request.getResourceId());
triggerKey = ApiScenarioTestJob.getTriggerKey(request.getResourceId());
clazz = ApiScenarioTestJob.class;
}
this.addOrUpdateCronJob(request,jobKey ,triggerKey , clazz);
}
public Object getCurrentlyExecutingJobs() {
return scheduleManager.getCurrentlyExecutingJobs();
}
} }

View File

@ -0,0 +1,43 @@
package io.metersphere.track.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.base.domain.LoadTest;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import io.metersphere.track.request.testplan.LoadCaseRequest;
import io.metersphere.track.service.TestPlanLoadCaseService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RequestMapping("/test/plan/load/case")
@RestController
public class TestPlanLoadCaseController {
@Resource
private TestPlanLoadCaseService testPlanLoadCaseService;
@PostMapping("/relevance/list/{goPage}/{pageSize}")
public Pager<List<LoadTest>> relevanceList(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody LoadCaseRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, testPlanLoadCaseService.relevanceList(request));
}
@PostMapping("/relevance")
public void relevanceCase(@RequestBody LoadCaseRequest request) {
testPlanLoadCaseService.relevanceCase(request);
}
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<TestPlanLoadCaseDTO>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody LoadCaseRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
return PageUtils.setPageInfo(page, testPlanLoadCaseService.list(request));
}
@GetMapping("/delete/{id}")
public void delete(@PathVariable String id) {
testPlanLoadCaseService.delete(id);
}
}

View File

@ -0,0 +1,12 @@
package io.metersphere.track.dto;
import io.metersphere.base.domain.TestPlanLoadCase;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class TestPlanLoadCaseDTO extends TestPlanLoadCase {
private String userName;
private String caseName;
}

View File

@ -0,0 +1,14 @@
package io.metersphere.track.request.testplan;
import io.metersphere.base.domain.TestPlanLoadCase;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class LoadCaseRequest extends TestPlanLoadCase {
private String projectId;
private List<String> caseIds;
}

View File

@ -1,23 +1,34 @@
package io.metersphere.track.service; package io.metersphere.track.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest; import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO; import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.dto.definition.request.MsThreadGroup;
import io.metersphere.api.service.ApiDefinitionExecResultService; import io.metersphere.api.service.ApiDefinitionExecResultService;
import io.metersphere.api.service.ApiDefinitionService;
import io.metersphere.api.service.ApiTestCaseService; import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlanApiCase; import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.base.domain.TestPlanApiCaseExample; import io.metersphere.base.domain.TestPlanApiCaseExample;
import io.metersphere.base.mapper.TestPlanApiCaseMapper; import io.metersphere.base.mapper.TestPlanApiCaseMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper; import io.metersphere.base.mapper.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.commons.utils.ServiceUtils; import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest; import io.metersphere.track.request.testcase.TestPlanApiCaseBatchRequest;
import org.apache.jmeter.testelement.TestElement;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
@Service @Service
@ -30,6 +41,8 @@ public class TestPlanApiCaseService {
ApiTestCaseService apiTestCaseService; ApiTestCaseService apiTestCaseService;
@Resource @Resource
ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper; ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
@Resource
ApiDefinitionService apiDefinitionService;
@Lazy @Lazy
@Resource @Resource
ApiDefinitionExecResultService apiDefinitionExecResultService; ApiDefinitionExecResultService apiDefinitionExecResultService;

View File

@ -0,0 +1,71 @@
package io.metersphere.track.service;
import io.metersphere.base.domain.LoadTest;
import io.metersphere.base.domain.TestPlanLoadCase;
import io.metersphere.base.domain.TestPlanLoadCaseExample;
import io.metersphere.base.mapper.TestPlanLoadCaseMapper;
import io.metersphere.base.mapper.ext.ExtTestPlanLoadCaseMapper;
import io.metersphere.performance.service.PerformanceTestService;
import io.metersphere.track.dto.TestPlanLoadCaseDTO;
import io.metersphere.track.request.testplan.LoadCaseRequest;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanLoadCaseService {
@Resource
private TestPlanLoadCaseMapper testPlanLoadCaseMapper;
@Resource
private ExtTestPlanLoadCaseMapper extTestPlanLoadCaseMapper;
@Resource
private PerformanceTestService performanceTestService;
@Resource
private SqlSessionFactory sqlSessionFactory;
public List<LoadTest> relevanceList(LoadCaseRequest request) {
List<String> ids = extTestPlanLoadCaseMapper.selectIdsNotInPlan(request.getProjectId(), request.getTestPlanId());
if (CollectionUtils.isEmpty(ids)) {
return new ArrayList<>();
}
return performanceTestService.getLoadTestListByIds(ids);
}
public List<TestPlanLoadCaseDTO> list(LoadCaseRequest request) {
return extTestPlanLoadCaseMapper.selectTestPlanLoadCaseList(request.getTestPlanId());
}
public void relevanceCase(LoadCaseRequest request) {
List<String> caseIds = request.getCaseIds();
String planId = request.getTestPlanId();
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
TestPlanLoadCaseMapper testPlanLoadCaseMapper = sqlSession.getMapper(TestPlanLoadCaseMapper.class);
caseIds.forEach(id -> {
TestPlanLoadCase t = new TestPlanLoadCase();
t.setId(UUID.randomUUID().toString());
t.setTestPlanId(planId);
t.setLoadCaseId(id);
t.setCreateTime(System.currentTimeMillis());
t.setUpdateTime(System.currentTimeMillis());
testPlanLoadCaseMapper.insert(t);
});
sqlSession.flushStatements();
}
public void delete(String id) {
TestPlanLoadCaseExample testPlanLoadCaseExample = new TestPlanLoadCaseExample();
testPlanLoadCaseExample.createCriteria().andIdEqualTo(id);
testPlanLoadCaseMapper.deleteByExample(testPlanLoadCaseExample);
}
}

View File

@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS `test_plan_load_case`
(
`id` varchar(50) NOT NULL COMMENT 'ID',
`test_plan_id` varchar(50) NOT NULL COMMENT 'Test plan ID',
`load_case_id` varchar(50) NOT NULL COMMENT 'Load test case ID',
`status` varchar(50) DEFAULT NULL COMMENT 'Load case status',
`create_time` bigint(13) NOT NULL COMMENT 'Create timestamp',
`update_time` bigint(13) NOT NULL COMMENT 'Update timestamp',
PRIMARY KEY (`id`),
UNIQUE KEY `plan_load_case_id` (`test_plan_id`, `load_case_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4;

View File

@ -30,7 +30,7 @@
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('schedule.task_notification')" name="second"> <el-tab-pane :label="$t('schedule.task_notification')" name="second">
<ms-schedule-notification :is-tester-permission="isTesterPermission" :test-id="testId" <ms-schedule-notification :is-tester-permission="isTesterPermission" :test-id="testId"
:schedule-receiver-options="scheduleReceiverOptions"/> :schedule-receiver-options="scheduleReceiverOptions"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</div> </div>
@ -55,7 +55,7 @@ const noticeTemplate = requireComponent.keys().length > 0 ? requireComponent("./
export default { export default {
name: "MsScheduleMaintain", name: "MsScheduleMaintain",
components: {CrontabResult, Crontab, MsScheduleNotification,"NoticeTemplate": noticeTemplate.default}, components: {CrontabResult, Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default},
props: { props: {
customValidate: { customValidate: {
@ -81,8 +81,7 @@ export default {
callback(new Error(this.$t('commons.input_content'))); callback(new Error(this.$t('commons.input_content')));
} else if (!cronValidate(cronValue)) { } else if (!cronValidate(cronValue)) {
callback(new Error(this.$t('schedule.cron_expression_format_error'))); callback(new Error(this.$t('schedule.cron_expression_format_error')));
} } else if (!customValidate.pass) {
else if (!customValidate.pass) {
callback(new Error(customValidate.info)); callback(new Error(customValidate.info));
} else { } else {
callback(); callback();
@ -93,9 +92,10 @@ export default {
operation: true, operation: true,
dialogVisible: false, dialogVisible: false,
schedule: { schedule: {
value : "", value: "",
}, },
testId:String, scheduleTaskType: "",
testId: String,
showCron: false, showCron: false,
form: { form: {
cronValue: "" cronValue: ""
@ -130,20 +130,30 @@ export default {
return param; return param;
}, },
open(row) { open(row) {
this.testId = row.id; //
this.findSchedule(row.id); let paramTestId = "";
if (row.redirectFrom == 'testPlan') {
paramTestId = row.id;
this.scheduleTaskType = "TEST_PLAN_TEST";
} else {
paramTestId = row.id;
this.scheduleTaskType = "API_SCENARIO_TEST";
}
this.testId = paramTestId;
this.findSchedule(paramTestId);
this.initUserList(); this.initUserList();
this.dialogVisible = true; this.dialogVisible = true;
this.form.cronValue = this.schedule.value; this.form.cronValue = this.schedule.value;
listenGoBack(this.close); listenGoBack(this.close);
this.activeName = 'first' this.activeName = 'first'
}, },
findSchedule(){ findSchedule() {
var scenarioID = this.testId; var scheduleResourceID = this.testId;
this.result = this.$get("/schedule/findOne/"+scenarioID+"/API_SCENARIO_TEST", response => { var taskType = this.scheduleTaskType;
if(response.data!=null){ this.result = this.$get("/schedule/findOne/" + scheduleResourceID + "/" +taskType, response => {
if (response.data != null) {
this.schedule = response.data; this.schedule = response.data;
}else { } else {
this.schedule = {}; this.schedule = {};
} }
}); });
@ -177,9 +187,20 @@ export default {
param = this.schedule; param = this.schedule;
param.resourceId = this.testId; param.resourceId = this.testId;
let url = '/api/automation/schedule/create'; let url = '/api/automation/schedule/create';
if (param.id) { if(this.scheduleTaskType === "TEST_PLAN_TEST"){
url = '/api/automation/schedule/update'; param.scheduleFrom = "testPlan";
//
url = '/schedule/create';
if (param.id) {
url = '/schedule/update';
}
}else {
param.scheduleFrom = "scenario";
if (param.id) {
url = '/api/automation/schedule/update';
}
} }
this.$post(url, param, () => { this.$post(url, param, () => {
this.$success(this.$t('commons.save_success')); this.$success(this.$t('commons.save_success'));
}); });

View File

@ -17,7 +17,9 @@ export default class JDBCSampler extends Sampler {
this.type = "JDBCSampler"; this.type = "JDBCSampler";
this.hashTree = []; this.hashTree = [];
this.variables = []; this.variables = [];
this.environmentId = undefined;
this.dataSource = undefined; this.dataSource = undefined;
this.dataSourceId = undefined;
this.query = undefined; this.query = undefined;
this.queryType = undefined; this.queryType = undefined;
this.queryArguments = undefined; this.queryArguments = undefined;

View File

@ -247,6 +247,7 @@
}, },
environmentChange(value) { environmentChange(value) {
this.request.dataSource = undefined; this.request.dataSource = undefined;
this.request.dataSourceId = "";
for (let i in this.environments) { for (let i in this.environments) {
if (this.environments[i].id === value) { if (this.environments[i].id === value) {
this.databaseConfigsOptions = []; this.databaseConfigsOptions = [];

View File

@ -4,7 +4,7 @@
<ms-table-operator-button :isTesterPermission="isTesterPermission" :tip="tip1" icon="el-icon-edit" @exec="editClick" @click.stop="editClickStop"/> <ms-table-operator-button :isTesterPermission="isTesterPermission" :tip="tip1" icon="el-icon-edit" @exec="editClick" @click.stop="editClickStop"/>
<slot name="middle"></slot> <slot name="middle"></slot>
<ms-table-operator-button :isTesterPermission="isTesterPermission" :tip="tip2" icon="el-icon-delete" type="danger" @exec="deleteClick" @click.stop="deleteClickStop"/> <ms-table-operator-button :isTesterPermission="isTesterPermission" :tip="tip2" icon="el-icon-delete" type="danger" @exec="deleteClick" @click.stop="deleteClickStop"/>
<slot name="behind"></slot> <slot name="beheind"></slot>
</span> </span>
</template> </template>

View File

@ -128,19 +128,21 @@
@exec="openReport(scope.row.id, scope.row.reportId)"/> @exec="openReport(scope.row.id, scope.row.reportId)"/>
</template> </template>
</ms-table-operator> </ms-table-operator>
<ms-table-operator-button style="margin-left: 10px;color:#6C317C" type=""
:tip="$t('test_track.plan_view.view_report')" icon="el-icon-time"
@exec="scheduleTask(scope.row)"/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize" <ms-table-pagination :change="initTableData" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/> :total="total"/>
<test-report-template-list @openReport="openReport" ref="testReportTemplateList"/> <test-report-template-list @openReport="openReport" ref="testReportTemplateList"/>
<test-case-report-view @refresh="initTableData" ref="testCaseReportView"/> <test-case-report-view @refresh="initTableData" ref="testCaseReportView"/>
<ms-delete-confirm :title="$t('test_track.plan.plan_delete')" @delete="_handleDelete" ref="deleteConfirm" :with-tip="enableDeleteTip"> <ms-delete-confirm :title="$t('test_track.plan.plan_delete')" @delete="_handleDelete" ref="deleteConfirm" :with-tip="enableDeleteTip">
{{$t('test_track.plan.plan_delete_tip')}} {{$t('test_track.plan.plan_delete_tip')}}
</ms-delete-confirm> </ms-delete-confirm>
<ms-schedule-maintain ref="scheduleMaintain" />
</el-card> </el-card>
</template> </template>
@ -160,6 +162,7 @@ import MsDeleteConfirm from "../../../common/components/MsDeleteConfirm";
import {TEST_PLAN_CONFIGS} from "../../../common/components/search/search-components"; import {TEST_PLAN_CONFIGS} from "../../../common/components/search/search-components";
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent"; import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
import {getCurrentProjectID} from "../../../../../common/js/utils"; import {getCurrentProjectID} from "../../../../../common/js/utils";
import MsScheduleMaintain from "@/business/components/api/automation/schedule/ScheduleMaintain"
export default { export default {
name: "TestPlanList", name: "TestPlanList",
@ -169,6 +172,7 @@ export default {
TestReportTemplateList, TestReportTemplateList,
PlanStageTableItem, PlanStageTableItem,
PlanStatusTableItem, PlanStatusTableItem,
MsScheduleMaintain,
MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination MsTableOperator, MsTableOperatorButton, MsDialogFooter, MsTableHeader, MsCreateBox, MsTablePagination
}, },
data() { data() {
@ -289,6 +293,10 @@ export default {
this.$refs.testCaseReportView.open(planId, reportId); this.$refs.testCaseReportView.open(planId, reportId);
} }
}, },
scheduleTask(row){
row.redirectFrom = "testPlan";
this.$refs.scheduleMaintain.open(row);
},
} }
} }
</script> </script>

View File

@ -14,12 +14,14 @@
class="el-menu-demo header-menu" mode="horizontal" @select="handleSelect"> class="el-menu-demo header-menu" mode="horizontal" @select="handleSelect">
<el-menu-item index="functional">功能测试用例</el-menu-item> <el-menu-item index="functional">功能测试用例</el-menu-item>
<el-menu-item index="api">接口测试用例</el-menu-item> <el-menu-item index="api">接口测试用例</el-menu-item>
<el-menu-item index="load">性能测试用例</el-menu-item>
<el-menu-item index="report">报告统计</el-menu-item> <el-menu-item index="report">报告统计</el-menu-item>
</el-menu> </el-menu>
</template> </template>
</ms-test-plan-header-bar> </ms-test-plan-header-bar>
<test-plan-functional v-if="activeIndex === 'functional'" :plan-id="planId"/> <test-plan-functional v-if="activeIndex === 'functional'" :plan-id="planId"/>
<test-plan-api v-if="activeIndex === 'api'" :plan-id="planId"/> <test-plan-api v-if="activeIndex === 'api'" :plan-id="planId"/>
<test-plan-load v-if="activeIndex === 'load'" :plan-id="planId"/>
<test-case-statistics-report-view :test-plan="currentPlan" v-if="activeIndex === 'report'"/> <test-case-statistics-report-view :test-plan="currentPlan" v-if="activeIndex === 'report'"/>
<test-report-template-list @openReport="openReport" ref="testReportTemplateList"/> <test-report-template-list @openReport="openReport" ref="testReportTemplateList"/>
@ -42,6 +44,7 @@
import TestPlanApi from "./comonents/api/TestPlanApi"; import TestPlanApi from "./comonents/api/TestPlanApi";
import TestCaseStatisticsReportView from "./comonents/report/statistics/TestCaseStatisticsReportView"; import TestCaseStatisticsReportView from "./comonents/report/statistics/TestCaseStatisticsReportView";
import TestReportTemplateList from "./comonents/TestReportTemplateList"; import TestReportTemplateList from "./comonents/TestReportTemplateList";
import TestPlanLoad from "@/business/components/track/plan/view/comonents/load/TestPlanLoad";
export default { export default {
name: "TestPlanView", name: "TestPlanView",
@ -52,7 +55,7 @@
TestPlanFunctional, TestPlanFunctional,
MsTestPlanHeaderBar, MsTestPlanHeaderBar,
MsMainContainer, MsMainContainer,
MsAsideContainer, MsContainer, NodeTree, TestPlanTestCaseList, TestCaseRelevance, SelectMenu}, MsAsideContainer, MsContainer, NodeTree, TestPlanTestCaseList, TestCaseRelevance, SelectMenu, TestPlanLoad},
data() { data() {
return { return {
testPlans: [], testPlans: [],

View File

@ -0,0 +1,241 @@
<template>
<test-case-relevance-base
@setProject="setProject"
@save="saveCaseRelevance"
:plan-id="planId"
ref="baseRelevance">
<template v-slot:aside>
<node-tree class="node-tree"
v-loading="result.loading"
@nodeSelectEvent="nodeChange"
:tree-nodes="treeNodes"
ref="nodeTree"/>
</template>
<!-- <ms-table-header :condition.sync="condition" @search="search" title="" :show-create="false"/>-->
<el-table
v-loading="result.loading"
:data="testCases"
row-key="id"
@select-all="handleSelectAll"
@select="handleSelectionChange"
height="50vh"
ref="table">
<el-table-column
type="selection"/>
<el-table-column
prop="name"
:label="$t('commons.name')"
show-overflow-tooltip>
</el-table-column>
<!-- <el-table-column-->
<!-- prop="userName"-->
<!-- :label="$t('load_test.user_name')"-->
<!-- show-overflow-tooltip>-->
<!-- </el-table-column>-->
<el-table-column
prop="status"
column-key="status"
:filters="statusFilters"
:label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-performance-test-status :row="row"/>
</template>
</el-table-column>
<el-table-column
sortable
prop="createTime"
:label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
sortable
prop="updateTime"
:label="$t('commons.update_time')">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="getTestCases" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</test-case-relevance-base>
</template>
<script>
import TestCaseRelevanceBase from "@/business/components/track/plan/view/comonents/base/TestCaseRelevanceBase";
import NodeTree from "@/business/components/track/common/NodeTree";
import PriorityTableItem from "@/business/components/track/common/tableItems/planview/PriorityTableItem";
import TypeTableItem from "@/business/components/track/common/tableItems/planview/TypeTableItem";
import MsTableSearchBar from "@/business/components/common/components/MsTableSearchBar";
import MsTableAdvSearchBar from "@/business/components/common/components/search/MsTableAdvSearchBar";
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
import MsPerformanceTestStatus from "@/business/components/performance/test/PerformanceTestStatus";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
export default {
name: "TestCaseLoadRelevance",
components: {
TestCaseRelevanceBase,
NodeTree,
PriorityTableItem,
TypeTableItem,
MsTableSearchBar,
MsTableAdvSearchBar,
MsTableHeader,
MsPerformanceTestStatus,
MsTablePagination
},
data() {
return {
result: {},
testCases: [],
selectIds: new Set(),
treeNodes: [],
selectNodeIds: [],
selectNodeNames: [],
projectId: '',
projectName: '',
projects: [],
pageSize: 10,
currentPage: 1,
total: 0,
condition: {},
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
]
};
},
props: {
planId: {
type: String
}
},
watch: {
planId() {
this.condition.planId = this.planId;
},
selectNodeIds() {
this.search();
},
projectId() {
this.condition.projectId = this.projectId;
this.getProjectNode();
this.search();
}
},
methods: {
open() {
this.$refs.baseRelevance.open();
},
setProject(projectId) {
this.projectId = projectId;
},
saveCaseRelevance() {
let param = {};
param.testPlanId = this.planId;
param.caseIds = [...this.selectIds];
this.result = this.$post('/test/plan/load/case/relevance', param, () => {
this.selectIds.clear();
this.$success(this.$t('commons.save_success'));
this.$refs.baseRelevance.close();
this.$emit('refresh');
});
},
buildPagePath(path) {
return path + "/" + this.currentPage + "/" + this.pageSize;
},
search() {
this.currentPage = 1;
this.testCases = [];
this.getTestCases(true);
},
getTestCases() {
if (this.planId) {
this.condition.testPlanId = this.planId;
}
if (this.projectId) {
this.condition.projectId = this.projectId;
this.result = this.$post(this.buildPagePath('/test/plan/load/case/relevance/list'), this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.testCases = data.listObject;
});
}
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.testCases.forEach(item => {
this.selectIds.add(item.id);
});
} else {
this.testCases.forEach(item => {
if (this.selectIds.has(item.id)) {
this.selectIds.delete(item.id);
}
});
}
},
handleSelectionChange(selection, row) {
if (this.selectIds.has(row.id)) {
this.selectIds.delete(row.id);
} else {
this.selectIds.add(row.id);
}
},
nodeChange(node, nodeIds, nodeNames) {
this.selectNodeIds = nodeIds;
this.selectNodeNames = nodeNames;
},
refresh() {
this.close();
},
getAllNodeTreeByPlanId() {
if (this.planId) {
let param = {
testPlanId: this.planId,
projectId: this.projectId
};
this.result = this.$post("/case/node/list/all/plan", param, response => {
this.treeNodes = response.data;
});
}
},
close() {
this.selectIds.clear();
this.selectNodeIds = [];
this.selectNodeNames = [];
},
getProjectNode(projectId) {
const index = this.projects.findIndex(project => project.id === projectId);
if (index !== -1) {
this.projectName = this.projects[index].name;
}
if (projectId) {
this.projectId = projectId;
}
this.$refs.nodeTree.result = this.$post("/case/node/list/all/plan",
{testPlanId: this.planId, projectId: this.projectId}, response => {
this.treeNodes = response.data;
});
this.selectNodeIds = [];
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,96 @@
<template>
<ms-test-plan-common-component>
<template v-slot:aside>
<node-tree class="node-tree"
v-loading="result.loading"
@nodeSelectEvent="nodeChange"
:tree-nodes="treeNodes"
ref="nodeTree"/>
</template>
<template v-slot:main>
<test-plan-load-case-list
class="table-list"
@refresh="refresh"
:plan-id="planId"
:select-node-ids="selectNodeIds"
:select-parent-nodes="selectParentNodes"
@relevanceCase="openTestCaseRelevanceDialog"
ref="testPlanLoadCaseList"/>
</template>
<test-case-load-relevance
@refresh="refresh"
:plan-id="planId"
ref="testCaseLoadRelevance"/>
</ms-test-plan-common-component>
</template>
<script>
import MsTestPlanCommonComponent from "@/business/components/track/plan/view/comonents/base/TestPlanCommonComponent";
import NodeTree from "@/business/components/track/common/NodeTree";
import TestPlanLoadCaseList from "@/business/components/track/plan/view/comonents/load/TestPlanLoadCaseList";
import TestCaseLoadRelevance from "@/business/components/track/plan/view/comonents/load/TestCaseLoadRelevance";
export default {
name: "TestPlanLoad",
components: {
MsTestPlanCommonComponent,
NodeTree,
TestPlanLoadCaseList,
TestCaseLoadRelevance,
},
data() {
return {
result: {},
selectNodeIds: [],
selectParentNodes: [],
treeNodes: [],
}
},
props: [
'planId'
],
watch: {
planId() {
this.initData();
}
},
mounted() {
this.initData();
},
methods: {
refresh() {
this.selectNodeIds = [];
this.selectParentNodes = [];
this.$refs.testCaseLoadRelevance.search();
this.getNodeTreeByPlanId();
},
initData() {
this.getNodeTreeByPlanId();
},
openTestCaseRelevanceDialog() {
this.$refs.testCaseLoadRelevance.open();
},
nodeChange(node, nodeIds, pNodes) {
this.selectNodeIds = nodeIds;
this.selectParentNodes = pNodes;
// node
this.$refs.testPlanLoadCaseList.currentPage = 1;
this.$refs.testPlanLoadCaseList.pageSize = 10;
},
getNodeTreeByPlanId() {
if (this.planId) {
this.result = this.$get("/case/node/list/plan/" + this.planId, response => {
this.treeNodes = response.data;
});
}
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,220 @@
<template>
<div class="card-container">
<el-card class="card-content" v-loading="result.loading">
<template v-slot:header>
<test-plan-load-case-list-header
:condition="condition"
:plan-id="planId"
@refresh="initTable"
@relevanceCase="$emit('relevanceCase')"
/>
</template>
<el-table v-loading="result.loading"
border
:data="tableData" row-key="id" class="test-content adjust-table"
@select-all="handleSelectAll"
@filter-change="filter"
@sort-change="sort"
@select="handleSelectionChange" :height="screenHeight">
<el-table-column type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore && !isReadOnly" :buttons="buttons" :size="selectRows.size"/>
</template>
</el-table-column>
<!-- <el-table-column prop="num" label="ID" show-overflow-tooltip/>-->
<el-table-column
prop="caseName"
:label="$t('commons.name')"
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>-->
<el-table-column
prop="userName"
:label="$t('load_test.user_name')"
show-overflow-tooltip>
</el-table-column>
<el-table-column
sortable
prop="createTime"
:label="$t('commons.create_time')">
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column
sortable
prop="updateTime"
: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"
column-key="status"
:filters="statusFilters"
:label="$t('commons.status')">
<template v-slot:default="{row}">
<ms-performance-test-status :row="row"/>
</template>
</el-table-column>
<el-table-column v-if="!isReadOnly" :label="$t('commons.operating')" align="center">
<template v-slot:default="scope">
<ms-table-operator-button class="run-button" :is-tester-permission="true" :tip="$t('api_test.run')" icon="el-icon-video-play"
@exec="run(scope.row)" v-tester/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('test_track.plan_view.cancel_relevance')"
icon="el-icon-unlock" type="danger" @exec="handleDelete(scope.row)" v-tester/>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="initTable" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</div>
</template>
<script>
import TestPlanLoadCaseListHeader from "@/business/components/track/plan/view/comonents/load/TestPlanLoadCaseListHeader";
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import {_filter, _sort} from "@/common/js/utils";
import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import MsPerformanceTestStatus from "@/business/components/performance/test/PerformanceTestStatus";
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
export default {
name: "TestPlanLoadCaseList",
components: {
TestPlanLoadCaseListHeader,
ShowMoreBtn,
MsTablePagination,
MsPerformanceTestStatus,
MsTableOperatorButton
},
data() {
return {
condition: {},
result: {},
tableData: [],
selectRows: new Set(),
currentPage: 1,
pageSize: 10,
total: 0,
screenHeight: document.documentElement.clientHeight - 330,//
buttons: [
{
name: "批量编辑用例", handleClick: this.handleBatchEdit
},
{
name: "批量取消关联", handleClick: this.handleDeleteBatch
},
{
name: "批量执行用例", handleClick: this.handleRunBatch
}
],
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'}
],
}
},
props: {
selectNodeIds: Array,
isReadOnly: {
type: Boolean,
default: false
},
planId: String
},
created() {
this.initTable();
},
watch: {
selectNodeIds() {
this.initTable();
},
planId() {
this.initTable();
}
},
methods: {
initTable() {
this.$post("/test/plan/load/case/list/" + this.currentPage + "/" + this.pageSize, {testPlanId: this.planId}, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
})
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.tableData.forEach(item => {
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
} else {
this.selectRows.clear();
this.tableData.forEach(row => {
this.$set(row, "showMore", false);
})
}
},
handleSelectionChange(selection, row) {
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
} else {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
},
handleBatchEdit() {
},
handleDeleteBatch() {
},
handleRunBatch() {
},
run() {
},
handleDelete(loadCase) {
this.$get('/test/plan/load/case/delete/' + loadCase.id, () => {
this.$success(this.$t('test_track.cancel_relevance_success'));
this.$emit('refresh');
this.initTable();
});
},
sort(column) {
//
if (this.condition.orders) {
this.condition.orders = [];
}
_sort(column, this.condition);
this.initTableData();
},
filter(filters) {
_filter(filters, this.condition);
this.initTableData();
},
}
}
</script>
<style scoped>
/deep/ .run-button {
background-color: #409EFF;
border-color: #409EFF;
}
</style>

View File

@ -0,0 +1,32 @@
<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 MsTableButton from "@/business/components/common/components/MsTableButton";
import MsTableHeader from "@/business/components/common/components/MsTableHeader";
export default {
name: "TestPlanLoadCaseListHeader",
components: {MsTableButton, MsTableHeader},
props: ['condition'],
}
</script>
<style scoped>
</style>