Merge remote-tracking branch 'origin/master'
# Conflicts: # frontend/src/business/components/api/definition/components/complete/EditCompleteHTTPApi.vue
This commit is contained in:
commit
2cdf1e6730
|
@ -101,6 +101,12 @@ public class ApiAutomationController {
|
|||
return apiAutomationService.run(request);
|
||||
}
|
||||
|
||||
@PostMapping("/batch/edit")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public void bathEdit(@RequestBody SaveApiScenarioRequest request) {
|
||||
apiAutomationService.bathEdit(request);
|
||||
}
|
||||
|
||||
@PostMapping("/getReference")
|
||||
public ReferenceDTO getReference(@RequestBody ApiScenarioRequest request) {
|
||||
return apiAutomationService.getReference(request);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package io.metersphere.api.dto.automation;
|
||||
|
||||
import io.metersphere.base.domain.ApiScenario;
|
||||
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
|
@ -8,7 +8,7 @@ import java.util.List;
|
|||
|
||||
@Getter
|
||||
@Setter
|
||||
public class ApiScenarioDTO extends ApiScenario {
|
||||
public class ApiScenarioDTO extends ApiScenarioWithBLOBs {
|
||||
|
||||
private String projectName;
|
||||
private String userName;
|
||||
|
|
|
@ -27,4 +27,5 @@ public class ApiScenarioRequest {
|
|||
private boolean isSelectThisWeedData;
|
||||
private long createTime = 0;
|
||||
private String executeStatus;
|
||||
private boolean notInTestPlan;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
|
@ -32,6 +33,8 @@ public class RunScenarioRequest {
|
|||
|
||||
private List<String> scenarioIds;
|
||||
|
||||
private Map<String,String> scenarioTestPlanIdMap;
|
||||
|
||||
/**
|
||||
* isSelectAllDate:选择的数据是否是全部数据(全部数据是不受分页影响的数据)
|
||||
* filters: 数据状态
|
||||
|
|
|
@ -19,6 +19,8 @@ public class SaveApiScenarioRequest {
|
|||
|
||||
private String apiScenarioModuleId;
|
||||
|
||||
private String environmentId;
|
||||
|
||||
private String modulePath;
|
||||
|
||||
private String name;
|
||||
|
@ -40,4 +42,15 @@ public class SaveApiScenarioRequest {
|
|||
private MsTestElement scenarioDefinition;
|
||||
|
||||
List<String> bodyUploadIds;
|
||||
|
||||
private List<String> scenarioIds;
|
||||
|
||||
private boolean isSelectAllDate;
|
||||
|
||||
private List<String> filters;
|
||||
|
||||
private List<String> moduleIds;
|
||||
|
||||
private List<String> unSelectIds;
|
||||
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ public class TaskInfoResult {
|
|||
private int index;
|
||||
//任务ID
|
||||
private String taskID;
|
||||
//场景名称
|
||||
private String scenario;
|
||||
//任务名称
|
||||
private String name;
|
||||
//场景ID
|
||||
private String scenarioId;
|
||||
//规则
|
||||
|
|
|
@ -3,6 +3,7 @@ package io.metersphere.api.dto.scenario;
|
|||
import io.metersphere.api.dto.scenario.request.BodyFile;
|
||||
import io.metersphere.commons.json.JSONSchemaGenerator;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy;
|
||||
import org.apache.jmeter.protocol.http.util.HTTPFileArg;
|
||||
|
@ -81,12 +82,16 @@ public class Body {
|
|||
|
||||
private HTTPFileArg[] httpFileArgs(String requestId) {
|
||||
List<HTTPFileArg> list = new ArrayList<>();
|
||||
this.getKvs().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> {
|
||||
setFileArg(list, keyValue.getFiles(), keyValue, requestId);
|
||||
});
|
||||
this.getBinary().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> {
|
||||
setFileArg(list, keyValue.getFiles(), keyValue, requestId);
|
||||
});
|
||||
if (CollectionUtils.isNotEmpty(this.getKvs())) {
|
||||
this.getKvs().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> {
|
||||
setFileArg(list, keyValue.getFiles(), keyValue, requestId);
|
||||
});
|
||||
}
|
||||
if (CollectionUtils.isNotEmpty(this.getBinary())) {
|
||||
this.getBinary().stream().filter(KeyValue::isFile).filter(KeyValue::isEnable).forEach(keyValue -> {
|
||||
setFileArg(list, keyValue.getFiles(), keyValue, requestId);
|
||||
});
|
||||
}
|
||||
return list.toArray(new HTTPFileArg[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,10 +24,7 @@ import io.metersphere.base.mapper.ext.ExtTestPlanMapper;
|
|||
import io.metersphere.base.mapper.ext.ExtTestPlanScenarioCaseMapper;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.DateUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.ServiceUtils;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.commons.utils.*;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.job.sechedule.ApiScenarioTestJob;
|
||||
import io.metersphere.service.ScheduleService;
|
||||
|
@ -35,6 +32,7 @@ import io.metersphere.track.dto.TestPlanDTO;
|
|||
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestPlanRequest;
|
||||
import io.metersphere.track.request.testplan.FileOperationRequest;
|
||||
import io.metersphere.track.service.TestPlanScenarioCaseService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
|
@ -45,6 +43,7 @@ import org.apache.jorphan.collections.ListedHashTree;
|
|||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
@ -81,6 +80,9 @@ public class ApiAutomationService {
|
|||
SqlSessionFactory sqlSessionFactory;
|
||||
@Resource
|
||||
private ApiScenarioReportMapper apiScenarioReportMapper;
|
||||
@Resource
|
||||
@Lazy
|
||||
private TestPlanScenarioCaseService testPlanScenarioCaseService;
|
||||
|
||||
public List<ApiScenarioDTO> list(ApiScenarioRequest request) {
|
||||
request = this.initRequest(request,true,true);
|
||||
|
@ -199,6 +201,7 @@ public class ApiAutomationService {
|
|||
public void delete(String id) {
|
||||
//及连删除外键表
|
||||
this.preDelete(id);
|
||||
testPlanScenarioCaseService.deleteByScenarioId(id);
|
||||
apiScenarioMapper.deleteByPrimaryKey(id);
|
||||
}
|
||||
|
||||
|
@ -397,8 +400,19 @@ public class ApiAutomationService {
|
|||
scenarios.add(scenario);
|
||||
// 创建场景报告
|
||||
if (reportIds != null) {
|
||||
createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
|
||||
request.getExecuteType(), item.getProjectId(), request.getReportUserID());
|
||||
//如果是测试计划页面触发的执行方式,生成报告时createScenarioReport第二个参数需要特殊处理
|
||||
if(StringUtils.equals(request.getRunMode(),ApiRunMode.SCENARIO_PLAN.name())){
|
||||
String testPlanScenarioId = item.getId();
|
||||
if(request.getScenarioTestPlanIdMap()!=null&&request.getScenarioTestPlanIdMap().containsKey(item.getId())){
|
||||
testPlanScenarioId = request.getScenarioTestPlanIdMap().get(item.getId());
|
||||
}
|
||||
createScenarioReport(group.getName(), testPlanScenarioId, item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
|
||||
request.getExecuteType(), item.getProjectId(), request.getReportUserID());
|
||||
}else{
|
||||
createScenarioReport(group.getName(), item.getId(), item.getName(), request.getTriggerMode() == null ? ReportTriggerMode.MANUAL.name() : request.getTriggerMode(),
|
||||
request.getExecuteType(), item.getProjectId(), request.getReportUserID());
|
||||
}
|
||||
|
||||
reportIds.add(group.getName());
|
||||
}
|
||||
group.setHashTree(scenarios);
|
||||
|
@ -589,6 +603,12 @@ public class ApiAutomationService {
|
|||
return apiScenarioMapper.selectByExample(example);
|
||||
}
|
||||
|
||||
public List<ApiScenarioWithBLOBs> selectByIdsWithBLOBs(List<String> ids) {
|
||||
ApiScenarioExample example = new ApiScenarioExample();
|
||||
example.createCriteria().andIdIn(ids);
|
||||
return apiScenarioMapper.selectByExampleWithBLOBs(example);
|
||||
}
|
||||
|
||||
public void createSchedule(Schedule request) {
|
||||
Schedule schedule = scheduleService.buildApiTestSchedule(request);
|
||||
schedule.setJob(ApiScenarioTestJob.class.getName());
|
||||
|
@ -641,4 +661,38 @@ public class ApiAutomationService {
|
|||
dto.setXml(jmx);
|
||||
return dto;
|
||||
}
|
||||
|
||||
public void bathEdit(SaveApiScenarioRequest request) {
|
||||
if (CollectionUtils.isEmpty(request.getScenarioIds())) {
|
||||
return;
|
||||
}
|
||||
if (request.isSelectAllDate()) {
|
||||
request.setScenarioIds(this.getAllScenarioIdsByFontedSelect(
|
||||
request.getModuleIds(), request.getName(), request.getProjectId(), request.getFilters(), request.getUnSelectIds()));
|
||||
}
|
||||
if (StringUtils.isNotBlank(request.getEnvironmentId())) {
|
||||
bathEditEnv(request);
|
||||
return;
|
||||
}
|
||||
ApiScenarioExample apiScenarioExample = new ApiScenarioExample();
|
||||
apiScenarioExample.createCriteria().andIdIn(request.getScenarioIds());
|
||||
ApiScenarioWithBLOBs apiScenarioWithBLOBs = new ApiScenarioWithBLOBs();
|
||||
BeanUtils.copyBean(apiScenarioWithBLOBs, request);
|
||||
apiScenarioWithBLOBs.setUpdateTime(System.currentTimeMillis());
|
||||
apiScenarioMapper.updateByExampleSelective(
|
||||
apiScenarioWithBLOBs,
|
||||
apiScenarioExample);
|
||||
}
|
||||
|
||||
public void bathEditEnv(SaveApiScenarioRequest request) {
|
||||
if (StringUtils.isNotBlank(request.getEnvironmentId())) {
|
||||
List<ApiScenarioWithBLOBs> apiScenarios = selectByIdsWithBLOBs(request.getScenarioIds());
|
||||
apiScenarios.forEach(item -> {
|
||||
JSONObject object = JSONObject.parseObject(item.getScenarioDefinition());
|
||||
object.put("environmentId", request.getEnvironmentId());
|
||||
item.setScenarioDefinition(JSONObject.toJSONString(object));
|
||||
apiScenarioMapper.updateByPrimaryKeySelective(item);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -134,31 +134,41 @@ public class ApiScenarioReportService {
|
|||
}
|
||||
|
||||
public ApiScenarioReport updatePlanCase(TestResult result) {
|
||||
TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(result.getTestId());
|
||||
ScenarioResult scenarioResult = result.getScenarios().get(0);
|
||||
if (scenarioResult.getError() > 0) {
|
||||
testPlanApiScenario.setLastResult(ScenarioStatus.Fail.name());
|
||||
} else {
|
||||
testPlanApiScenario.setLastResult(ScenarioStatus.Success.name());
|
||||
// TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(result.getTestId());
|
||||
List<ScenarioResult> scenarioResultList = result.getScenarios();
|
||||
ApiScenarioReport returnReport = null;
|
||||
for (ScenarioResult scenarioResult :
|
||||
scenarioResultList) {
|
||||
ApiScenarioReport report = editReport(scenarioResult);
|
||||
// 报告详情内容
|
||||
ApiScenarioReportDetail detail = new ApiScenarioReportDetail();
|
||||
TestResult newResult = createTestResult(result.getTestId(), scenarioResult);
|
||||
scenarioResult.setName(report.getScenarioName());
|
||||
newResult.addScenario(scenarioResult);
|
||||
|
||||
detail.setContent(JSON.toJSONString(newResult).getBytes(StandardCharsets.UTF_8));
|
||||
detail.setReportId(report.getId());
|
||||
detail.setProjectId(report.getProjectId());
|
||||
apiScenarioReportDetailMapper.insert(detail);
|
||||
|
||||
TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(report.getScenarioId());
|
||||
if(testPlanApiScenario!=null){
|
||||
report.setScenarioId(testPlanApiScenario.getApiScenarioId());
|
||||
apiScenarioReportMapper.updateByPrimaryKeySelective(report);
|
||||
if (scenarioResult.getError() > 0) {
|
||||
testPlanApiScenario.setLastResult(ScenarioStatus.Fail.name());
|
||||
} else {
|
||||
testPlanApiScenario.setLastResult(ScenarioStatus.Success.name());
|
||||
}
|
||||
String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError()));
|
||||
testPlanApiScenario.setPassRate(passRate);
|
||||
testPlanApiScenario.setReportId(report.getId());
|
||||
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
|
||||
}
|
||||
returnReport = report;
|
||||
}
|
||||
String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError()));
|
||||
testPlanApiScenario.setPassRate(passRate);
|
||||
// 存储场景报告
|
||||
ApiScenarioReport report = editReport(scenarioResult);
|
||||
// 报告详情内容
|
||||
ApiScenarioReportDetail detail = new ApiScenarioReportDetail();
|
||||
TestResult newResult = createTestResult(result.getTestId(), scenarioResult);
|
||||
scenarioResult.setName(report.getScenarioName());
|
||||
newResult.addScenario(scenarioResult);
|
||||
|
||||
detail.setContent(JSON.toJSONString(newResult).getBytes(StandardCharsets.UTF_8));
|
||||
detail.setReportId(report.getId());
|
||||
detail.setProjectId(report.getProjectId());
|
||||
apiScenarioReportDetailMapper.insert(detail);
|
||||
|
||||
testPlanApiScenario.setReportId(report.getId());
|
||||
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
|
||||
return report;
|
||||
return returnReport;
|
||||
}
|
||||
|
||||
public ApiScenarioReport updateSchedulePlanCase(TestResult result) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler;
|
|||
import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler;
|
||||
import io.metersphere.api.dto.definition.request.timer.MsConstantTimer;
|
||||
import io.metersphere.api.dto.scenario.Body;
|
||||
import io.metersphere.api.dto.scenario.KeyValue;
|
||||
import io.metersphere.api.dto.scenario.Scenario;
|
||||
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
|
||||
import io.metersphere.api.dto.scenario.request.*;
|
||||
|
@ -108,12 +109,40 @@ public class HistoricalDataUpgradeService {
|
|||
URL urlObject = new URL(request1.getUrl());
|
||||
String envPath = StringUtils.equals(urlObject.getPath(), "/") ? "" : urlObject.getPath();
|
||||
request1.setPath(envPath);
|
||||
request1.setUrl(null);
|
||||
} catch (Exception ex) {
|
||||
LogUtil.error(ex.getMessage());
|
||||
}
|
||||
} else {
|
||||
request1.setUrl(null);
|
||||
}
|
||||
if (request1.getBody() != null && request1.getBody().isOldKV()) {
|
||||
request1.getBody().setType(Body.FORM_DATA);
|
||||
if (request1.getBody() != null) {
|
||||
request1.getBody().setBinary(new ArrayList<>());
|
||||
if (request1.getBody().isOldKV()) {
|
||||
request1.getBody().setType(Body.FORM_DATA);
|
||||
}
|
||||
if ("json".equals(request1.getBody().getFormat())) {
|
||||
request1.getBody().setType(Body.JSON);
|
||||
if (CollectionUtils.isEmpty(request1.getHeaders())) {
|
||||
List<KeyValue> headers = new LinkedList<>();
|
||||
headers.add(new KeyValue("Content-Type", "application/json"));
|
||||
request1.setHeaders(headers);
|
||||
} else {
|
||||
boolean isJsonType = false;
|
||||
for (KeyValue keyValue : request1.getHeaders()) {
|
||||
if ("Content-Type".equals(keyValue.getName())) {
|
||||
isJsonType = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isJsonType) {
|
||||
request1.getHeaders().add(new KeyValue("Content-Type", "application/json"));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ("xml".equals(request1.getBody().getFormat())) {
|
||||
request1.getBody().setType(Body.XML);
|
||||
}
|
||||
}
|
||||
BeanUtils.copyBean(element, request1);
|
||||
((MsHTTPSamplerProxy) element).setProtocol(RequestType.HTTP);
|
||||
|
@ -292,7 +321,7 @@ public class HistoricalDataUpgradeService {
|
|||
copyDir(dir, BODY_FILE_DIR);
|
||||
}
|
||||
|
||||
private void createApiScenarioWithBLOBs(SaveHistoricalDataUpgrade saveHistoricalDataUpgrade, Scenario oldScenario, String scenarioDefinition, ApiScenarioMapper mapper) {
|
||||
private void createApiScenarioWithBLOBs(SaveHistoricalDataUpgrade saveHistoricalDataUpgrade, Scenario oldScenario, String scenarioDefinition, ApiScenarioMapper mapper, int num) {
|
||||
if (StringUtils.isEmpty(oldScenario.getName())) {
|
||||
oldScenario.setName("默认名称-" + DateUtils.getTimeStr(System.currentTimeMillis()));
|
||||
}
|
||||
|
@ -310,7 +339,6 @@ public class HistoricalDataUpgradeService {
|
|||
scenario.setUpdateTime(System.currentTimeMillis());
|
||||
scenario.setStatus(ScenarioStatus.Underway.name());
|
||||
scenario.setUserId(SessionUtils.getUserId());
|
||||
scenario.setNum(getNextNum(saveHistoricalDataUpgrade.getProjectId()));
|
||||
mapper.updateByPrimaryKeySelective(scenario);
|
||||
} else {
|
||||
scenario = new ApiScenarioWithBLOBs();
|
||||
|
@ -328,7 +356,7 @@ public class HistoricalDataUpgradeService {
|
|||
scenario.setUpdateTime(System.currentTimeMillis());
|
||||
scenario.setStatus(ScenarioStatus.Underway.name());
|
||||
scenario.setUserId(SessionUtils.getUserId());
|
||||
scenario.setNum(getNextNum(saveHistoricalDataUpgrade.getProjectId()));
|
||||
scenario.setNum(num);
|
||||
mapper.insert(scenario);
|
||||
}
|
||||
}
|
||||
|
@ -342,6 +370,7 @@ public class HistoricalDataUpgradeService {
|
|||
List<ApiTest> blobs = apiTestMapper.selectByExampleWithBLOBs(example);
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
ApiScenarioMapper mapper = sqlSession.getMapper(ApiScenarioMapper.class);
|
||||
int num = getNextNum(saveHistoricalDataUpgrade.getProjectId());
|
||||
for (ApiTest test : blobs) {
|
||||
// 附件迁移
|
||||
createBodyFiles(test.getId());
|
||||
|
@ -352,7 +381,8 @@ public class HistoricalDataUpgradeService {
|
|||
for (Scenario scenario : scenarios) {
|
||||
MsScenario scenario1 = createScenario(scenario);
|
||||
String scenarioDefinition = JSON.toJSONString(scenario1);
|
||||
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario, scenarioDefinition, mapper);
|
||||
num++;
|
||||
createApiScenarioWithBLOBs(saveHistoricalDataUpgrade, scenario, scenarioDefinition, mapper, num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -365,14 +395,16 @@ public class HistoricalDataUpgradeService {
|
|||
environmentDTOMap = new HashMap<>();
|
||||
if (CollectionUtils.isNotEmpty(environments)) {
|
||||
environments.forEach(environment -> {
|
||||
EnvironmentConfig envConfig = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
|
||||
envConfig.getDatabaseConfigs().forEach(item -> {
|
||||
EnvironmentDTO dto = new EnvironmentDTO();
|
||||
dto.setDatabaseConfig(item);
|
||||
dto.setEnvironmentId(environment.getId());
|
||||
environmentDTOMap.put(item.getId(), dto);
|
||||
});
|
||||
if (environment != null && environment.getConfig() != null) {
|
||||
EnvironmentConfig envConfig = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
|
||||
envConfig.getDatabaseConfigs().forEach(item -> {
|
||||
EnvironmentDTO dto = new EnvironmentDTO();
|
||||
dto.setDatabaseConfig(item);
|
||||
dto.setEnvironmentId(environment.getId());
|
||||
environmentDTOMap.put(item.getId(), dto);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -164,7 +164,13 @@
|
|||
<if test="request.executeStatus == 'executePass'">
|
||||
and api_scenario.last_result = 'Success'
|
||||
</if>
|
||||
|
||||
<if test="request.notInTestPlan == true ">
|
||||
and api_scenario.id not in (
|
||||
select pc.api_scenario_id
|
||||
from test_plan_api_scenario pc
|
||||
where pc.test_plan_id = #{request.planId}
|
||||
)
|
||||
</if>
|
||||
</where>
|
||||
<if test="request.orders != null and request.orders.size() > 0">
|
||||
order by
|
||||
|
|
|
@ -145,43 +145,43 @@
|
|||
</sql>
|
||||
<sql id="combine">
|
||||
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
|
||||
and atc.name
|
||||
and t1.name
|
||||
<include refid="condition">
|
||||
<property name="object" value="${condition}.name"/>
|
||||
</include>
|
||||
</if>
|
||||
<if test="${condition}.updateTime != null">
|
||||
and atc.update_time
|
||||
and t1.update_time
|
||||
<include refid="condition">
|
||||
<property name="object" value="${condition}.updateTime"/>
|
||||
</include>
|
||||
</if>
|
||||
<if test="${condition}.createTime != null">
|
||||
and atc.create_time
|
||||
<if test="${condition}.createTime != null">
|
||||
and t1.create_time
|
||||
<include refid="condition">
|
||||
<property name="object" value="${condition}.createTime"/>
|
||||
</include>
|
||||
</if>
|
||||
<if test="${condition}.priority != null">
|
||||
and atc.priority
|
||||
and t1.priority
|
||||
<include refid="condition">
|
||||
<property name="object" value="${condition}.priority"/>
|
||||
</include>
|
||||
</if>
|
||||
<if test="${condition}.creator != null">
|
||||
and atc.user_id
|
||||
and t1.user_id
|
||||
<include refid="condition">
|
||||
<property name="object" value="${condition}.creator"/>
|
||||
</include>
|
||||
</if>
|
||||
<if test="${condition}.tags != null">
|
||||
and atc.tags
|
||||
and t1.tags
|
||||
<include refid="condition">
|
||||
<property name="object" value="${condition}.tags"/>
|
||||
</include>
|
||||
</if>
|
||||
<if test="${condition}.status != null">
|
||||
and ader.status
|
||||
and t2.status
|
||||
<include refid="condition">
|
||||
<property name="object" value="${condition}.status"/>
|
||||
</include>
|
||||
|
@ -190,17 +190,18 @@
|
|||
</sql>
|
||||
<select id="list" resultType="io.metersphere.api.dto.definition.ApiTestCaseResult">
|
||||
SELECT
|
||||
t1.*,
|
||||
t2.STATUS AS execResult,
|
||||
t2.create_time AS execTime,
|
||||
u2.NAME AS createUser,
|
||||
u1.NAME AS updateUser
|
||||
t1.*,
|
||||
t2.STATUS AS execResult,
|
||||
t2.create_time AS execTime,
|
||||
u2.NAME AS createUser,
|
||||
u1.NAME AS updateUser
|
||||
FROM
|
||||
api_test_case t1
|
||||
LEFT JOIN api_definition_exec_result t2 ON t1.last_result_id = t2.id
|
||||
LEFT JOIN USER u1 ON t1.update_user_id = u1.id
|
||||
LEFT JOIN USER u2 ON t1.create_user_id = u2.id
|
||||
LEFT JOIN USER u3 ON t2.user_id = u3.id
|
||||
api_test_case t1
|
||||
LEFT JOIN api_definition_exec_result t2 ON t1.last_result_id = t2.id
|
||||
inner join api_definition a on t1.api_definition_id = a.id
|
||||
LEFT JOIN USER u1 ON t1.update_user_id = u1.id
|
||||
LEFT JOIN USER u2 ON t1.create_user_id = u2.id
|
||||
LEFT JOIN USER u3 ON t2.user_id = u3.id
|
||||
<where>
|
||||
<if test="request.combine != null">
|
||||
<include refid="combine">
|
||||
|
@ -224,38 +225,42 @@
|
|||
<if test="request.apiDefinitionId != null and request.apiDefinitionId!=''">
|
||||
AND t1.api_definition_id = #{request.apiDefinitionId}
|
||||
</if>
|
||||
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
|
||||
and a.module_id in
|
||||
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
|
||||
#{nodeId}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
|
||||
<if test="request.orders != null and request.orders.size() > 0">
|
||||
order by
|
||||
<foreach collection="request.orders" separator="," item="order">
|
||||
t1.${order.name} ${order.type}
|
||||
</foreach>
|
||||
</foreach>
|
||||
</if>
|
||||
|
||||
</select>
|
||||
|
||||
<select id="listSimple" resultType="io.metersphere.api.dto.definition.ApiTestCaseDTO">
|
||||
select
|
||||
atc.id, atc.project_id, atc.name, atc.api_definition_id, atc.priority, atc.description, atc.create_user_id, atc.update_user_id, atc.create_time, atc.update_time, atc.num,
|
||||
a.module_id, a.path, a.protocol
|
||||
t1.id, t1.project_id, t1.name, t1.api_definition_id, t1.priority, t1.description, t1.create_user_id, t1.update_user_id, t1.create_time, t1.update_time, t1.num,
|
||||
a.module_id, a.path, a.protocol, t1.tags
|
||||
from
|
||||
api_test_case atc
|
||||
inner join
|
||||
api_definition a
|
||||
on
|
||||
atc.api_definition_id = a.id
|
||||
<if test="request.protocol != null and request.protocol!=''">
|
||||
and a.protocol = #{request.protocol}
|
||||
</if>
|
||||
<choose>
|
||||
<when test="request.status == 'Trash'">
|
||||
and a.status = 'Trash'
|
||||
</when>
|
||||
<otherwise>
|
||||
and a.status != 'Trash'
|
||||
</otherwise>
|
||||
</choose>
|
||||
api_test_case t1
|
||||
LEFT JOIN api_definition_exec_result t2 ON t1.last_result_id = t2.id
|
||||
inner join api_definition a on t1.api_definition_id = a.id
|
||||
<if test="request.protocol != null and request.protocol!=''">
|
||||
and a.protocol = #{request.protocol}
|
||||
</if>
|
||||
<choose>
|
||||
<when test="request.status == 'Trash'">
|
||||
and a.status = 'Trash'
|
||||
</when>
|
||||
<otherwise>
|
||||
and a.status != 'Trash'
|
||||
</otherwise>
|
||||
</choose>
|
||||
<where>
|
||||
<if test="request.combine != null">
|
||||
<include refid="combine">
|
||||
|
@ -264,25 +269,25 @@
|
|||
</include>
|
||||
</if>
|
||||
<if test="request.projectId != null and request.projectId!=''">
|
||||
and atc.project_id = #{request.projectId}
|
||||
and t1.project_id = #{request.projectId}
|
||||
</if>
|
||||
<if test="request.id != null and request.id!=''">
|
||||
and atc.id = #{request.id}
|
||||
and t1.id = #{request.id}
|
||||
</if>
|
||||
<if test="request.ids != null and request.ids.size() > 0">
|
||||
<if test="request.projectId != null and request.projectId!=''">
|
||||
and
|
||||
</if>
|
||||
atc.id in
|
||||
t1.id in
|
||||
<foreach collection="request.ids" item="caseId" separator="," open="(" close=")">
|
||||
#{caseId}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="request.name != null and request.name!=''">
|
||||
and atc.name like CONCAT('%', #{request.name},'%')
|
||||
and (t1.name like CONCAT('%', #{request.name},'%') or t1.tags like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.createTime > 0">
|
||||
and atc.create_time >= #{request.createTime}
|
||||
and t1.create_time >= #{request.createTime}
|
||||
</if>
|
||||
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
|
||||
and a.module_id in
|
||||
|
@ -295,7 +300,7 @@
|
|||
<if test="values != null and values.size() > 0">
|
||||
<choose>
|
||||
<when test="key == 'priority'">
|
||||
and atc.priority in
|
||||
and t1.priority in
|
||||
<foreach collection="values" item="value" separator="," open="(" close=")">
|
||||
#{value}
|
||||
</foreach>
|
||||
|
|
|
@ -50,14 +50,14 @@
|
|||
AND create_time BETWEEN #{startTime} and #{endTime}
|
||||
</select>
|
||||
<select id="findRunningTaskInfoByProjectID" resultType="io.metersphere.api.dto.datacount.response.TaskInfoResult">
|
||||
SELECT apiScene.id AS scenarioId,apiScene.`name` AS scenario,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
|
||||
SELECT apiScene.id AS scenarioId,apiScene.`name` AS `name` ,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
|
||||
'scenario' AS taskType
|
||||
FROM api_scenario apiScene
|
||||
INNER JOIN `schedule` sch ON apiScene.id = sch.resource_id
|
||||
INNER JOIN `user` u ON u.id = sch.user_id
|
||||
WHERE sch.`enable` = true AND apiScene.project_id = #{0,jdbcType=VARCHAR}
|
||||
UNION
|
||||
SELECT testPlan.id AS scenarioId,testPlan.`name` AS scenario,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
|
||||
SELECT testPlan.id AS scenarioId,testPlan.`name` AS `name`,sch.id AS taskID,sch.`value` AS rule,sch.`enable` AS `taskStatus`,u.`name` AS creator,sch.update_time AS updateTime,
|
||||
'testPlan' AS taskType
|
||||
FROM test_plan testPlan
|
||||
INNER JOIN `schedule` sch ON testPlan.id = sch.resource_id
|
||||
|
|
|
@ -268,7 +268,14 @@
|
|||
|
||||
<select id="list" resultType="io.metersphere.track.dto.TestCaseDTO">
|
||||
select
|
||||
<include refid="io.metersphere.base.mapper.TestCaseMapper.Base_Column_List"/>
|
||||
<if test="request.selectFields != null and request.selectFields.size() > 0">
|
||||
<foreach collection="request.selectFields" item="field" separator=",">
|
||||
${field}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="request.selectFields == null or request.selectFields.size() == 0">
|
||||
<include refid="io.metersphere.base.mapper.TestCaseMapper.Base_Column_List"/>
|
||||
</if>
|
||||
from test_case
|
||||
<where>
|
||||
<if test="request.combine != null">
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
</foreach>
|
||||
</if>
|
||||
<if test="request.name != null and request.name!=''">
|
||||
and c.name like CONCAT('%', #{request.name},'%')
|
||||
and (c.name like CONCAT('%', #{request.name},'%') or c.tags like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
|
||||
and a.module_id in
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
package io.metersphere.commons.constants;
|
||||
|
||||
public enum TestPlanReportStatus {
|
||||
RUNNING, COMPLETED, SUCCESS, FAILED
|
||||
}
|
|
@ -1,12 +1,7 @@
|
|||
package io.metersphere.job.sechedule;
|
||||
|
||||
import io.metersphere.api.dto.automation.ExecuteType;
|
||||
import io.metersphere.api.dto.automation.RunScenarioRequest;
|
||||
import io.metersphere.api.dto.automation.SchedulePlanScenarioExecuteRequest;
|
||||
import io.metersphere.api.dto.definition.RunCaseRequest;
|
||||
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.*;
|
||||
import io.metersphere.commons.constants.ApiRunMode;
|
||||
|
@ -19,13 +14,9 @@ import io.metersphere.track.dto.TestPlanLoadCaseDTO;
|
|||
import io.metersphere.track.request.testplan.LoadCaseRequest;
|
||||
import io.metersphere.track.request.testplan.RunTestPlanRequest;
|
||||
import io.metersphere.track.service.*;
|
||||
import org.python.antlr.ast.Str;
|
||||
import org.quartz.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* 情景测试Job
|
||||
|
@ -130,7 +121,7 @@ public class TestPlanTestJob extends MsScheduleJob {
|
|||
scenarioRequest.setTestPlanID(this.resourceId);
|
||||
scenarioRequest.setRunMode(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
|
||||
scenarioRequest.setTestPlanReportId(testPlanReport.getId());
|
||||
testPlanService.runApiCase(scenarioRequest);
|
||||
testPlanService.runScenarioCase(scenarioRequest);
|
||||
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
|
||||
|
||||
//执行性能测试任务
|
||||
|
|
|
@ -105,7 +105,7 @@ public class ShiroDBRealm extends AuthorizingRealm {
|
|||
if (user == null) {
|
||||
msg = "The user does not exist: " + userId;
|
||||
logger.warn(msg);
|
||||
throw new UnknownAccountException(Translator.get("password_is_incorrect") + userId);
|
||||
throw new UnknownAccountException(Translator.get("password_is_incorrect"));
|
||||
}
|
||||
}
|
||||
return user;
|
||||
|
@ -119,7 +119,7 @@ public class ShiroDBRealm extends AuthorizingRealm {
|
|||
if (user == null) {
|
||||
msg = "The user does not exist: " + userId;
|
||||
logger.warn(msg);
|
||||
throw new UnknownAccountException(Translator.get("password_is_incorrect") + userId);
|
||||
throw new UnknownAccountException(Translator.get("password_is_incorrect"));
|
||||
}
|
||||
userId = user.getId();
|
||||
}
|
||||
|
|
|
@ -14,10 +14,12 @@ import io.metersphere.excel.domain.ExcelResponse;
|
|||
import io.metersphere.service.CheckPermissionService;
|
||||
import io.metersphere.service.FileService;
|
||||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.dto.TestPlanCaseDTO;
|
||||
import io.metersphere.track.request.testcase.EditTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
|
||||
import io.metersphere.track.request.testplan.FileOperationRequest;
|
||||
import io.metersphere.track.request.testplancase.QueryTestPlanCaseRequest;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import org.apache.shiro.authz.annotation.Logical;
|
||||
import org.apache.shiro.authz.annotation.RequiresRoles;
|
||||
|
@ -65,6 +67,11 @@ public class TestCaseController {
|
|||
return testCaseService.listTestCaseMthod(request);
|
||||
}
|
||||
|
||||
@PostMapping("/list/ids")
|
||||
public List<TestCaseDTO> getTestPlanCaseIds(@RequestBody QueryTestCaseRequest request) {
|
||||
return testCaseService.listTestCaseIds(request);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("recent/{count}")
|
||||
public List<TestCase> recentTestPlans(@PathVariable int count) {
|
||||
|
|
|
@ -22,6 +22,8 @@ public class QueryTestCaseRequest extends TestCase {
|
|||
|
||||
private Map<String, List<String>> filters;
|
||||
|
||||
private List<String> selectFields;
|
||||
|
||||
private String planId;
|
||||
|
||||
private String workspaceId;
|
||||
|
|
|
@ -676,4 +676,19 @@ public class TestCaseService {
|
|||
editTestCase(request);
|
||||
return request.getId();
|
||||
}
|
||||
|
||||
public List<TestCaseDTO> listTestCaseIds(QueryTestCaseRequest request) {
|
||||
List<OrderRequest> orderList = ServiceUtils.getDefaultOrder(request.getOrders());
|
||||
OrderRequest order = new OrderRequest();
|
||||
// 对模板导入的测试用例排序
|
||||
order.setName("sort");
|
||||
order.setType("desc");
|
||||
orderList.add(order);
|
||||
request.setOrders(orderList);
|
||||
List<String> selectFields = new ArrayList<>();
|
||||
selectFields.add("id");
|
||||
selectFields.add("name");
|
||||
request.setSelectFields(selectFields);
|
||||
return extTestCaseMapper.list(request);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
@ -78,6 +79,13 @@ public class TestPlanReportService {
|
|||
return returnList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成测试计划
|
||||
* @param planId
|
||||
* @param userId
|
||||
* @param triggerMode
|
||||
* @return
|
||||
*/
|
||||
public TestPlanReport genTestPlanReport(String planId, String userId,String triggerMode) {
|
||||
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId);
|
||||
|
||||
|
@ -101,6 +109,7 @@ public class TestPlanReportService {
|
|||
TestPlanReport testPlanReport = new TestPlanReport();
|
||||
testPlanReport.setTestPlanId(planId);
|
||||
testPlanReport.setId(testPlanReportID);
|
||||
testPlanReport.setStatus(APITestStatus.Starting.name());
|
||||
testPlanReport.setCreateTime(System.currentTimeMillis());
|
||||
testPlanReport.setUpdateTime(System.currentTimeMillis());
|
||||
try {
|
||||
|
@ -244,13 +253,13 @@ public class TestPlanReportService {
|
|||
component.afterBuild(testCaseReportMetricDTO);
|
||||
});
|
||||
|
||||
this.update(testPlanReport);
|
||||
|
||||
TestPlanReportDataExample example = new TestPlanReportDataExample();
|
||||
example.createCriteria().andTestPlanReportIdEqualTo(planReportId);
|
||||
List<TestPlanReportDataWithBLOBs> testPlanReportDataList = testPlanReportDataMapper.selectByExampleWithBLOBs(example);
|
||||
|
||||
TestPlanReportDataWithBLOBs testPlanReportData = null;
|
||||
if (!testPlanReportDataList.isEmpty()) {
|
||||
TestPlanReportDataWithBLOBs testPlanReportData = testPlanReportDataList.get(0);
|
||||
testPlanReportData = testPlanReportDataList.get(0);
|
||||
testPlanReportData.setExecuteResult(JSONObject.toJSONString(testCaseReportMetricDTO.getExecuteResult()));
|
||||
testPlanReportData.setFailurTestCases(JSONObject.toJSONString(testCaseReportMetricDTO.getFailureTestCases()));
|
||||
testPlanReportData.setModuleExecuteResult(JSONArray.toJSONString(testCaseReportMetricDTO.getModuleExecuteResult()));
|
||||
|
@ -260,8 +269,49 @@ public class TestPlanReportService {
|
|||
testPlanReportDataMapper.updateByPrimaryKeyWithBLOBs(testPlanReportData);
|
||||
}
|
||||
|
||||
String testPlanStatus = this.getTestPlanReportStatus(testPlanReport,testPlanReportData);
|
||||
testPlanReport.setStatus(testPlanStatus);
|
||||
this.update(testPlanReport);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算测试计划的状态
|
||||
* @param testPlanReport
|
||||
* @return
|
||||
*/
|
||||
private String getTestPlanReportStatus(TestPlanReport testPlanReport, TestPlanReportDataWithBLOBs testPlanReportData) {
|
||||
String status = TestPlanReportStatus.COMPLETED.name();
|
||||
if(testPlanReport!=null){
|
||||
if(testPlanReport.getIsApiCaseExecuting() || testPlanReport.getIsPerformanceExecuting() || testPlanReport.getIsScenarioExecuting()){
|
||||
status = TestPlanReportStatus.RUNNING.name();
|
||||
}else {
|
||||
if(testPlanReportData == null){
|
||||
String failCaseString = testPlanReportData.getFailurTestCases();
|
||||
status = TestPlanReportStatus.SUCCESS.name();
|
||||
try {
|
||||
JSONObject failurCaseObject = JSONObject.parseObject(failCaseString);
|
||||
if(failurCaseObject.containsKey("apiTestCases")&&failurCaseObject.getJSONArray("apiTestCases").size()>=0){
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
return status;
|
||||
}
|
||||
if(failurCaseObject.containsKey("loadTestCases")&&failurCaseObject.getJSONArray("loadTestCases").size()>=0){
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
return status;
|
||||
}
|
||||
if(failurCaseObject.containsKey("scenarioTestCases")&&failurCaseObject.getJSONArray("scenarioTestCases").size()>=0){
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
return status;
|
||||
}
|
||||
}catch (Exception e){
|
||||
status = TestPlanReportStatus.FAILED.name();
|
||||
}
|
||||
}else {
|
||||
status = TestPlanReportStatus.COMPLETED.name();
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
public void update(TestPlanReport report) {
|
||||
if (!report.getIsApiCaseExecuting() && !report.getIsPerformanceExecuting() && !report.getIsScenarioExecuting()) {
|
||||
|
@ -295,26 +345,26 @@ public class TestPlanReportService {
|
|||
String subject = "";
|
||||
String event = "";
|
||||
|
||||
successContext = "接口测试定时任务通知:'" + testPlan.getName() + "'执行成功" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url;
|
||||
failedContext = "接口测试定时任务通知:'" + testPlan.getName() + "'执行失败" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url;
|
||||
successContext = "测试计划定时任务通知:'" + testPlan.getName() + "'执行成功" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url;
|
||||
failedContext = "测试计划定时任务通知:'" + testPlan.getName() + "'执行失败" + "\n" + "请点击下面链接进入测试报告页面" + "\n" + url;
|
||||
subject = Translator.get("task_notification");
|
||||
|
||||
if (StringUtils.equals("Success", testPlanReport.getStatus())) {
|
||||
event = NoticeConstants.Event.EXECUTE_SUCCESSFUL;
|
||||
} else {
|
||||
if (StringUtils.equals(TestPlanReportStatus.FAILED.name(), testPlanReport.getStatus())) {
|
||||
event = NoticeConstants.Event.EXECUTE_FAILED;
|
||||
} else {
|
||||
event = NoticeConstants.Event.EXECUTE_SUCCESSFUL;
|
||||
}
|
||||
Map<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put("testName", testPlan.getName());
|
||||
paramMap.put("id", testPlanReport.getId());
|
||||
paramMap.put("type", "api");
|
||||
paramMap.put("type", "testPlan");
|
||||
paramMap.put("url", url);
|
||||
paramMap.put("status", testPlanReport.getStatus());
|
||||
NoticeModel noticeModel = NoticeModel.builder()
|
||||
.successContext(successContext)
|
||||
.successMailTemplate("ApiSuccessfulNotification")
|
||||
.successMailTemplate("TestPlanSuccessfulNotification")
|
||||
.failedContext(failedContext)
|
||||
.failedMailTemplate("ApiFailedNotification")
|
||||
.failedMailTemplate("TestPlanFailedNotification")
|
||||
.testId(testPlan.getId())
|
||||
.status(testPlanReport.getStatus())
|
||||
.event(event)
|
||||
|
|
|
@ -22,9 +22,7 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
|
@ -51,11 +49,12 @@ public class TestPlanScenarioCaseService {
|
|||
}
|
||||
|
||||
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);
|
||||
// List<String> ids = apiAutomationService.selectIdsNotExistsInPlan(request.getProjectId(), request.getPlanId());
|
||||
// if (CollectionUtils.isEmpty(ids)) {
|
||||
// return new ArrayList<>();
|
||||
// }
|
||||
// request.setIds(ids);
|
||||
request.setNotInTestPlan(true);
|
||||
return apiAutomationService.list(request);
|
||||
}
|
||||
|
||||
|
@ -88,11 +87,17 @@ public class TestPlanScenarioCaseService {
|
|||
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);
|
||||
List<TestPlanApiScenario> testPlanApiScenarioList = testPlanApiScenarioMapper.selectByExample(example);
|
||||
|
||||
List<String> scenarioIds = new ArrayList<>();
|
||||
Map<String,String> scenarioIdApiScarionMap = new HashMap<>();
|
||||
for (TestPlanApiScenario apiScenario:
|
||||
testPlanApiScenarioList) {
|
||||
scenarioIds.add(apiScenario.getApiScenarioId());
|
||||
scenarioIdApiScarionMap.put(apiScenario.getApiScenarioId(),apiScenario.getId());
|
||||
}
|
||||
request.setScenarioIds(scenarioIds);
|
||||
request.setScenarioTestPlanIdMap(scenarioIdApiScarionMap);
|
||||
request.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
|
||||
return apiAutomationService.run(request);
|
||||
}
|
||||
|
@ -120,4 +125,16 @@ public class TestPlanScenarioCaseService {
|
|||
request.setPlanId(planId);
|
||||
deleteApiCaseBath(request);
|
||||
}
|
||||
|
||||
public void bathDeleteByScenarioIds(List<String> ids) {
|
||||
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
|
||||
example.createCriteria().andApiScenarioIdIn(ids);
|
||||
testPlanApiScenarioMapper.deleteByExample(example);
|
||||
}
|
||||
|
||||
public void deleteByScenarioId(String id) {
|
||||
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
|
||||
example.createCriteria().andApiScenarioIdEqualTo(id);
|
||||
testPlanApiScenarioMapper.deleteByExample(example);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -751,7 +751,7 @@ public class TestPlanService {
|
|||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public String runApiCase(SchedulePlanScenarioExecuteRequest request) {
|
||||
public String runScenarioCase(SchedulePlanScenarioExecuteRequest request) {
|
||||
MsTestPlan testPlan = new MsTestPlan();
|
||||
testPlan.setHashTree(new LinkedList<>());
|
||||
HashTree jmeterHashTree = new ListedHashTree();
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 132f406fac7fb4d841210343eb98c09f78317f18
|
||||
Subproject commit 8d175b5363274672ff33a5883b730e8aaa823f8d
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MeterSphere</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<div style="text-align: left">
|
||||
<p>尊敬的用户:</p>
|
||||
<p style="margin-left: 60px">您好:
|
||||
</div>
|
||||
<div style="margin-left: 100px">
|
||||
<p>您所执行的 ${testName} 测试计划运行失败<br/>
|
||||
请点击下面链接进入测试报告页面</p>
|
||||
<a href="${url}/#/${type}/report/view/${id}">${url}/#/${type}/report/view/${id}</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>MeterSphere</title>
|
||||
</head>
|
||||
<body>
|
||||
<div style="text-align: left">
|
||||
<p>尊敬的用户:</p>
|
||||
<p style="margin-left: 60px">您好:
|
||||
</div>
|
||||
<div style="margin-left: 100px">
|
||||
<p>您所执行的 ${testName} 测试计划运行成功<br/>
|
||||
请点击下面链接进入测试报告页面</p>
|
||||
<a href="${url}/#/${type}/report/view/${id}">${url}/#/${type}/report/view/${id}</a>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -38,7 +38,6 @@
|
|||
"vue-echarts": "^4.1.0",
|
||||
"vue-float-action-button": "^0.6.6",
|
||||
"vue-i18n": "^8.15.3",
|
||||
"vue-input-tag": "^2.0.7",
|
||||
"vue-jsonpath-picker": "^1.1.5",
|
||||
"vue-papa-parse": "^2.0.0",
|
||||
"vue-pdf": "^4.2.0",
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
@refreshTable="refresh"
|
||||
@saveAsEdit="editScenario"
|
||||
@setModuleOptions="setModuleOptions"
|
||||
@setNodeTree="setNodeTree"
|
||||
@enableTrash="enableTrash"
|
||||
:type="'edit'"
|
||||
ref="nodeTree"/>
|
||||
|
@ -15,6 +16,8 @@
|
|||
<el-tabs v-model="activeName" @tab-click="addTab" @tab-remove="removeTab">
|
||||
<el-tab-pane name="default" :label="$t('api_test.automation.scenario_test')">
|
||||
<ms-api-scenario-list
|
||||
:module-tree="nodeTree"
|
||||
:module-options="moduleOptions"
|
||||
:select-node-ids="selectNodeIds"
|
||||
:trash-enable="trashEnable"
|
||||
:checkRedirectID="checkRedirectID"
|
||||
|
@ -98,6 +101,7 @@
|
|||
tabs: [],
|
||||
trashEnable: false,
|
||||
selectNodeIds: [],
|
||||
nodeTree: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -209,6 +213,9 @@
|
|||
setModuleOptions(data) {
|
||||
this.moduleOptions = data;
|
||||
},
|
||||
setNodeTree(data) {
|
||||
this.nodeTree = data;
|
||||
},
|
||||
changeSelectDataRangeAll(tableType) {
|
||||
this.$route.params.dataSelectRange = 'all';
|
||||
},
|
||||
|
|
|
@ -65,11 +65,9 @@
|
|||
</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">
|
||||
<el-table-column fixed="right" :label="$t('commons.operating')" width="200px" v-if="!referenced">
|
||||
<template v-slot:default="{row}">
|
||||
<div v-if="trashEnable">
|
||||
<!-- <el-button type="text" @click="reductionApi(row)" v-tester>{{ $t('commons.reduction') }}</el-button>-->
|
||||
<!-- <el-button type="text" @click="remove(row)" v-tester>{{ $t('api_test.automation.remove') }}</el-button>-->
|
||||
<ms-table-operator-button :tip="$t('commons.reduction')" icon="el-icon-refresh-left" @exec="reductionApi(row)" v-tester/>
|
||||
<ms-table-operator-button :tip="$t('api_test.automation.remove')" icon="el-icon-delete" @exec="remove(row)" type="danger" v-tester/>
|
||||
</div>
|
||||
|
@ -102,6 +100,14 @@
|
|||
</div>
|
||||
</el-card>
|
||||
|
||||
<batch-edit ref="batchEdit" @batchEdit="batchEdit" :typeArr="typeArr" :value-arr="valueArr" :dialog-title="$t('test_track.case.batch_edit_case')">
|
||||
<template v-slot:value>
|
||||
<environment-select :current-data="{}" :project-id="projectId"/>
|
||||
</template>
|
||||
</batch-edit>
|
||||
|
||||
<batch-move @refresh="search" @moveSave="moveSave" ref="testBatchMove"/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -120,10 +126,17 @@
|
|||
import MsTableOperatorButton from "@/business/components/common/components/MsTableOperatorButton";
|
||||
import PriorityTableItem from "../../../track/common/tableItems/planview/PriorityTableItem";
|
||||
import PlanStatusTableItem from "../../../track/common/tableItems/plan/PlanStatusTableItem";
|
||||
import BatchEdit from "../../../track/case/components/BatchEdit";
|
||||
import {WORKSPACE_ID} from "../../../../../common/js/constants";
|
||||
import EnvironmentSelect from "../../definition/components/environment/EnvironmentSelect";
|
||||
import BatchMove from "../../../track/case/components/BatchMove";
|
||||
|
||||
export default {
|
||||
name: "MsApiScenarioList",
|
||||
components: {
|
||||
BatchMove,
|
||||
EnvironmentSelect,
|
||||
BatchEdit,
|
||||
PlanStatusTableItem,
|
||||
PriorityTableItem,
|
||||
MsTableSelectAll,
|
||||
|
@ -146,6 +159,18 @@
|
|||
trashEnable: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
moduleTree: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
},
|
||||
moduleOptions: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
},
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
@ -176,13 +201,41 @@
|
|||
buttons: [
|
||||
{
|
||||
name: this.$t('api_test.automation.batch_add_plan'), handleClick: this.handleBatchAddCase
|
||||
}, {
|
||||
},
|
||||
{
|
||||
name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute
|
||||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleBatchEdit
|
||||
},
|
||||
{
|
||||
name: this.$t('test_track.case.batch_move_case'), handleClick: this.handleBatchMove
|
||||
}
|
||||
],
|
||||
isSelectAllDate: false,
|
||||
unSelection: [],
|
||||
selectDataCounts: 0,
|
||||
typeArr: [
|
||||
{id: 'level', name: this.$t('test_track.case.priority')},
|
||||
{id: 'status', name: this.$t('test_track.plan.plan_status')},
|
||||
{id: 'principal', name: this.$t('api_test.definition.request.responsible'), optionMethod: this.getPrincipalOptions},
|
||||
{id: 'environmentId', name: this.$t('api_test.definition.request.run_env'), optionMethod: this.getEnvsOptions},
|
||||
],
|
||||
valueArr: {
|
||||
level: [
|
||||
{name: 'P0', id: 'P0'},
|
||||
{name: 'P1', id: 'P1'},
|
||||
{name: 'P2', id: 'P2'},
|
||||
{name: 'P3', id: 'P3'}
|
||||
],
|
||||
status: [
|
||||
{name: this.$t('test_track.plan.plan_status_prepare'), id: 'Prepare'},
|
||||
{name: this.$t('test_track.plan.plan_status_running'), id: 'Underway'},
|
||||
{name: this.$t('test_track.plan.plan_status_completed'), id: 'Completed'}
|
||||
],
|
||||
principal: [],
|
||||
environmentId: []
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
@ -279,6 +332,48 @@
|
|||
handleBatchAddCase() {
|
||||
this.planVisible = true;
|
||||
},
|
||||
handleBatchEdit() {
|
||||
this.$refs.batchEdit.open(this.selectDataCounts);
|
||||
},
|
||||
handleBatchMove() {
|
||||
this.$refs.testBatchMove.open(this.moduleTree, [], this.moduleOptions);
|
||||
},
|
||||
moveSave(param) {
|
||||
this.buildBatchParam(param);
|
||||
param.apiScenarioModuleId = param.nodeId;
|
||||
this.$post('/api/automation/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.testBatchMove.close();
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
batchEdit(form) {
|
||||
let param = {};
|
||||
param[form.type] = form.value;
|
||||
this.buildBatchParam(param);
|
||||
this.$post('/api/automation/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.search();
|
||||
});
|
||||
},
|
||||
getPrincipalOptions(option) {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
option.push(...response.data);
|
||||
});
|
||||
},
|
||||
getEnvsOptions(option) {
|
||||
this.$get('/api/environment/list/' + this.projectId, response => {
|
||||
option.push(...response.data);
|
||||
option.forEach(environment => {
|
||||
if (!(environment.config instanceof Object)) {
|
||||
environment.config = JSON.parse(environment.config);
|
||||
}
|
||||
environment.name = environment.name + (environment.config.httpConfig.socket ?
|
||||
(': ' + environment.config.httpConfig.protocol + '://' + environment.config.httpConfig.socket) : '');
|
||||
});
|
||||
});
|
||||
},
|
||||
addTestPlan(plans) {
|
||||
let obj = {planIds: plans, scenarioIds: this.selection};
|
||||
|
||||
|
@ -316,18 +411,19 @@
|
|||
});
|
||||
}
|
||||
},
|
||||
buildBatchParam(param) {
|
||||
param.scenarioIds = this.selection;
|
||||
param.projectId = getCurrentProjectID();
|
||||
param.selectAllDate = this.isSelectAllDate;
|
||||
param.unSelectIds = this.unSelection;
|
||||
param = Object.assign(param, this.condition);
|
||||
},
|
||||
handleBatchExecute() {
|
||||
this.infoDb = false;
|
||||
let url = "/api/automation/run/batch";
|
||||
let run = {};
|
||||
let scenarioIds = this.selection;
|
||||
run.id = getUUID();
|
||||
run.scenarioIds = scenarioIds;
|
||||
run.projectId = getCurrentProjectID();
|
||||
run.selectAllDate = this.isSelectAllDate;
|
||||
run.unSelectIds = this.unSelection;
|
||||
|
||||
run = Object.assign(run, this.condition);
|
||||
this.buildBatchParam(run);
|
||||
this.$post(url, run, response => {
|
||||
let data = response.data;
|
||||
this.runVisible = false;
|
||||
|
@ -373,9 +469,10 @@
|
|||
});
|
||||
},
|
||||
copy(row) {
|
||||
row.copy = true;
|
||||
row.name = 'copy_'+row.name;
|
||||
this.$emit('edit', row);
|
||||
let rowParam = JSON.parse(JSON.stringify(row));
|
||||
rowParam.copy = true;
|
||||
rowParam.name = 'copy_'+rowParam.name;
|
||||
this.$emit('edit', rowParam);
|
||||
},
|
||||
showReport(row) {
|
||||
this.runVisible = true;
|
||||
|
|
|
@ -119,6 +119,10 @@
|
|||
buildNodePath(node, {path: ''}, moduleOptions);
|
||||
});
|
||||
this.$emit('setModuleOptions', moduleOptions);
|
||||
this.$emit('setNodeTree', this.data);
|
||||
if (this.$refs.nodeTree) {
|
||||
this.$refs.nodeTree.filter(this.condition.filterText);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
@ -142,6 +146,7 @@
|
|||
}, (error) => {
|
||||
this.list();
|
||||
});
|
||||
|
||||
},
|
||||
remove(nodeIds) {
|
||||
this.$post("/api/automation/module/delete", nodeIds, () => {
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="7">
|
||||
<el-form-item label="Tag" prop="tags">
|
||||
<el-form-item :label="$t('api_test.automation.tag')" prop="tags">
|
||||
<ms-input-tag :currentScenario="currentScenario" ref="tag"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
@ -217,7 +217,6 @@
|
|||
import MsApiReportDetail from "../report/ApiReportDetail";
|
||||
import MsVariableList from "./variable/VariableList";
|
||||
import ApiImport from "../../definition/components/import/ApiImport";
|
||||
import InputTag from 'vue-input-tag'
|
||||
import "@/common/css/material-icons.css"
|
||||
import OutsideClick from "@/common/js/outside-click";
|
||||
import ScenarioApiRelevance from "./api/ApiRelevance";
|
||||
|
@ -239,7 +238,6 @@
|
|||
MsInputTag, MsRun,
|
||||
MsApiCustomize,
|
||||
ApiImport,
|
||||
InputTag,
|
||||
MsComponentConfig,
|
||||
},
|
||||
data() {
|
||||
|
@ -616,7 +614,7 @@
|
|||
this.$refs.tag.open();
|
||||
},
|
||||
remove(row, node) {
|
||||
let name = row.name === undefined ? "" : row.name;
|
||||
let name = row === undefined || row.name === undefined ? "" : row.name;
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm_step') + ' ' + name + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
callback: (action) => {
|
||||
|
@ -697,9 +695,9 @@
|
|||
}
|
||||
},
|
||||
|
||||
checkDataIsCopy(){
|
||||
checkDataIsCopy() {
|
||||
// 如果是复制按钮创建的场景,直接进行保存
|
||||
if(this.currentScenario.copy){
|
||||
if (this.currentScenario.copy) {
|
||||
this.editScenario(false);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,68 +1,170 @@
|
|||
<template>
|
||||
<input-tag v-model="data" placeholder="输入回车添加标签" class="ms-tag-input ms-input-div ms-input-tag-wrapper ms-input ms-input-remove"></input-tag>
|
||||
<div
|
||||
class="el-input-tag input-tag-wrapper"
|
||||
:class="[size ? 'el-input-tag--' + size : '']"
|
||||
style="height: auto"
|
||||
@click="foucusTagInput">
|
||||
<el-tag
|
||||
v-for="(tag, idx) in innerTags"
|
||||
v-bind="$attrs"
|
||||
type="info"
|
||||
:key="tag"
|
||||
:size="size"
|
||||
:closable="!readOnly"
|
||||
:disable-transitions="false"
|
||||
@close="remove(idx)">
|
||||
{{tag}}
|
||||
</el-tag>
|
||||
<input
|
||||
v-if="!readOnly"
|
||||
class="tag-input el-input"
|
||||
v-model="newTag"
|
||||
placeholder="输入回车添加"
|
||||
@keydown.delete.stop="removeLastTag"
|
||||
@keydown="addNew"
|
||||
@blur="addNew"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import InputTag from 'vue-input-tag'
|
||||
|
||||
export default {
|
||||
name: "MsInputTag",
|
||||
components: {InputTag},
|
||||
props: {currentScenario: {}},
|
||||
name: 'MsInputTag',
|
||||
props: {
|
||||
currentScenario: {},
|
||||
addTagOnKeys: {
|
||||
type: Array,
|
||||
default: () => [13, 188, 9]
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {type: String, default: "small"}
|
||||
},
|
||||
created() {
|
||||
if (!this.currentScenario.tags) {
|
||||
this.currentScenario.tags = [];
|
||||
}
|
||||
this.data = this.currentScenario.tags;
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
newTag: '',
|
||||
innerTags: this.currentScenario.tags ? [...this.currentScenario.tags] : []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
this.currentScenario.tags = this.data;
|
||||
innerTags() {
|
||||
this.currentScenario.tags = this.innerTags;
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
methods: {
|
||||
foucusTagInput() {
|
||||
if (this.readOnly || !this.$el.querySelector('.tag-input')) {
|
||||
console.log()
|
||||
} else {
|
||||
this.$el.querySelector('.tag-input').focus()
|
||||
}
|
||||
},
|
||||
addNew(e) {
|
||||
if (e && (!this.addTagOnKeys.includes(e.keyCode)) && (e.type !== 'blur')) {
|
||||
return
|
||||
}
|
||||
if (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
let addSuucess = false
|
||||
if (this.newTag.includes(',')) {
|
||||
this.newTag.split(',').forEach(item => {
|
||||
if (this.addTag(item.trim())) {
|
||||
addSuucess = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (this.addTag(this.newTag.trim())) {
|
||||
addSuucess = true
|
||||
}
|
||||
}
|
||||
if (addSuucess) {
|
||||
this.tagChange()
|
||||
this.newTag = ''
|
||||
}
|
||||
},
|
||||
addTag(tag) {
|
||||
tag = tag.trim()
|
||||
if (tag && !this.innerTags.includes(tag)) {
|
||||
this.innerTags.push(tag)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
remove(index) {
|
||||
this.innerTags.splice(index, 1)
|
||||
this.tagChange()
|
||||
},
|
||||
removeLastTag() {
|
||||
if (this.newTag) {
|
||||
return
|
||||
}
|
||||
this.innerTags.pop()
|
||||
this.tagChange()
|
||||
},
|
||||
tagChange() {
|
||||
this.$emit('input', this.innerTags)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.ms-input-tag-wrapper >>> .vue-input-tag-wrapper {
|
||||
border-radius: 2px;
|
||||
border: 1px solid #a5d24a;
|
||||
color: #909399;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ms-input-remove >>> .remove {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.ms-input-div {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ms-input >>> .input-tag {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
min-width: auto;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 4px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
.input-tag-wrapper {
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
background-color: #fff;
|
||||
border-color: #909399;
|
||||
color: #909399;
|
||||
width: auto;
|
||||
background-image: none;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dcdfe6;
|
||||
box-sizing: border-box;
|
||||
color: #606266;
|
||||
display: inline-block;
|
||||
outline: none;
|
||||
padding: 0 10px 0 5px;
|
||||
transition: border-color .2s cubic-bezier(.645, .045, .355, 1);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
height: 23px;
|
||||
padding: 0 5px;
|
||||
line-height: 19px;
|
||||
.el-tag {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.tag-input {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: #303133;
|
||||
font-size: 12px;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
|
||||
outline: none;
|
||||
padding-left: 0;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.el-input-tag {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
|
||||
.el-input-tag--mini {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.el-input-tag--small {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.el-input-tag--medium {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -86,6 +86,11 @@
|
|||
if (!this.data.name) {
|
||||
this.isShowInput = true;
|
||||
}
|
||||
if (this.$refs.nameEdit) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nameEdit.focus();
|
||||
});
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
active() {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
<el-col>
|
||||
<div class="document-url">
|
||||
<el-link href="https://jmeter.apache.org/usermanual/component_reference.html#BeanShell_PostProcessor"
|
||||
target="componentReferenceDoc"
|
||||
type="primary">{{$t('commons.reference_documentation')}}
|
||||
</el-link>
|
||||
</div>
|
||||
|
|
|
@ -11,11 +11,11 @@
|
|||
:title="displayTitle">
|
||||
|
||||
<template v-slot:behindHeaderLeft>
|
||||
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Deleted'" type="danger">{{$t('api_test.automation.reference_deleted')}}</el-tag>
|
||||
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Copy'">{{ $t('commons.copy') }}</el-tag>
|
||||
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced ==='REF'">{{ $t('api_test.scenario.reference') }}</el-tag>
|
||||
<ms-run :debug="false" :reportId="reportId" :run-data="runData"
|
||||
@runRefresh="runRefresh" ref="runTest"/>
|
||||
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Deleted'" type="danger">{{$t('api_test.automation.reference_deleted')}}</el-tag>
|
||||
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced==='Copy'">{{ $t('commons.copy') }}</el-tag>
|
||||
<el-tag size="mini" style="margin-left: 20px" v-if="request.referenced ==='REF'">{{ $t('api_test.scenario.reference') }}</el-tag>
|
||||
<ms-run :debug="false" :reportId="reportId" :run-data="runData"
|
||||
@runRefresh="runRefresh" ref="runTest"/>
|
||||
|
||||
</template>
|
||||
|
||||
|
@ -42,7 +42,7 @@
|
|||
<ms-dubbo-basis-parameters :request="request" v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'" :showScript="false"/>
|
||||
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
<ms-request-result-tail draggable :currentProtocol="request.protocol" :response="request.requestResult" ref="runResult"/>
|
||||
<api-response-component :result="request.requestResult"/>
|
||||
|
||||
<!-- 保存操作 -->
|
||||
<el-button type="primary" size="small" style="margin: 20px; float: right" @click="saveTestCase(item)" v-if="!request.referenced">
|
||||
|
@ -62,6 +62,8 @@
|
|||
import MsRun from "../../../definition/components/Run";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import ApiBaseComponent from "../common/ApiBaseComponent";
|
||||
import ApiResponseComponent from "./ApiResponseComponent";
|
||||
|
||||
export default {
|
||||
name: "MsApiComponent",
|
||||
props: {
|
||||
|
@ -75,8 +77,9 @@
|
|||
currentEnvironmentId: String,
|
||||
},
|
||||
components: {
|
||||
ApiBaseComponent,
|
||||
MsSqlBasisParameters, MsTcpBasisParameters, MsDubboBasisParameters, MsApiRequestForm, MsRequestResultTail, MsRun},
|
||||
ApiBaseComponent, ApiResponseComponent,
|
||||
MsSqlBasisParameters, MsTcpBasisParameters, MsDubboBasisParameters, MsApiRequestForm, MsRequestResultTail, MsRun
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
|
@ -129,31 +132,31 @@
|
|||
return this.$t('api_test.automation.api_list_import');
|
||||
} else if (this.isExternalImport) {
|
||||
return this.$t('api_test.automation.external_import');
|
||||
} else if(this.isCustomizeReq) {
|
||||
} else if (this.isCustomizeReq) {
|
||||
return this.$t('api_test.automation.customize_req');
|
||||
}
|
||||
return "";
|
||||
},
|
||||
isApiImport() {
|
||||
if (this.request.referenced!=undefined && this.request.referenced==='Deleted' || this.request.referenced=='REF' || this.request.referenced==='Copy') {
|
||||
if (this.request.referenced != undefined && this.request.referenced === 'Deleted' || this.request.referenced == 'REF' || this.request.referenced === 'Copy') {
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isExternalImport() {
|
||||
if (this.request.referenced!=undefined && this.request.referenced==='OT_IMPORT') {
|
||||
if (this.request.referenced != undefined && this.request.referenced === 'OT_IMPORT') {
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isCustomizeReq() {
|
||||
if (this.request.referenced==undefined || this.request.referenced==='Created') {
|
||||
if (this.request.referenced == undefined || this.request.referenced === 'Created') {
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
},
|
||||
isDeletedOrRef() {
|
||||
if (this.request.referenced!= undefined && this.request.referenced === 'Deleted' || this.request.referenced === 'REF') {
|
||||
if (this.request.referenced != undefined && this.request.referenced === 'Deleted' || this.request.referenced === 'REF') {
|
||||
return true
|
||||
}
|
||||
return false;
|
||||
|
@ -218,6 +221,7 @@
|
|||
this.$error(this.$t('api_test.environment.select_environment'));
|
||||
return;
|
||||
}
|
||||
this.request.active = true;
|
||||
this.loading = true;
|
||||
this.runData = [];
|
||||
this.request.useEnvironment = this.currentEnvironmentId;
|
||||
|
@ -250,9 +254,11 @@
|
|||
margin-right: 20px;
|
||||
color: #409EFF;
|
||||
}
|
||||
|
||||
/deep/ .el-card__body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.tip {
|
||||
padding: 3px 5px;
|
||||
font-size: 16px;
|
||||
|
@ -260,12 +266,15 @@
|
|||
border-left: 4px solid #783887;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.name-input {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.el-icon-arrow-right {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.icon.is-active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
export default {
|
||||
name: "ApiResponseComponent",
|
||||
components: {ElCollapseTransition, MsRequestResultTail, ApiBaseComponent, MsRequestMetric},
|
||||
props: ['apiItem'],
|
||||
props: {apiItem: {}, result: {}},
|
||||
data() {
|
||||
return {
|
||||
isActive: false,
|
||||
|
@ -32,7 +32,15 @@
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.getExecResult();
|
||||
if (!this.result) {
|
||||
this.getExecResult();
|
||||
if (this.apiItem.isActive) {
|
||||
this.isActive = true;
|
||||
}
|
||||
} else {
|
||||
this.response = this.result;
|
||||
this.isActive = true;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getExecResult() {
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
:title="$t('api_test.automation.wait_controller')">
|
||||
|
||||
<template v-slot:headerLeft>
|
||||
<el-input-number class="time-input" size="small" v-model="timer.delay" :min="0" :step="1000"/> ms
|
||||
<el-input-number class="time-input" size="small" v-model="timer.delay" :min="0" :step="1000" ref="nameInput"/>
|
||||
ms
|
||||
</template>
|
||||
|
||||
</api-base-component>
|
||||
|
@ -18,6 +19,7 @@
|
|||
|
||||
<script>
|
||||
import ApiBaseComponent from "../common/ApiBaseComponent";
|
||||
|
||||
export default {
|
||||
name: "MsConstantTimer",
|
||||
components: {ApiBaseComponent},
|
||||
|
@ -32,6 +34,11 @@
|
|||
data() {
|
||||
return {}
|
||||
},
|
||||
created() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nameInput.focus();
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
remove() {
|
||||
this.$emit('remove', this.timer, this.node);
|
||||
|
|
|
@ -52,10 +52,10 @@
|
|||
},
|
||||
methods: {
|
||||
remove() {
|
||||
this.$emit('remove', this.jsr223ProcessorData, this.node);
|
||||
this.$emit('remove', this.jsr223Processor, this.node);
|
||||
},
|
||||
copyRow() {
|
||||
this.$emit('copyRow', this.jsr223ProcessorData, this.node);
|
||||
this.$emit('copyRow', this.jsr223Processor, this.node);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
editFlag: false,
|
||||
previewData: [],
|
||||
columns: [],
|
||||
allDatas: [],
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('api_test.variable_name'), trigger: 'blur'},
|
||||
|
@ -97,14 +98,20 @@
|
|||
this.$error(results.errors);
|
||||
return;
|
||||
}
|
||||
if (results.data) {
|
||||
this.columns = results.data[0];
|
||||
this.previewData = results.data;
|
||||
if (this.allDatas) {
|
||||
this.columns = this.allDatas[0];
|
||||
this.allDatas.splice(0, 1);
|
||||
this.previewData = this.allDatas;
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
step(results, parser) {
|
||||
this.allDatas.push(results.data);
|
||||
},
|
||||
|
||||
handleClick() {
|
||||
let config = {complete: this.complete};
|
||||
let config = {complete: this.complete, step: this.step};
|
||||
this.allDatas = [];
|
||||
// 本地文件
|
||||
if (this.editData.files && this.editData.files.length > 0 && this.editData.files[0].file) {
|
||||
this.loading = true;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-dialog :close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible"
|
||||
<el-dialog :close-on-click-modal="false" width="60%" class="schedule-edit" :visible.sync="dialogVisible" :append-to-body='true'
|
||||
@close="close">
|
||||
<template>
|
||||
<div>
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
<!-- 添加/编辑测试窗口-->
|
||||
<div v-else-if="item.type=== 'ADD'" class="ms-api-div">
|
||||
<ms-api-config @runTest="runTest" @saveApi="saveApi" @createRootModel="createRootModel" ref="apiConfig"
|
||||
<ms-api-config :syncTabs="syncTabs" @runTest="runTest" @saveApi="saveApi" @createRootModel="createRootModel" ref="apiConfig"
|
||||
:current-api="item.api"
|
||||
:currentProtocol="currentProtocol"
|
||||
:moduleOptions="moduleOptions"/>
|
||||
|
@ -76,13 +76,13 @@
|
|||
|
||||
<!-- 测试-->
|
||||
<div v-else-if="item.type=== 'TEST'" class="ms-api-div">
|
||||
<ms-run-test-http-page :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
<ms-run-test-http-page :syncTabs="syncTabs" :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
@refresh="refresh" v-if="currentProtocol==='HTTP'"/>
|
||||
<ms-run-test-tcp-page :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
<ms-run-test-tcp-page :syncTabs="syncTabs" :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
@refresh="refresh" v-if="currentProtocol==='TCP'"/>
|
||||
<ms-run-test-sql-page :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
<ms-run-test-sql-page :syncTabs="syncTabs" :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
@refresh="refresh" v-if="currentProtocol==='SQL'"/>
|
||||
<ms-run-test-dubbo-page :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
<ms-run-test-dubbo-page :syncTabs="syncTabs" :currentProtocol="currentProtocol" :api-data="item.api" @saveAsApi="editApi"
|
||||
@refresh="refresh" v-if="currentProtocol==='DUBBO'"/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
@ -184,7 +184,8 @@
|
|||
type: "list",
|
||||
closable: false
|
||||
}],
|
||||
isApiListEnable: true
|
||||
isApiListEnable: true,
|
||||
syncTabs: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -208,7 +209,7 @@
|
|||
},
|
||||
addTab(tab) {
|
||||
if (tab.name === 'add') {
|
||||
this.handleCommand("ADD");
|
||||
this.handleTabsEdit(this.$t('api_test.definition.request.fast_debug'), "debug");
|
||||
}
|
||||
},
|
||||
handleCommand(e) {
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
<div class="card-container">
|
||||
<!-- HTTP 请求参数 -->
|
||||
<ms-edit-complete-http-api @runTest="runTest" @saveApi="saveApi" @createRootModelInTree="createRootModelInTree" :request="request" :response="response"
|
||||
:basisData="currentApi" :moduleOptions="moduleOptions" v-if="currentProtocol === 'HTTP'"/>
|
||||
:basisData="currentApi" :moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'HTTP'"/>
|
||||
<!-- TCP -->
|
||||
<ms-edit-complete-tcp-api :request="request" @runTest="runTest" @createRootModelInTree="createRootModelInTree" @saveApi="saveApi" :basisData="currentApi"
|
||||
:moduleOptions="moduleOptions" v-if="currentProtocol === 'TCP'"/>
|
||||
:moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'TCP'"/>
|
||||
<!--DUBBO-->
|
||||
<ms-edit-complete-dubbo-api :request="request" @runTest="runTest" @createRootModelInTree="createRootModelInTree" @saveApi="saveApi" :basisData="currentApi"
|
||||
:moduleOptions="moduleOptions" v-if="currentProtocol === 'DUBBO'"/>
|
||||
:moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'DUBBO'"/>
|
||||
<!--SQL-->
|
||||
<ms-edit-complete-sql-api :request="request" @runTest="runTest" @createRootModelInTree="createRootModelInTree" @saveApi="saveApi" :basisData="currentApi"
|
||||
:moduleOptions="moduleOptions" v-if="currentProtocol === 'SQL'"/>
|
||||
:moduleOptions="moduleOptions" :syncTabs="syncTabs" v-if="currentProtocol === 'SQL'"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -45,6 +45,7 @@
|
|||
currentApi: {},
|
||||
moduleOptions: {},
|
||||
currentProtocol: String,
|
||||
syncTabs: Array,
|
||||
},
|
||||
created() {
|
||||
this.projectId = getCurrentProjectID();
|
||||
|
@ -75,7 +76,7 @@
|
|||
this.$emit('runTest', data);
|
||||
})
|
||||
},
|
||||
createRootModelInTree(){
|
||||
createRootModelInTree() {
|
||||
this.$emit("createRootModel");
|
||||
},
|
||||
getMaintainerOptions() {
|
||||
|
@ -168,10 +169,27 @@
|
|||
let bodyFiles = this.getBodyUploadFiles(data);
|
||||
this.$fileUpload(this.reqUrl, null, bodyFiles, data, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
if (this.reqUrl.endsWith('/create')) {
|
||||
this.saveTestCase(data);
|
||||
}
|
||||
this.reqUrl = "/api/definition/update";
|
||||
this.$emit('saveApi', data);
|
||||
});
|
||||
},
|
||||
saveTestCase(row) {
|
||||
let tmp = {request: JSON.parse(JSON.stringify(row.request))};
|
||||
tmp.projectId = getCurrentProjectID();
|
||||
tmp.active = true;
|
||||
tmp.priority = "P0";
|
||||
tmp.name = row.name;
|
||||
tmp.request.path = row.path;
|
||||
tmp.request.method = row.method;
|
||||
tmp.apiDefinitionId = row.id;
|
||||
let bodyFiles = this.getBodyUploadFiles(tmp);
|
||||
let url = "/api/testcase/create";
|
||||
this.$fileUpload(url, null, bodyFiles, tmp, (response) => {
|
||||
});
|
||||
},
|
||||
setParameters(data) {
|
||||
data.projectId = this.projectId;
|
||||
this.request.name = this.currentApi.name;
|
||||
|
@ -184,6 +202,9 @@
|
|||
data.request.protocol = this.currentProtocol;
|
||||
}
|
||||
data.id = data.request.id;
|
||||
if (!data.method) {
|
||||
data.method = this.currentProtocol;
|
||||
}
|
||||
data.response = this.response;
|
||||
},
|
||||
getBodyUploadFiles(data) {
|
||||
|
|
|
@ -50,16 +50,8 @@
|
|||
@setEnvironment="setEnvironment"/>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="1" v-if="!(isReadOnly || isCaseEdit)">
|
||||
<el-dropdown size="small" split-button type="primary" class="ms-api-header-select" @click="addCase"
|
||||
@command="handleCommand" v-tester>
|
||||
+{{$t('api_test.definition.request.case')}}
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="run">{{$t('api_test.automation.batch_execute')}}</el-dropdown-item>
|
||||
<el-dropdown-item command="batch_edit_case">{{$t('test_track.case.batch_edit_case')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<el-button size="small" type="primary" @click="addCase">+{{$t('api_test.definition.request.case')}}</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
@ -166,14 +158,6 @@
|
|||
addCase() {
|
||||
this.$emit('addCase');
|
||||
},
|
||||
handleCommand(e) {
|
||||
if (e === "run") {
|
||||
this.$emit('batchRun');
|
||||
}
|
||||
if (e === 'batch_edit_case') {
|
||||
this.$emit('batchEditCase');
|
||||
}
|
||||
},
|
||||
getColor(enable, method) {
|
||||
if (enable) {
|
||||
return this.methodColorMap.get(method);
|
||||
|
|
|
@ -3,17 +3,23 @@
|
|||
<div @click="active(apiCase)">
|
||||
<el-row>
|
||||
<el-col :span="5">
|
||||
|
||||
<el-checkbox class="item-select" v-model="apiCase.selected"/>
|
||||
|
||||
<div class="el-step__icon is-text ms-api-col">
|
||||
<div class="el-step__icon-inner">{{ index + 1 }}</div>
|
||||
</div>
|
||||
|
||||
<label class="ms-api-label">{{ $t('test_track.case.priority') }}</label>
|
||||
<el-select size="small" v-model="apiCase.priority" class="ms-api-select" @change="changePriority(apiCase)">
|
||||
<el-option v-for="grd in priorities" :key="grd.id" :label="grd.name" :value="grd.id"/>
|
||||
</el-select>
|
||||
<el-row>
|
||||
<el-col :span="2" style="margin-top: 5px">
|
||||
<el-checkbox class="item-select" v-model="apiCase.selected"/>
|
||||
</el-col>
|
||||
<el-col :span="2" style="margin-top: 2px">
|
||||
<show-more-btn :is-show="apiCase.selected" :buttons="buttons"/>
|
||||
</el-col>
|
||||
<el-col :span="20">
|
||||
<div class="el-step__icon is-text ms-api-col">
|
||||
<div class="el-step__icon-inner">{{ index + 1 }}</div>
|
||||
</div>
|
||||
<label class="ms-api-label">{{ $t('test_track.case.priority') }}</label>
|
||||
<el-select size="small" v-model="apiCase.priority" class="ms-api-select" @change="changePriority(apiCase)">
|
||||
<el-option v-for="grd in priorities" :key="grd.id" :label="grd.name" :value="grd.id"/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
|
@ -40,7 +46,9 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :span="4">
|
||||
<ms-input-tag class="tag-item" :currentScenario="apiCase" ref="tag" @keyup.enter.native="saveTestCase(apiCase)"/>
|
||||
<div class="tag-item">
|
||||
<ms-input-tag :currentScenario="apiCase" ref="tag" @keyup.enter.native="saveTestCase(apiCase)"/>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="4">
|
||||
|
@ -112,6 +120,7 @@
|
|||
import MsRequestResultTail from "../response/RequestResultTail";
|
||||
import MsJmxStep from "../step/JmxStep";
|
||||
import ApiResponseComponent from "../../../automation/scenario/component/ApiResponseComponent";
|
||||
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
|
||||
|
||||
export default {
|
||||
name: "ApiCaseItem",
|
||||
|
@ -128,7 +137,8 @@
|
|||
MsDubboBasisParameters,
|
||||
MsApiExtendBtns,
|
||||
MsRequestResultTail,
|
||||
MsJmxStep
|
||||
MsJmxStep,
|
||||
ShowMoreBtn
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -144,6 +154,10 @@
|
|||
condition: {},
|
||||
responseData: {type: 'HTTP', responseResult: {}, subRequestResults: []},
|
||||
isShowInput: false,
|
||||
buttons: [
|
||||
{name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleRunBatch},
|
||||
{name: this.$t('test_track.case.batch_edit_case'), handleClick: this.handleEditBatch}
|
||||
],
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -170,6 +184,12 @@
|
|||
},
|
||||
watch: {},
|
||||
methods: {
|
||||
handleRunBatch() {
|
||||
this.$emit('batchRun');
|
||||
},
|
||||
handleEditBatch() {
|
||||
this.$emit('batchEditCase');
|
||||
},
|
||||
deleteCase(index, row) {
|
||||
this.$alert(this.$t('api_test.definition.request.delete_confirm') + ' ' + row.name + " ?", '', {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
|
@ -269,8 +289,9 @@
|
|||
return true;
|
||||
}
|
||||
},
|
||||
showExecResult(data) {
|
||||
this.$emit('showExecResult', data);
|
||||
showExecResult(item) {
|
||||
item.active = false;
|
||||
item.isActive = true;
|
||||
},
|
||||
getBodyUploadFiles(row) {
|
||||
let bodyUploadFiles = [];
|
||||
|
@ -324,7 +345,13 @@
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.ms-opt-btn {
|
||||
position: fixed;
|
||||
left: 60px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.tag-item {
|
||||
margin-right: 30px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
@getApiTest="getApiTest"
|
||||
@setEnvironment="setEnvironment"
|
||||
@addCase="addCase"
|
||||
@batchRun="batchRun"
|
||||
@selectAll="selectAll"
|
||||
@batchEditCase="batchEditCase"
|
||||
:condition="condition"
|
||||
:priorities="priorities"
|
||||
:apiCaseList="apiCaseList"
|
||||
|
@ -28,6 +26,8 @@
|
|||
@singleRun="singleRun"
|
||||
@copyCase="copyCase"
|
||||
@showExecResult="showExecResult"
|
||||
@batchEditCase="batchEditCase"
|
||||
@batchRun="batchRun"
|
||||
:environment="environment"
|
||||
:is-case-edit="isCaseEdit"
|
||||
:api="api"
|
||||
|
|
|
@ -53,12 +53,12 @@
|
|||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('commons.tag')" prop="tag">
|
||||
<ms-input-tag :currentScenario="basicForm" ref="tag"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="16">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="$t('commons.description')" prop="description">
|
||||
<el-input class="ms-http-textarea"
|
||||
v-model="basicForm.description"
|
||||
|
|
|
@ -44,8 +44,30 @@
|
|||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
syncTabs: Array,
|
||||
},
|
||||
watch: {
|
||||
syncTabs() {
|
||||
if (this.basisData && this.syncTabs && this.syncTabs.includes(this.basisData.id)) {
|
||||
// 标示接口在其他地方更新过,当前页面需要同步
|
||||
let url = "/api/definition/get/";
|
||||
this.$get(url + this.basisData.id, response => {
|
||||
if (response.data) {
|
||||
let request = JSON.parse(response.data.request);
|
||||
let index = this.syncTabs.findIndex(item => {
|
||||
if (item === this.basisData.id) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
this.syncTabs.splice(index, 1);
|
||||
Object.assign(this.request, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {validated: false}
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<div class="card-container">
|
||||
<el-card class="card-content" v-loading="httpForm.loading">
|
||||
|
||||
<el-form :model="httpForm" :rules="rule" ref="httpForm" :inline="true" label-position="right">
|
||||
<el-form :model="httpForm" :rules="rule" ref="httpForm" label-width="80px" label-position="right">
|
||||
<!-- 操作按钮 -->
|
||||
<div style="float: right;margin-right: 20px">
|
||||
<el-button type="primary" size="small" @click="saveApi">{{ $t('commons.save') }}</el-button>
|
||||
|
@ -33,29 +33,6 @@
|
|||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
|
||||
<el-select class="ms-http-select" size="small" v-model="httpForm.moduleId">
|
||||
<div v-if="moduleOptions.length>0">
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-option :key="0" :value="''">
|
||||
<div style="margin-left: 40px">
|
||||
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{
|
||||
$t('api_test.definition.select_comp.no_data')
|
||||
}},
|
||||
</span>
|
||||
<el-link type="primary" @click="createModules">{{
|
||||
$t('api_test.definition.select_comp.add_data')
|
||||
}}
|
||||
</el-link>
|
||||
</div>
|
||||
</el-option>
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('api_test.definition.request.responsible')" prop="userId">
|
||||
<el-select v-model="httpForm.userId"
|
||||
|
@ -70,7 +47,26 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-col :span="7">
|
||||
<el-form-item :label="$t('test_track.module.module')" prop="moduleId">
|
||||
<el-select class="ms-http-select" size="small" v-model="httpForm.moduleId">
|
||||
<div v-if="moduleOptions.length>0">
|
||||
<el-option v-for="item in moduleOptions" :key="item.id" :label="item.path" :value="item.id"/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-option :key="0" :value="''">
|
||||
<div style="margin-left: 40px">
|
||||
<span style="font-size: 14px;color: #606266;font-weight: 48.93">{{ $t('api_test.definition.select_comp.no_data') }},
|
||||
</span>
|
||||
<el-link type="primary" @click="createModules">{{ $t('api_test.definition.select_comp.add_data') }}</el-link>
|
||||
</div>
|
||||
</el-option>
|
||||
</div>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="7">
|
||||
<el-form-item :label="$t('commons.status')" prop="status">
|
||||
<el-select class="ms-http-select" size="small" v-model="httpForm.status">
|
||||
<el-option v-for="item in options" :key="item.id" :label="item.label" :value="item.id"/>
|
||||
|
@ -78,7 +74,6 @@
|
|||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('commons.tag')" prop="tag">
|
||||
|
@ -100,8 +95,7 @@
|
|||
<!-- 请求参数 -->
|
||||
<div>
|
||||
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
|
||||
<ms-api-request-form :showScript="false" :request="request" :headers="request.headers"
|
||||
:isShowEnable="isShowEnable"/>
|
||||
<ms-api-request-form :showScript="false" :request="request" :headers="request.headers" :isShowEnable="isShowEnable"/>
|
||||
</div>
|
||||
|
||||
</el-form>
|
||||
|
@ -115,167 +109,189 @@
|
|||
|
||||
<script>
|
||||
|
||||
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
|
||||
import MsResponseText from "../response/ResponseText";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
|
||||
import {KeyValue} from "../../model/ApiTestModel";
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
|
||||
import MsResponseText from "../response/ResponseText";
|
||||
import {WORKSPACE_ID} from '../../../../../../common/js/constants';
|
||||
import {API_STATUS, REQ_METHOD} from "../../model/JsonData";
|
||||
import {KeyValue} from "../../model/ApiTestModel";
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
|
||||
export default {
|
||||
name: "MsAddCompleteHttpApi",
|
||||
components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag},
|
||||
data() {
|
||||
let validateURL = (rule, value, callback) => {
|
||||
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
|
||||
callback(this.$t('api_test.definition.request.path_valid_info'));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
|
||||
],
|
||||
path: [{required: true, message: this.$t('api_test.definition.request.path_info'), trigger: 'blur'}, {
|
||||
validator: validateURL,
|
||||
trigger: 'blur'
|
||||
}],
|
||||
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
|
||||
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
|
||||
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
|
||||
},
|
||||
httpForm: {environmentId: "", tags: []},
|
||||
isShowEnable: false,
|
||||
maintainerOptions: [],
|
||||
currentModule: {},
|
||||
reqOptions: REQ_METHOD,
|
||||
options: API_STATUS,
|
||||
}
|
||||
},
|
||||
props: {moduleOptions: {}, request: {}, response: {}, basisData: {}},
|
||||
methods: {
|
||||
runTest() {
|
||||
this.$refs['httpForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.setParameter();
|
||||
this.$emit('runTest', this.httpForm);
|
||||
} else {
|
||||
return false;
|
||||
export default {
|
||||
name: "MsAddCompleteHttpApi",
|
||||
components: {MsJsr233Processor, MsResponseText, MsApiRequestForm, MsInputTag},
|
||||
data() {
|
||||
let validateURL = (rule, value, callback) => {
|
||||
if (!this.httpForm.path.startsWith("/") || this.httpForm.path.match(/\s/) != null) {
|
||||
callback(this.$t('api_test.definition.request.path_valid_info'));
|
||||
}
|
||||
})
|
||||
},
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
},
|
||||
setParameter() {
|
||||
this.httpForm.modulePath = this.getPath(this.httpForm.moduleId);
|
||||
this.request.path = this.httpForm.path;
|
||||
this.request.method = this.httpForm.method;
|
||||
this.httpForm.request.useEnvironment = undefined;
|
||||
if (this.httpForm.tags instanceof Array) {
|
||||
this.httpForm.tags = JSON.stringify(this.httpForm.tags);
|
||||
callback();
|
||||
};
|
||||
return {
|
||||
rule: {
|
||||
name: [
|
||||
{required: true, message: this.$t('test_track.case.input_name'), trigger: 'blur'},
|
||||
{max: 50, message: this.$t('test_track.length_less_than') + '50', trigger: 'blur'}
|
||||
],
|
||||
path: [{required: true, message: this.$t('api_test.definition.request.path_info'), trigger: 'blur'}, {
|
||||
validator: validateURL,
|
||||
trigger: 'blur'
|
||||
}],
|
||||
userId: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
|
||||
moduleId: [{required: true, message: this.$t('test_track.case.input_module'), trigger: 'change'}],
|
||||
status: [{required: true, message: this.$t('commons.please_select'), trigger: 'change'}],
|
||||
},
|
||||
httpForm: {environmentId: "", tags: []},
|
||||
isShowEnable: false,
|
||||
maintainerOptions: [],
|
||||
currentModule: {},
|
||||
reqOptions: REQ_METHOD,
|
||||
options: API_STATUS,
|
||||
}
|
||||
},
|
||||
saveApi() {
|
||||
this.$refs['httpForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.setParameter();
|
||||
this.$emit('saveApi', this.httpForm);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
createModules() {
|
||||
this.$emit("createRootModelInTree");
|
||||
},
|
||||
getPath(id) {
|
||||
if (id === null) {
|
||||
return null;
|
||||
}
|
||||
let path = this.moduleOptions.filter(function (item) {
|
||||
return item.id === id ? item.path : "";
|
||||
});
|
||||
return path[0].path;
|
||||
},
|
||||
urlChange() {
|
||||
if (!this.httpForm.path || this.httpForm.path.indexOf('?') === -1) return;
|
||||
let url = this.getURL(this.addProtocol(this.httpForm.path));
|
||||
if (url) {
|
||||
this.httpForm.path = decodeURIComponent(this.httpForm.path.substr(0, this.httpForm.path.indexOf("?")));
|
||||
}
|
||||
},
|
||||
addProtocol(url) {
|
||||
if (url) {
|
||||
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
|
||||
return "https://" + url;
|
||||
props: {moduleOptions: {}, request: {}, response: {}, basisData: {}, syncTabs: Array},
|
||||
watch: {
|
||||
syncTabs() {
|
||||
if (this.basisData && this.syncTabs && this.syncTabs.includes(this.basisData.id)) {
|
||||
// 标示接口在其他地方更新过,当前页面需要同步
|
||||
let url = "/api/definition/get/";
|
||||
this.$get(url + this.basisData.id, response => {
|
||||
if (response.data) {
|
||||
let request = JSON.parse(response.data.request);
|
||||
let index = this.syncTabs.findIndex(item => {
|
||||
if (item === this.basisData.id) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
this.syncTabs.splice(index, 1);
|
||||
this.httpForm.path = response.data.path;
|
||||
this.httpForm.method = response.data.method;
|
||||
Object.assign(this.request, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return url;
|
||||
},
|
||||
getURL(urlStr) {
|
||||
try {
|
||||
let url = new URL(urlStr);
|
||||
url.searchParams.forEach((value, key) => {
|
||||
if (key && value) {
|
||||
this.request.arguments.splice(0, 0, new KeyValue({name: key, required: false, value: value}));
|
||||
methods: {
|
||||
runTest() {
|
||||
this.$refs['httpForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.setParameter();
|
||||
this.$emit('runTest', this.httpForm);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
this.maintainerOptions = response.data;
|
||||
});
|
||||
},
|
||||
setParameter() {
|
||||
this.httpForm.modulePath = this.getPath(this.httpForm.moduleId);
|
||||
this.request.path = this.httpForm.path;
|
||||
this.request.method = this.httpForm.method;
|
||||
this.httpForm.request.useEnvironment = undefined;
|
||||
if (this.httpForm.tags instanceof Array) {
|
||||
this.httpForm.tags = JSON.stringify(this.httpForm.tags);
|
||||
}
|
||||
},
|
||||
saveApi() {
|
||||
this.$refs['httpForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.setParameter();
|
||||
this.$emit('saveApi', this.httpForm);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
},
|
||||
createModules() {
|
||||
this.$emit("createRootModelInTree");
|
||||
},
|
||||
getPath(id) {
|
||||
if (id === null) {
|
||||
return null;
|
||||
}
|
||||
let path = this.moduleOptions.filter(function (item) {
|
||||
return item.id === id ? item.path : "";
|
||||
});
|
||||
return path[0].path;
|
||||
},
|
||||
urlChange() {
|
||||
if (!this.httpForm.path || this.httpForm.path.indexOf('?') === -1) return;
|
||||
let url = this.getURL(this.addProtocol(this.httpForm.path));
|
||||
if (url) {
|
||||
this.httpForm.path = decodeURIComponent(this.httpForm.path.substr(0, this.httpForm.path.indexOf("?")));
|
||||
}
|
||||
},
|
||||
addProtocol(url) {
|
||||
if (url) {
|
||||
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
|
||||
return "https://" + url;
|
||||
}
|
||||
}
|
||||
return url;
|
||||
} catch (e) {
|
||||
this.$error(this.$t('api_test.request.url_invalid'), 2000);
|
||||
}
|
||||
},
|
||||
getURL(urlStr) {
|
||||
try {
|
||||
let url = new URL(urlStr);
|
||||
url.searchParams.forEach((value, key) => {
|
||||
if (key && value) {
|
||||
this.request.arguments.splice(0, 0, new KeyValue({name: key, required: false, value: value}));
|
||||
}
|
||||
});
|
||||
return url;
|
||||
} catch (e) {
|
||||
this.$error(this.$t('api_test.request.url_invalid'), 2000);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
this.getMaintainerOptions();
|
||||
if (!this.basisData.environmentId) {
|
||||
this.basisData.environmentId = "";
|
||||
created() {
|
||||
this.getMaintainerOptions();
|
||||
if (!this.basisData.environmentId) {
|
||||
this.basisData.environmentId = "";
|
||||
}
|
||||
|
||||
this.httpForm = JSON.parse(JSON.stringify(this.basisData));
|
||||
}
|
||||
|
||||
this.httpForm = JSON.parse(JSON.stringify(this.basisData));
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.base-info .el-form-item {
|
||||
width: 100%;
|
||||
}
|
||||
.base-info .el-form-item {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.base-info .el-form-item >>> .el-form-item__content {
|
||||
width: 80%;
|
||||
}
|
||||
.base-info .el-form-item >>> .el-form-item__content {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.base-info .ms-http-select {
|
||||
width: 100%;
|
||||
}
|
||||
.base-info .ms-http-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tip {
|
||||
padding: 3px 5px;
|
||||
font-size: 16px;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #783887;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.tip {
|
||||
padding: 3px 5px;
|
||||
font-size: 16px;
|
||||
border-radius: 4px;
|
||||
border-left: 4px solid #783887;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.ms-http-textarea {
|
||||
width: 400px;
|
||||
}
|
||||
.ms-http-textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ms-left-cell {
|
||||
margin-top: 100px;
|
||||
}
|
||||
.ms-left-cell {
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
.ms-left-buttion {
|
||||
margin: 6px 0px 8px 30px;
|
||||
}
|
||||
.ms-left-buttion {
|
||||
margin: 6px 0px 8px 30px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -43,9 +43,29 @@ export default {
|
|||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
syncTabs:{},
|
||||
},
|
||||
watch: {
|
||||
syncTabs() {
|
||||
if (this.basisData && this.syncTabs && this.syncTabs.includes(this.basisData.id)) {
|
||||
// 标示接口在其他地方更新过,当前页面需要同步
|
||||
let url = "/api/definition/get/";
|
||||
this.$get(url + this.basisData.id, response => {
|
||||
if (response.data) {
|
||||
let request = JSON.parse(response.data.request);
|
||||
let index = this.syncTabs.findIndex(item => {
|
||||
if (item === this.basisData.id) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
this.syncTabs.splice(index, 1);
|
||||
Object.assign(this.request, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {validated: false}
|
||||
},
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
<!-- 请求参数 -->
|
||||
<p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
|
||||
<ms-basis-parameters :request="request"/>
|
||||
<ms-basis-parameters :show-script="false" :request="request"/>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -42,14 +42,34 @@ export default {
|
|||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
syncTabs:Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
validated: false,
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
syncTabs() {
|
||||
if (this.basisData && this.syncTabs && this.syncTabs.includes(this.basisData.id)) {
|
||||
// 标示接口在其他地方更新过,当前页面需要同步
|
||||
let url = "/api/definition/get/";
|
||||
this.$get(url + this.basisData.id, response => {
|
||||
if (response.data) {
|
||||
let request = JSON.parse(response.data.request);
|
||||
let index = this.syncTabs.findIndex(item => {
|
||||
if (item === this.basisData.id) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
this.syncTabs.splice(index, 1);
|
||||
Object.assign(this.request, request);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
callback() {
|
||||
this.validated = true;
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
|
||||
{{$t('commons.test')}}
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
|
@ -43,7 +43,7 @@
|
|||
|
||||
export default {
|
||||
name: "ApiConfig",
|
||||
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters,MsJmxStep},
|
||||
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters, MsJmxStep},
|
||||
props: {
|
||||
currentProtocol: String,
|
||||
scenario: Boolean,
|
||||
|
@ -119,6 +119,7 @@
|
|||
},
|
||||
saveAs() {
|
||||
let obj = {request: this.request};
|
||||
obj.request.id = getUUID();
|
||||
this.$emit('saveAs', obj);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
@command="handleCommand" size="small" v-if="testCase===undefined && !scenario">
|
||||
{{$t('commons.test')}}
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-form-item>
|
||||
|
@ -58,7 +58,7 @@
|
|||
|
||||
export default {
|
||||
name: "ApiConfig",
|
||||
components: {MsRequestResultTail, MsResponseResult, MsApiRequestForm, MsRequestMetric, MsResponseText, MsRun,MsJmxStep},
|
||||
components: {MsRequestResultTail, MsResponseResult, MsApiRequestForm, MsRequestMetric, MsResponseText, MsRun, MsJmxStep},
|
||||
props: {
|
||||
currentProtocol: String,
|
||||
testCase: {},
|
||||
|
@ -155,6 +155,8 @@
|
|||
saveAs() {
|
||||
this.$refs['debugForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.debugForm.id = null;
|
||||
this.request.id = getUUID();
|
||||
this.debugForm.request = this.request;
|
||||
this.debugForm.userId = getCurrentUser().id;
|
||||
this.debugForm.status = "Underway";
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
|
||||
{{$t('commons.test')}}
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
|
@ -122,6 +122,7 @@
|
|||
},
|
||||
saveAs() {
|
||||
let obj = {request: this.request};
|
||||
obj.request.id = getUUID();
|
||||
this.$emit('saveAs', obj);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
|
||||
{{$t('commons.test')}}
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-form-item>
|
||||
|
@ -131,6 +131,7 @@
|
|||
},
|
||||
saveAs() {
|
||||
let obj = {request: this.request};
|
||||
obj.request.id = getUUID();
|
||||
this.$emit('saveAs', obj);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<el-form class="tcp" :model="config" :rules="rules" ref="config" label-width="120px" :disabled="isReadOnly"
|
||||
size="small">
|
||||
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="16">
|
||||
<el-form-item :label="$t('api_test.request.tcp.server')" prop="server">
|
||||
<el-input v-model="config.server" maxlength="300" show-word-limit/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('api_test.request.tcp.port')" prop="port" label-width="60px">
|
||||
<el-input-number v-model="config.port" controls-position="right" :min="0" :max="65535" :controls="false"/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
|
||||
name: "EnvironmentTcpConfig",
|
||||
props: {
|
||||
config: {},
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rules: {}
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tcp >>> .el-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
|
@ -51,6 +51,14 @@
|
|||
:label="$t('api_test.definition.api_path')"
|
||||
show-overflow-tooltip/>
|
||||
|
||||
<el-table-column prop="tags" :label="$t('commons.tag')">
|
||||
<template v-slot:default="scope">
|
||||
<div v-for="(itemName,index) in scope.row.tags" :key="index">
|
||||
<ms-tag type="success" effect="plain" :content="itemName"/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="createUser"
|
||||
:label="'创建人'"
|
||||
|
@ -100,35 +108,34 @@
|
|||
|
||||
<script>
|
||||
|
||||
import MsTableOperator from "../../../../common/components/MsTableOperator";
|
||||
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
|
||||
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
||||
import MsTablePagination from "../../../../common/pagination/TablePagination";
|
||||
import MsTag from "../../../../common/components/MsTag";
|
||||
import MsApiCaseList from "../case/ApiCaseList";
|
||||
import MsContainer from "../../../../common/components/MsContainer";
|
||||
import MsBottomContainer from "../BottomContainer";
|
||||
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
|
||||
import MsBatchEdit from "../basis/BatchEdit";
|
||||
import {API_METHOD_COLOUR, CASE_PRIORITY, REQ_METHOD} from "../../model/JsonData";
|
||||
import MsTableOperator from "../../../../common/components/MsTableOperator";
|
||||
import MsTableOperatorButton from "../../../../common/components/MsTableOperatorButton";
|
||||
import MsTablePagination from "../../../../common/pagination/TablePagination";
|
||||
import MsTag from "../../../../common/components/MsTag";
|
||||
import MsApiCaseList from "../case/ApiCaseList";
|
||||
import ApiCaseList from "../case/ApiCaseList";
|
||||
import MsContainer from "../../../../common/components/MsContainer";
|
||||
import MsBottomContainer from "../BottomContainer";
|
||||
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
|
||||
import MsBatchEdit from "../basis/BatchEdit";
|
||||
import {API_METHOD_COLOUR, CASE_PRIORITY, REQ_METHOD} from "../../model/JsonData";
|
||||
|
||||
import {getBodyUploadFiles, getCurrentProjectID} from "@/common/js/utils";
|
||||
import ApiListContainer from "./ApiListContainer";
|
||||
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
|
||||
import ApiCaseList from "../case/ApiCaseList";
|
||||
import {_filter, _sort} from "../../../../../../common/js/utils";
|
||||
import {_handleSelect, _handleSelectAll} from "../../../../../../common/js/tableUtils";
|
||||
import MsApiCaseTableExtendBtns from "../reference/ApiCaseTableExtendBtns";
|
||||
import MsReferenceView from "../reference/ReferenceView";
|
||||
import MsSetEnvironment from "@/business/components/api/definition/components/basis/SetEnvironment";
|
||||
import TestPlan from "@/business/components/api/definition/components/jmeter/components/test-plan";
|
||||
import ThreadGroup from "@/business/components/api/definition/components/jmeter/components/thread-group";
|
||||
import {parseEnvironment} from "@/business/components/api/test/model/EnvironmentModel";
|
||||
import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll";
|
||||
import MsTableAdvSearchBar from "@/business/components/common/components/search/MsTableAdvSearchBar";
|
||||
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
|
||||
import {getBodyUploadFiles, getCurrentProjectID} from "@/common/js/utils";
|
||||
import ApiListContainer from "./ApiListContainer";
|
||||
import PriorityTableItem from "../../../../track/common/tableItems/planview/PriorityTableItem";
|
||||
import {_filter, _sort} from "../../../../../../common/js/utils";
|
||||
import {_handleSelect, _handleSelectAll} from "../../../../../../common/js/tableUtils";
|
||||
import MsApiCaseTableExtendBtns from "../reference/ApiCaseTableExtendBtns";
|
||||
import MsReferenceView from "../reference/ReferenceView";
|
||||
import MsSetEnvironment from "@/business/components/api/definition/components/basis/SetEnvironment";
|
||||
import TestPlan from "@/business/components/api/definition/components/jmeter/components/test-plan";
|
||||
import ThreadGroup from "@/business/components/api/definition/components/jmeter/components/thread-group";
|
||||
import {parseEnvironment} from "@/business/components/api/test/model/EnvironmentModel";
|
||||
import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll";
|
||||
import MsTableAdvSearchBar from "@/business/components/common/components/search/MsTableAdvSearchBar";
|
||||
import {API_CASE_CONFIGS} from "@/business/components/common/components/search/search-components";
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: "ApiCaseSimpleList",
|
||||
components: {
|
||||
MsTableSelectAll,
|
||||
|
@ -290,6 +297,12 @@
|
|||
this.total = response.data.itemCount;
|
||||
this.tableData = response.data.listObject;
|
||||
this.unSelection = response.data.listObject.map(s => s.id);
|
||||
|
||||
this.tableData.forEach(item => {
|
||||
if (item.tags && item.tags.length > 0) {
|
||||
item.tags = JSON.parse(item.tags);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
<el-button-group v-if="isShowChangeButton">
|
||||
|
||||
<el-tooltip class="item" effect="dark" content="接口列表" placement="left">
|
||||
<el-button plain size="small" icon="el-icon-tickets" :class="{active: isApiListEnable}" @click="apiChange('api')"></el-button>
|
||||
<el-button plain style="width: 44px;height: 32px;padding: 5px 8px;" :class="{active: isApiListEnable}" @click="apiChange('api')">API</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-tooltip class="item" effect="dark" content="用例列表" placement="right">
|
||||
<el-button plain class="case-button" size="small" icon="el-icon-paperclip" :class="{active: !isApiListEnable}" @click="caseChange('case')"></el-button>
|
||||
<el-button plain class="case-button" style="width: 44px;height: 32px;padding: 1px;" :class="{active: !isApiListEnable}" @click="caseChange('case')">CASE</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
</el-button-group>
|
||||
|
|
|
@ -126,6 +126,9 @@
|
|||
buildNodePath(node, {path: ''}, moduleOptions);
|
||||
});
|
||||
this.$emit('setModuleOptions', moduleOptions);
|
||||
if (this.$refs.nodeTree) {
|
||||
this.$refs.nodeTree.filter(this.condition.filterText);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<el-col :span="3" class="ms-left-cell">
|
||||
<el-button class="ms-left-buttion" size="small" @click="addPre">+{{$t('api_test.definition.request.pre_script')}}</el-button>
|
||||
<br/>
|
||||
<el-button class="ms-left-buttion" size="small" @click="addPost">+{{$t('api_test.definition.request.post_script')}}</el-button>
|
||||
<br/>
|
||||
<el-button class="ms-left-buttion" size="small" @click="addAssertions">+{{$t('api_test.definition.request.assertions_rule')}}</el-button>
|
||||
<br/>
|
||||
<el-button class="ms-left-buttion" size="small" @click="addExtract">+{{$t('api_test.definition.request.extract_param')}}</el-button>
|
||||
</el-col>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {createComponent} from "../../jmeter/components";
|
||||
import {Assertions, Extract} from "../../../model/ApiTestModel";
|
||||
|
||||
export default {
|
||||
name: "ApiDefinitionStepButton",
|
||||
props: {
|
||||
request: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addPre() {
|
||||
let jsr223PreProcessor = createComponent("JSR223PreProcessor");
|
||||
this.request.hashTree.push(jsr223PreProcessor);
|
||||
},
|
||||
addPost() {
|
||||
let jsr223PostProcessor = createComponent("JSR223PostProcessor");
|
||||
this.request.hashTree.push(jsr223PostProcessor);
|
||||
},
|
||||
addAssertions() {
|
||||
let assertions = new Assertions();
|
||||
this.request.hashTree.push(assertions);
|
||||
},
|
||||
addExtract() {
|
||||
let jsonPostProcessor = new Extract();
|
||||
this.request.hashTree.push(jsonPostProcessor);
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(1) {
|
||||
color: #B8741A;
|
||||
background-color: #F9F1EA;
|
||||
border: #F9F1EA;
|
||||
}
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(2) {
|
||||
color: #783887;
|
||||
background-color: #F2ECF3;
|
||||
border: #F2ECF3;
|
||||
}
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(3) {
|
||||
color: #A30014;
|
||||
background-color: #F7E6E9;
|
||||
border: #F7E6E9;
|
||||
}
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(4) {
|
||||
color: #015478;
|
||||
background-color: #E6EEF2;
|
||||
border: #E6EEF2;
|
||||
}
|
||||
|
||||
.ms-left-cell {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.ms-left-buttion {
|
||||
margin: 6px 0px 8px 30px;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -71,35 +71,9 @@
|
|||
|
||||
</el-tabs>
|
||||
</div>
|
||||
<!--<div v-if="!referenced">-->
|
||||
<!--<div v-for="row in request.hashTree" :key="row.id">-->
|
||||
<!--<!–前置脚本–>-->
|
||||
<!--<ms-jsr233-processor v-if="row.type==='JSR223PreProcessor'" @remove="remove" @copyRow="copyRow" :title="$t('api_test.definition.request.pre_script')"-->
|
||||
<!--:jsr223-processor="row"/>-->
|
||||
<!--<!–后置脚本–>-->
|
||||
<!--<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')"-->
|
||||
<!--:jsr223-processor="row"/>-->
|
||||
<!--<!–断言规则–>-->
|
||||
<!--<div style="margin-top: 10px">-->
|
||||
<!--<ms-api-assertions :response="response" v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>-->
|
||||
<!--</div>-->
|
||||
<!--<!–提取规则–>-->
|
||||
<!--<div style="margin-top: 10px">-->
|
||||
<!--<ms-api-extract :response="response" :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>-->
|
||||
<!--</div>-->
|
||||
<!--</div>-->
|
||||
<!--</div>-->
|
||||
</el-col>
|
||||
<!--操作按钮-->
|
||||
<el-col :span="3" class="ms-left-cell" v-if="!referenced && showScript">
|
||||
<el-button class="ms-left-buttion" size="small" @click="addPre">+{{$t('api_test.definition.request.pre_script')}}</el-button>
|
||||
<br/>
|
||||
<el-button class="ms-left-buttion" size="small" @click="addPost">+{{$t('api_test.definition.request.post_script')}}</el-button>
|
||||
<br/>
|
||||
<el-button class="ms-left-buttion" size="small" @click="addAssertions">+{{$t('api_test.definition.request.assertions_rule')}}</el-button>
|
||||
<br/>
|
||||
<el-button class="ms-left-buttion" size="small" @click="addExtract">+{{$t('api_test.definition.request.extract_param')}}</el-button>
|
||||
</el-col>
|
||||
<api-definition-step-button :request="request" v-if="!referenced && showScript"/>
|
||||
</el-row>
|
||||
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
|
||||
</div>
|
||||
|
@ -120,10 +94,12 @@
|
|||
import BatchAddParameter from "../../basis/BatchAddParameter";
|
||||
import MsApiAdvancedConfig from "./ApiAdvancedConfig";
|
||||
import MsJsr233Processor from "../../../../automation/scenario/component/Jsr233Processor";
|
||||
import ApiDefinitionStepButton from "../components/ApiDefinitionStepButton";
|
||||
|
||||
export default {
|
||||
name: "MsApiHttpRequestForm",
|
||||
components: {
|
||||
ApiDefinitionStepButton,
|
||||
MsJsr233Processor,
|
||||
MsApiAdvancedConfig,
|
||||
BatchAddParameter,
|
||||
|
@ -194,26 +170,6 @@
|
|||
},
|
||||
|
||||
methods: {
|
||||
addPre() {
|
||||
let jsr223PreProcessor = createComponent("JSR223PreProcessor");
|
||||
this.request.hashTree.push(jsr223PreProcessor);
|
||||
this.reload();
|
||||
},
|
||||
addPost() {
|
||||
let jsr223PostProcessor = createComponent("JSR223PostProcessor");
|
||||
this.request.hashTree.push(jsr223PostProcessor);
|
||||
this.reload();
|
||||
},
|
||||
addAssertions() {
|
||||
let assertions = new Assertions();
|
||||
this.request.hashTree.push(assertions);
|
||||
this.reload();
|
||||
},
|
||||
addExtract() {
|
||||
let jsonPostProcessor = new Extract();
|
||||
this.request.hashTree.push(jsonPostProcessor);
|
||||
this.reload();
|
||||
},
|
||||
remove(row) {
|
||||
let index = this.request.hashTree.indexOf(row);
|
||||
this.request.hashTree.splice(index, 1);
|
||||
|
@ -289,13 +245,6 @@
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ms-left-cell {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.ms-left-buttion {
|
||||
margin: 6px 0px 8px 30px;
|
||||
}
|
||||
|
||||
.ms-query {
|
||||
background: #783887;
|
||||
|
@ -316,30 +265,6 @@
|
|||
min-height: 200px;
|
||||
}
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(1) {
|
||||
color: #B8741A;
|
||||
background-color: #F9F1EA;
|
||||
border: #F9F1EA;
|
||||
}
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(2) {
|
||||
color: #783887;
|
||||
background-color: #F2ECF3;
|
||||
border: #F2ECF3;
|
||||
}
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(3) {
|
||||
color: #A30014;
|
||||
background-color: #F7E6E9;
|
||||
border: #F7E6E9;
|
||||
}
|
||||
|
||||
.ms-left-cell .el-button:nth-of-type(4) {
|
||||
color: #015478;
|
||||
background-color: #E6EEF2;
|
||||
border: #E6EEF2;
|
||||
}
|
||||
|
||||
.ms-el-link {
|
||||
float: right;
|
||||
margin-right: 45px;
|
||||
|
|
|
@ -101,30 +101,11 @@
|
|||
|
||||
</el-form>
|
||||
</div>
|
||||
<!--<div v-for="row in request.hashTree" :key="row.id" v-loading="isReloadData" style="margin-left: 20px;width: 100%">-->
|
||||
<!--<!– 前置脚本 –>-->
|
||||
<!--<ms-jsr233-processor v-if="row.label ==='JSR223 PreProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.pre_script')" style-type="color: #B8741A;background-color: #F9F1EA"-->
|
||||
<!--:jsr223-processor="row"/>-->
|
||||
<!--<!–后置脚本–>-->
|
||||
<!--<ms-jsr233-processor v-if="row.label ==='JSR223 PostProcessor'" @copyRow="copyRow" @remove="remove" :is-read-only="false" :title="$t('api_test.definition.request.post_script')" style-type="color: #783887;background-color: #F2ECF3"-->
|
||||
<!--:jsr223-processor="row"/>-->
|
||||
<!--<!–断言规则–>-->
|
||||
<!--<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>-->
|
||||
<!--<!–提取规则–>-->
|
||||
<!--<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>-->
|
||||
|
||||
<!--</div>-->
|
||||
</el-col>
|
||||
|
||||
<!--<el-col :span="3" class="ms-left-cell">-->
|
||||
<!--<el-button class="ms-left-buttion" size="small" style="color: #B8741A;background-color: #F9F1EA" @click="addPre">+{{$t('api_test.definition.request.pre_script')}}</el-button>-->
|
||||
<!--<br/>-->
|
||||
<!--<el-button class="ms-left-buttion" size="small" style="color: #783887;background-color: #F2ECF3" @click="addPost">+{{$t('api_test.definition.request.post_script')}}</el-button>-->
|
||||
<!--<br/>-->
|
||||
<!--<el-button class="ms-left-buttion" size="small" style="color: #A30014;background-color: #F7E6E9" @click="addAssertions">+{{$t('api_test.definition.request.assertions_rule')}}</el-button>-->
|
||||
<!--<br/>-->
|
||||
<!--<el-button class="ms-left-buttion" size="small" style="color: #015478;background-color: #E6EEF2" @click="addExtract">+{{$t('api_test.definition.request.extract_param')}}</el-button>-->
|
||||
<!--</el-col>-->
|
||||
<!--操作按钮-->
|
||||
<api-definition-step-button :request="request" v-if="!referenced && showScript"/>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -147,11 +128,13 @@
|
|||
import MsInstructionsIcon from "../../../../../common/components/MsInstructionsIcon";
|
||||
import Jsr233ProcessorContent from "../../../../automation/scenario/common/Jsr233ProcessorContent";
|
||||
import JSR223PreProcessor from "../../jmeter/components/pre-processors/jsr223-pre-processor";
|
||||
import ApiDefinitionStepButton from "../components/ApiDefinitionStepButton";
|
||||
|
||||
|
||||
export default {
|
||||
name: "TcpBasisParameters",
|
||||
components: {
|
||||
ApiDefinitionStepButton,
|
||||
Jsr233ProcessorContent,
|
||||
MsInstructionsIcon,
|
||||
MsApiVariable,
|
||||
|
@ -167,6 +150,14 @@
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showScript: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
referenced: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
reportId: "",
|
||||
}
|
||||
},
|
||||
props: {apiData: {}, currentProtocol: String,},
|
||||
props: {apiData: {}, currentProtocol: String,syncTabs: Array},
|
||||
methods: {
|
||||
handleCommand(e) {
|
||||
switch (e) {
|
||||
|
@ -173,6 +173,9 @@
|
|||
let bodyFiles = this.getBodyUploadFiles();
|
||||
this.$fileUpload(url, null, bodyFiles, this.api, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
if (this.syncTabs.indexOf(this.api.id) === -1) {
|
||||
this.syncTabs.push(this.api.id);
|
||||
}
|
||||
this.$emit('saveApi', this.api);
|
||||
});
|
||||
},
|
||||
|
@ -228,7 +231,8 @@
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.api = this.apiData;
|
||||
// 深度复制
|
||||
this.api = JSON.parse(JSON.stringify(this.apiData));
|
||||
this.api.protocol = this.currentProtocol;
|
||||
this.currentRequest = this.api.request;
|
||||
this.getEnvironments();
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
projectId: "",
|
||||
}
|
||||
},
|
||||
props: {apiData: {}, currentProtocol: String,},
|
||||
props: {apiData: {}, currentProtocol: String, syncTabs: Array},
|
||||
methods: {
|
||||
handleCommand(e) {
|
||||
switch (e) {
|
||||
|
@ -205,6 +205,9 @@
|
|||
this.$fileUpload(url, null, bodyFiles, this.api, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$emit('saveApi', this.api);
|
||||
if (this.syncTabs.indexOf(this.api.id) === -1) {
|
||||
this.syncTabs.push(this.api.id);
|
||||
}
|
||||
});
|
||||
},
|
||||
selectTestCase(item) {
|
||||
|
@ -230,7 +233,8 @@
|
|||
},
|
||||
created() {
|
||||
this.projectId = getCurrentProjectID();
|
||||
this.api = this.apiData;
|
||||
// 深度复制
|
||||
this.api = JSON.parse(JSON.stringify(this.apiData));
|
||||
this.api.protocol = this.currentProtocol;
|
||||
this.currentRequest = this.api.request;
|
||||
this.getResult();
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
reportId: "",
|
||||
}
|
||||
},
|
||||
props: {apiData: {}, currentProtocol: String,},
|
||||
props: {apiData: {}, currentProtocol: String,syncTabs: Array},
|
||||
methods: {
|
||||
handleCommand(e) {
|
||||
switch (e) {
|
||||
|
@ -172,6 +172,9 @@
|
|||
let bodyFiles = this.getBodyUploadFiles();
|
||||
this.$fileUpload(url, null, bodyFiles, this.api, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
if (this.syncTabs.indexOf(this.api.id) === -1) {
|
||||
this.syncTabs.push(this.api.id);
|
||||
}
|
||||
this.$emit('saveApi', this.api);
|
||||
});
|
||||
},
|
||||
|
@ -227,7 +230,8 @@
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.api = this.apiData;
|
||||
// 深度复制
|
||||
this.api = JSON.parse(JSON.stringify(this.apiData));
|
||||
this.api.protocol = this.currentProtocol;
|
||||
this.currentRequest = this.api.request;
|
||||
this.getEnvironments();
|
||||
|
|
|
@ -2,34 +2,43 @@
|
|||
|
||||
<div class="card-container">
|
||||
<el-card class="card-content" v-loading="loading">
|
||||
<!-- 操作按钮 -->
|
||||
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand('add')"
|
||||
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
|
||||
{{$t('commons.test')}}
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="load_case">{{$t('api_test.definition.request.load_case')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as_case">{{$t('api_test.definition.request.save_as_case')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="update_api">{{$t('api_test.definition.request.update_api')}}</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as_api">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<p class="tip">{{$t('test_track.plan_view.base_info')}} </p>
|
||||
<!-- 执行环境 -->
|
||||
{{$t('api_test.definition.request.run_env')}}:
|
||||
<environment-select :type="'TCP'" :current-data="api" :project-id="projectId"/>
|
||||
<el-form :model="api" :rules="rules" ref="apiData" :inline="true" label-position="right">
|
||||
|
||||
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
|
||||
<!-- TCP 请求参数 -->
|
||||
<ms-basis-parameters :request="api.request" @callback="runTest" ref="requestForm"/>
|
||||
<!-- 操作按钮 -->
|
||||
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand('add')"
|
||||
@command="handleCommand" size="small" style="float: right;margin-right: 20px">
|
||||
{{$t('commons.test')}}
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="load_case">{{$t('api_test.definition.request.load_case')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as_case">{{$t('api_test.definition.request.save_as_case')}}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="update_api">{{$t('api_test.definition.request.update_api')}}</el-dropdown-item>
|
||||
<el-dropdown-item command="save_as_api">{{$t('api_test.definition.request.save_as')}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<p class="tip">{{$t('test_track.plan_view.base_info')}} </p>
|
||||
<!-- 执行环境 -->
|
||||
<el-form-item prop="environmentId">
|
||||
{{$t('api_test.definition.request.run_env')}}:
|
||||
<environment-select :type="'TCP'" :current-data="api" :project-id="projectId"/>
|
||||
</el-form-item>
|
||||
|
||||
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p>
|
||||
<!-- TCP 请求参数 -->
|
||||
<ms-basis-parameters :request="api.request" @callback="runTest" ref="requestForm"/>
|
||||
|
||||
</el-form>
|
||||
|
||||
<!--返回结果-->
|
||||
<!-- HTTP 请求返回数据 -->
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
<ms-request-result-tail :response="responseData" ref="runResult"/>
|
||||
|
||||
<ms-jmx-step :request="api.request" :response="responseData"/>
|
||||
|
||||
</el-card>
|
||||
|
||||
<!-- 加载用例 -->
|
||||
|
@ -55,10 +64,12 @@
|
|||
import MsBasisParameters from "../request/tcp/TcpBasisParameters";
|
||||
import {REQ_METHOD} from "../../model/JsonData";
|
||||
import EnvironmentSelect from "../environment/EnvironmentSelect";
|
||||
import MsJmxStep from "../step/JmxStep";
|
||||
|
||||
export default {
|
||||
name: "RunTestTCPPage",
|
||||
components: {
|
||||
MsJmxStep,
|
||||
EnvironmentSelect,
|
||||
MsApiRequestForm,
|
||||
MsApiCaseList,
|
||||
|
@ -81,8 +92,6 @@
|
|||
refreshSign: "",
|
||||
createCase: "",
|
||||
rules: {
|
||||
method: [{required: true, message: this.$t('test_track.case.input_maintainer'), trigger: 'change'}],
|
||||
url: [{required: true, message: this.$t('api_test.definition.request.path_info'), trigger: 'blur'}],
|
||||
environmentId: [{required: true, message: this.$t('api_test.definition.request.run_env'), trigger: 'change'}],
|
||||
},
|
||||
runData: [],
|
||||
|
@ -90,7 +99,7 @@
|
|||
projectId: ""
|
||||
}
|
||||
},
|
||||
props: {apiData: {}, currentProtocol: String,},
|
||||
props: {apiData: {}, currentProtocol: String,syncTabs: Array},
|
||||
methods: {
|
||||
handleCommand(e) {
|
||||
switch (e) {
|
||||
|
@ -110,13 +119,17 @@
|
|||
this.$emit('refresh');
|
||||
},
|
||||
runTest() {
|
||||
this.loading = true;
|
||||
this.api.request.name = this.api.id;
|
||||
this.api.protocol = this.currentProtocol;
|
||||
this.runData = [];
|
||||
this.runData.push(this.api.request);
|
||||
/*触发执行操作*/
|
||||
this.reportId = getUUID().substring(0, 8);
|
||||
this.$refs['apiData'].validate((valid) => {
|
||||
if (valid) {
|
||||
this.loading = true;
|
||||
this.api.request.name = this.api.id;
|
||||
this.api.protocol = this.currentProtocol;
|
||||
this.runData = [];
|
||||
this.runData.push(this.api.request);
|
||||
/*触发执行操作*/
|
||||
this.reportId = getUUID().substring(0, 8);
|
||||
}
|
||||
})
|
||||
},
|
||||
runRefresh(data) {
|
||||
this.responseData = data;
|
||||
|
@ -173,6 +186,9 @@
|
|||
let bodyFiles = this.getBodyUploadFiles();
|
||||
this.$fileUpload(url, null, bodyFiles, this.api, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
if (this.syncTabs.indexOf(this.api.id) === -1) {
|
||||
this.syncTabs.push(this.api.id);
|
||||
}
|
||||
this.$emit('saveApi', this.api);
|
||||
});
|
||||
},
|
||||
|
@ -194,7 +210,8 @@
|
|||
}
|
||||
},
|
||||
created() {
|
||||
this.api = this.apiData;
|
||||
// 深度复制
|
||||
this.api = JSON.parse(JSON.stringify(this.apiData));
|
||||
this.api.protocol = this.currentProtocol;
|
||||
this.currentRequest = this.api.request;
|
||||
this.projectId = getCurrentProjectID();
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div>
|
||||
<div v-if="request.hashTree && request.hashTree.length > 0">
|
||||
<p class="tip">
|
||||
{{$t('test_track.plan_view.step')}}
|
||||
</p>
|
||||
<!-- HTTP 请求参数 -->
|
||||
<div style="border:1px #DCDFE6 solid; height: 100%;border-radius: 4px ;width: 100%" v-loading="isReloadData" v-if="request.hashTree && request.hashTree.length>0">
|
||||
<div style="height: 100%;border-radius: 4px ;width: 100%" v-loading="isReloadData" v-if="request.hashTree && request.hashTree.length>0">
|
||||
|
||||
<div v-for="row in request.hashTree" :key="row.id">
|
||||
<!--前置脚本-->
|
||||
|
|
|
@ -7,13 +7,19 @@
|
|||
</template>
|
||||
<el-table border :data="tableData" class="adjust-table table-content" height="300px">
|
||||
<el-table-column prop="index" :label="$t('api_test.home_page.running_task_list.table_coloum.index')" width="80" show-overflow-tooltip/>
|
||||
<el-table-column prop="scenario" :label="$t('api_test.home_page.running_task_list.table_coloum.scenario')" width="200" >
|
||||
<el-table-column prop="name" :label="$t('commons.name')" width="200" >
|
||||
<template v-slot:default="{row}">
|
||||
<el-link type="info" @click="redirect(row)">
|
||||
{{ row.scenario }}
|
||||
{{ row.name }}
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="taskType" :label="$t('api_test.home_page.running_task_list.table_coloum.task_type')" width="120" show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<ms-tag v-if="scope.row.taskType == 'scenario'" type="success" effect="plain" :content="$t('api_test.home_page.running_task_list.scenario_schedule')"/>
|
||||
<ms-tag v-if="scope.row.taskType == 'testPlan'" type="warning" effect="plain" :content="$t('api_test.home_page.running_task_list.test_plan_schedule')"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="rule" :label="$t('api_test.home_page.running_task_list.table_coloum.run_rule')" width="120" show-overflow-tooltip/>
|
||||
<el-table-column width="100" :label="$t('api_test.home_page.running_task_list.table_coloum.task_status')">
|
||||
<template v-slot:default="scope">
|
||||
|
@ -46,9 +52,12 @@
|
|||
|
||||
<script>
|
||||
import {getCurrentProjectID} from "@/common/js/utils";
|
||||
import MsTag from "@/business/components/common/components/MsTag";
|
||||
export default {
|
||||
name: "MsRunningTaskList",
|
||||
|
||||
components: {
|
||||
MsTag
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
|
|
@ -9,13 +9,12 @@
|
|||
|
||||
<script>
|
||||
import MsDialogFooter from "../../../../common/components/MsDialogFooter";
|
||||
import ApiEnvironmentConfig from "../ApiEnvironmentConfig";
|
||||
import {listenGoBack, removeGoBackListener} from "../../../../../../common/js/utils";
|
||||
import MsJarConfigList from "./JarConfigList";
|
||||
import MsJarConfigFrom from "./JarConfigFrom";
|
||||
export default {
|
||||
name: "MsJarConfig",
|
||||
components: {MsJarConfigFrom, MsJarConfigList, ApiEnvironmentConfig, MsDialogFooter},
|
||||
components: {MsJarConfigFrom, MsJarConfigList, MsDialogFooter},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
<template>
|
||||
<span class="previous-next-button">
|
||||
<span class="head-right-tip" v-if="index + 1 === list.length">
|
||||
{{ $t('test_track.plan_view.pre_case') }} : {{list[index - 1] ? list[index - 1].name : ''}}
|
||||
</span>
|
||||
<span class="head-right-tip" v-if="index + 1 !== list.length">
|
||||
{{ $t('test_track.plan_view.next_case') }} : {{list[index + 1] ? list[index + 1].name : ''}}
|
||||
</span>
|
||||
|
||||
<el-button plain size="mini" icon="el-icon-arrow-up" :disabled="index + 1 <= 1" @click="handlePre()"/>
|
||||
<span>
|
||||
{{ index + 1 }}/{{ list.length }}
|
||||
</span>
|
||||
<el-button plain size="mini" icon="el-icon-arrow-down" :disabled="index + 1 >= list.length" @click="handleNext()"/>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MsPreviousNextButton",
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
props: {
|
||||
list: {
|
||||
type: Array,
|
||||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
index: {
|
||||
type: Number,
|
||||
default() {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handlePre() {
|
||||
this.$emit('pre');
|
||||
},
|
||||
handleNext() {
|
||||
this.$emit('next');
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.head-right-tip {
|
||||
color: darkgrey;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -2,7 +2,7 @@
|
|||
<div id="app" v-loading="loading">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane :label="$t('organization.message.template')" name="apiTemplate">
|
||||
<el-button type="primary" size="mini" style="margin-left: 10px" @click="openOneClickOperation">导入</el-button>
|
||||
<el-button type="primary" size="mini" style="margin: 10px 10px 0px" @click="openOneClickOperation">导入</el-button>
|
||||
<div style="min-height: 200px">
|
||||
<json-schema-editor class="schema" :value="schema" lang="zh_CN" custom/>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
title="导入"
|
||||
:visible.sync="importVisible"
|
||||
width="50%"
|
||||
append-to-body
|
||||
show-close
|
||||
:close-on-click-modal="false"
|
||||
@closed="handleClose">
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<json-schema-editor :value="{items:pickValue.items}" :deep="deep+1" disabled isItem :root="false" class="children" :lang="lang" :custom="custom"/>
|
||||
</template>
|
||||
<!-- 高级设置-->
|
||||
<el-dialog :close-on-click-modal="false" :title="$t('schema.adv_setting')" :visible.sync="modalVisible" :destroy-on-close="true"
|
||||
<el-dialog append-to-body :close-on-click-modal="false" :title="$t('schema.adv_setting')" :visible.sync="modalVisible" :destroy-on-close="true"
|
||||
@close="handleClose">
|
||||
<p class="tip">基础设置 </p>
|
||||
<el-form :inline="true" v-model="advancedValue" class="ms-advanced-search-form">
|
||||
|
|
|
@ -10,7 +10,21 @@
|
|||
<el-table-column
|
||||
prop="label"
|
||||
label="Label"
|
||||
width="450"/>
|
||||
width="450">
|
||||
<template v-slot:header="{column}">
|
||||
<span>Label</span>
|
||||
<i class="el-icon-search" style="margin-left: 8px;cursor: pointer;font-weight: bold;" @click="click(column)"></i>
|
||||
<el-input v-model="searchLabel"
|
||||
placeholder="请输入 Label 搜索"
|
||||
size="mini"
|
||||
class="search_input"
|
||||
style="width: 250px; margin-left: 5px"
|
||||
v-if="column.showSearch"
|
||||
clearable
|
||||
@clear="filterLabel"
|
||||
@keyup.enter.native="filterLabel"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Executions" align="center">
|
||||
|
@ -94,17 +108,35 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
id: ''
|
||||
originalData: [],
|
||||
id: '',
|
||||
searchLabel: '',
|
||||
showSearch: false,
|
||||
showBtn: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initTableData() {
|
||||
this.$get("/performance/report/content/" + this.id).then(res => {
|
||||
this.tableData = res.data.data;
|
||||
this.originalData = res.data.data;
|
||||
}).catch(() => {
|
||||
this.tableData = [];
|
||||
})
|
||||
},
|
||||
click(column) {
|
||||
this.searchLabel = '';
|
||||
this.tableData = this.originalData;
|
||||
this.$set(column, 'showSearch', !column.showSearch);
|
||||
},
|
||||
filterLabel() {
|
||||
this.tableData = this.searchLabel ? this.originalData.filter(this.createFilter(this.searchLabel)) : this.originalData;
|
||||
},
|
||||
createFilter(queryString) {
|
||||
return item => {
|
||||
return (item.label.toLowerCase().indexOf(queryString.toLowerCase()) !== -1);
|
||||
};
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
report: {
|
||||
|
@ -128,5 +160,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.search_input >>> .el-input__inner {
|
||||
border-radius: 50px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -291,7 +291,7 @@ export default {
|
|||
},
|
||||
fileChange(threadGroups) {
|
||||
let handler = this.$refs.pressureConfig;
|
||||
handler.threadGroups = threadGroups;
|
||||
|
||||
threadGroups.forEach(tg => {
|
||||
tg.threadNumber = tg.threadNumber || 10;
|
||||
tg.duration = tg.duration || 10;
|
||||
|
@ -301,8 +301,13 @@ export default {
|
|||
tg.threadType = tg.threadType || 'DURATION';
|
||||
tg.iterateNum = tg.iterateNum || 1;
|
||||
tg.iterateRampUp = tg.iterateRampUp || 10;
|
||||
handler.calculateChart(tg);
|
||||
});
|
||||
|
||||
this.$set(handler, "threadGroups", threadGroups);
|
||||
|
||||
threadGroups.forEach(tg => {
|
||||
handler.calculateChart(tg);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
v-model="threadGroup.rpsLimit"
|
||||
@change="calculateChart(threadGroup)"
|
||||
:min="1"
|
||||
:max="500"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
|
@ -87,6 +88,7 @@
|
|||
:disabled="isReadOnly"
|
||||
v-model="threadGroup.iterateNum"
|
||||
:min="1"
|
||||
:max="10000"
|
||||
@change="calculateChart(threadGroup)"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
|
@ -98,6 +100,7 @@
|
|||
:disabled="isReadOnly || !threadGroup.rpsLimitEnable"
|
||||
v-model="threadGroup.rpsLimit"
|
||||
:min="1"
|
||||
:max="500"
|
||||
size="mini"/>
|
||||
</el-form-item>
|
||||
<br>
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
@refresh="refresh"
|
||||
@refreshAll="refreshAll"
|
||||
@moveToNode="moveToNode"
|
||||
@setCondition="setCondition"
|
||||
ref="testCaseList">
|
||||
</test-case-list>
|
||||
</ms-main-container>
|
||||
|
@ -30,12 +31,13 @@
|
|||
:read-only="testCaseReadOnly"
|
||||
:tree-nodes="treeNodes"
|
||||
:select-node="selectNode"
|
||||
:select-condition="condition"
|
||||
ref="testCaseEditDialog">
|
||||
</test-case-edit>
|
||||
|
||||
<test-case-move @refresh="refresh" ref="testCaseMove"/>
|
||||
|
||||
<batch-move @refresh="refresh" ref="testBatchMove"/>
|
||||
<batch-move @refresh="refresh" @moveSave="moveSave" ref="testBatchMove"/>
|
||||
|
||||
</ms-container>
|
||||
|
||||
|
@ -54,6 +56,7 @@ import MsMainContainer from "../../common/components/MsMainContainer";
|
|||
import {checkoutTestManagerOrTestUser, getCurrentProjectID, hasRoles} from "../../../../common/js/utils";
|
||||
import BatchMove from "./components/BatchMove";
|
||||
import TestCaseNodeTree from "../common/TestCaseNodeTree";
|
||||
import {TrackEvent,LIST_CHANGE} from "@/business/components/common/head/ListEvent";
|
||||
|
||||
export default {
|
||||
name: "TestCase",
|
||||
|
@ -72,6 +75,7 @@ export default {
|
|||
selectParentNodes: [],
|
||||
testCaseReadOnly: true,
|
||||
selectNode: {},
|
||||
condition: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -163,6 +167,18 @@ export default {
|
|||
},
|
||||
setTreeNodes(data) {
|
||||
this.treeNodes = data;
|
||||
},
|
||||
setCondition(data) {
|
||||
this.condition = data;
|
||||
},
|
||||
moveSave(param) {
|
||||
this.result = this.$post('/test/case/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.$refs.testBatchMove.close();
|
||||
// 发送广播,刷新 head 上的最新列表
|
||||
TrackEvent.$emit(LIST_CHANGE);
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,9 +76,13 @@
|
|||
}
|
||||
});
|
||||
},
|
||||
open() {
|
||||
open(size) {
|
||||
this.dialogVisible = true;
|
||||
this.size = this.$parent.selectRows.size;
|
||||
if (size) {
|
||||
this.size = size;
|
||||
} else {
|
||||
this.size = this.$parent.selectRows.size;
|
||||
}
|
||||
listenGoBack(this.handleClose);
|
||||
},
|
||||
handleClose() {
|
||||
|
@ -90,6 +94,15 @@
|
|||
this.$set(this.form, "value", "");
|
||||
this.filterable = val === "maintainer" || val === "executor";
|
||||
this.options = this.valueArr[val];
|
||||
this.typeArr.forEach(item => {
|
||||
if (item.id === val) {
|
||||
if (item.optionMethod) {
|
||||
this.options = [];
|
||||
item.optionMethod(this.options);
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-loading="result.loading">
|
||||
<div class="batch-move" v-loading="result.loading">
|
||||
<el-dialog :title="this.$t('test_track.case.select_catalog')"
|
||||
:visible.sync="dialogVisible"
|
||||
:before-close="close"
|
||||
|
@ -39,7 +39,6 @@
|
|||
|
||||
<script>
|
||||
import MsDialogFooter from "../../../common/components/MsDialogFooter";
|
||||
import {TrackEvent,LIST_CHANGE} from "@/business/components/common/head/ListEvent";
|
||||
|
||||
export default {
|
||||
name: "BatchMove",
|
||||
|
@ -77,19 +76,15 @@
|
|||
}
|
||||
let param = {};
|
||||
param.nodeId = this.currentKey;
|
||||
this.moduleOptions.forEach(item => {
|
||||
if (item.id === this.currentKey) {
|
||||
param.nodePath = item.path;
|
||||
}
|
||||
});
|
||||
if (this.moduleOptions) {
|
||||
this.moduleOptions.forEach(item => {
|
||||
if (item.id === this.currentKey) {
|
||||
param.nodePath = item.path;
|
||||
}
|
||||
});
|
||||
}
|
||||
param.ids = this.selectIds;
|
||||
this.result = this.$post('/test/case/batch/edit', param, () => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
this.close();
|
||||
// 发送广播,刷新 head 上的最新列表
|
||||
TrackEvent.$emit(LIST_CHANGE);
|
||||
this.$emit('refresh');
|
||||
});
|
||||
this.$emit('moveSave', param);
|
||||
},
|
||||
refresh() {
|
||||
this.$emit("refresh");
|
||||
|
@ -120,4 +115,8 @@
|
|||
overflow: hidden;
|
||||
}
|
||||
|
||||
.batch-move {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -5,6 +5,19 @@
|
|||
:title="operationType == 'edit' ? ( readOnly ? $t('test_track.case.view_case') : $t('test_track.case.edit_case')) : $t('test_track.case.create')"
|
||||
:visible.sync="dialogFormVisible" width="85%" v-if="dialogFormVisible">
|
||||
|
||||
<template v-slot:title>
|
||||
<el-row>
|
||||
<el-col :span="4">
|
||||
<span>
|
||||
{{operationType == 'edit' ? ( readOnly ? $t('test_track.case.view_case') : $t('test_track.case.edit_case')) : $t('test_track.case.create')}}
|
||||
</span>
|
||||
</el-col>
|
||||
<el-col class="head-right" :span="19">
|
||||
<ms-previous-next-button v-if="operationType == 'edit'" :index="index" @pre="handlePre" @next="handleNext" :list="testCases"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-row :gutter="10">
|
||||
<div>
|
||||
<el-col :span="17">
|
||||
|
@ -279,10 +292,11 @@ import {getCurrentProjectID} from "../../../../../common/js/utils";
|
|||
import {buildNodePath} from "../../../api/definition/model/NodeTree";
|
||||
import CaseComment from "@/business/components/track/case/components/CaseComment";
|
||||
import MsInputTag from "@/business/components/api/automation/scenario/MsInputTag";
|
||||
import MsPreviousNextButton from "../../../common/components/MsPreviousNextButton";
|
||||
|
||||
export default {
|
||||
name: "TestCaseEdit",
|
||||
components: {MsInputTag, CaseComment, MsDialogFooter, TestCaseAttachment},
|
||||
components: {MsPreviousNextButton, MsInputTag, CaseComment, MsDialogFooter, TestCaseAttachment},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
|
@ -335,6 +349,8 @@ export default {
|
|||
{value: 'manual', label: this.$t('test_track.case.manual')}
|
||||
],
|
||||
testCase: {},
|
||||
testCases: [],
|
||||
index: 0
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
@ -348,6 +364,9 @@ export default {
|
|||
selectNode: {
|
||||
type: Object
|
||||
},
|
||||
selectCondition: {
|
||||
type: Object
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getSelectOptions();
|
||||
|
@ -363,18 +382,12 @@ export default {
|
|||
this.$nextTick(() => (this.isStepTableAlive = true));
|
||||
},
|
||||
open(testCase) {
|
||||
this.testCase = {};
|
||||
if (testCase) {
|
||||
testCase.tags = JSON.parse(testCase.tags);
|
||||
// 复制 不查询评论
|
||||
this.testCase = testCase.isCopy ? {} : testCase;
|
||||
}
|
||||
this.resetForm();
|
||||
this.projectId = getCurrentProjectID();
|
||||
if (window.history && window.history.pushState) {
|
||||
history.pushState(null, null, document.URL);
|
||||
window.addEventListener('popstate', this.close);
|
||||
}
|
||||
this.resetForm();
|
||||
listenGoBack(this.close);
|
||||
this.operationType = 'add';
|
||||
if (testCase) {
|
||||
|
@ -383,13 +396,13 @@ export default {
|
|||
//复制
|
||||
if (testCase.name === '') {
|
||||
this.operationType = 'add';
|
||||
this.setFormData(testCase);
|
||||
this.setTestCaseExtInfo(testCase);
|
||||
this.getSelectOptions();
|
||||
this.reload();
|
||||
} else {
|
||||
this.initTestCases(testCase);
|
||||
}
|
||||
let tmp = {};
|
||||
Object.assign(tmp, testCase);
|
||||
tmp.steps = JSON.parse(testCase.steps);
|
||||
Object.assign(this.form, tmp);
|
||||
this.form.module = testCase.nodeId;
|
||||
this.getFileMetaData(testCase);
|
||||
} else {
|
||||
if (this.selectNode.data) {
|
||||
this.form.module = this.selectNode.data.id;
|
||||
|
@ -403,12 +416,56 @@ export default {
|
|||
this.form.type = 'functional';
|
||||
this.form.method = 'manual';
|
||||
this.form.maintainer = user.id;
|
||||
this.getSelectOptions();
|
||||
this.reload();
|
||||
}
|
||||
|
||||
this.getSelectOptions();
|
||||
this.reload();
|
||||
this.dialogFormVisible = true;
|
||||
},
|
||||
handlePre() {
|
||||
this.index--;
|
||||
this.getTestCase(this.index)
|
||||
},
|
||||
handleNext() {
|
||||
this.index++;
|
||||
this.getTestCase(this.index);
|
||||
},
|
||||
initTestCases(testCase) {
|
||||
this.result = this.$post('/test/case/list/ids', this.selectCondition, response => {
|
||||
this.testCases = response.data;
|
||||
for (let i = 0; i < this.testCases.length; i++) {
|
||||
if (this.testCases[i].id === testCase.id) {
|
||||
this.index = i;
|
||||
this.getTestCase(i);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
getTestCase(index) {
|
||||
let testCase = this.testCases[index];
|
||||
this.result = this.$get('/test/case/get/' + testCase.id, response => {
|
||||
let testCase = response.data;
|
||||
this.setFormData(testCase);
|
||||
this.setTestCaseExtInfo(testCase);
|
||||
this.getSelectOptions();
|
||||
this.reload();
|
||||
})
|
||||
},
|
||||
setFormData(testCase) {
|
||||
testCase.tags = JSON.parse(testCase.tags);
|
||||
let tmp = {};
|
||||
Object.assign(tmp, testCase);
|
||||
tmp.steps = JSON.parse(testCase.steps);
|
||||
Object.assign(this.form, tmp);
|
||||
this.form.module = testCase.nodeId;
|
||||
this.getFileMetaData(testCase);
|
||||
},
|
||||
setTestCaseExtInfo (testCase) {
|
||||
this.testCase = {};
|
||||
if (testCase) {
|
||||
// 复制 不查询评论
|
||||
this.testCase = testCase.isCopy ? {} : testCase;
|
||||
}
|
||||
},
|
||||
getFileMetaData(testCase) {
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
|
@ -607,28 +664,33 @@ export default {
|
|||
if (this.$refs['caseFrom']) {
|
||||
this.$refs['caseFrom'].validate((valid) => {
|
||||
this.$refs['caseFrom'].resetFields();
|
||||
this.form.name = '';
|
||||
this.form.module = '';
|
||||
this.form.type = '';
|
||||
this.form.method = '';
|
||||
this.form.maintainer = '';
|
||||
this.form.priority = '';
|
||||
this.form.prerequisite = '';
|
||||
this.form.remark = '';
|
||||
this.form.testId = '';
|
||||
this.form.testName = '';
|
||||
this.form.steps = [{
|
||||
num: 1,
|
||||
desc: '',
|
||||
result: ''
|
||||
}];
|
||||
this.uploadList = [];
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
this._resetForm();
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
this._resetForm();
|
||||
}
|
||||
},
|
||||
_resetForm() {
|
||||
this.form.name = '';
|
||||
this.form.module = '';
|
||||
this.form.type = '';
|
||||
this.form.method = '';
|
||||
this.form.maintainer = '';
|
||||
this.form.priority = '';
|
||||
this.form.prerequisite = '';
|
||||
this.form.remark = '';
|
||||
this.form.testId = '';
|
||||
this.form.testName = '';
|
||||
this.form.steps = [{
|
||||
num: 1,
|
||||
desc: '',
|
||||
result: ''
|
||||
}];
|
||||
this.uploadList = [];
|
||||
this.fileList = [];
|
||||
this.tableData = [];
|
||||
},
|
||||
handleExceed() {
|
||||
this.$error(this.$t('load_test.file_size_limit'));
|
||||
},
|
||||
|
@ -748,4 +810,8 @@ export default {
|
|||
height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
.head-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -286,12 +286,16 @@ export default {
|
|||
}
|
||||
},
|
||||
created: function () {
|
||||
this.$emit('setCondition', this.condition);
|
||||
this.initTableData();
|
||||
},
|
||||
watch: {
|
||||
selectNodeIds() {
|
||||
this.currentPage = 1;
|
||||
this.initTableData();
|
||||
},
|
||||
condition() {
|
||||
this.$emit('setCondition', this.condition);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -137,7 +137,15 @@ export default {
|
|||
return false;
|
||||
},
|
||||
filter(val) {
|
||||
this.$refs.tree.filter(val);
|
||||
if (!val) {
|
||||
val = this.filterText;
|
||||
} else {
|
||||
// 记录condition 的 filterText
|
||||
this.filterText = val;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.filter(val);
|
||||
});
|
||||
},
|
||||
nodeExpand(data) {
|
||||
if (data.id) {
|
||||
|
@ -149,10 +157,15 @@ export default {
|
|||
this.expandedNode.splice(this.expandedNode.indexOf(data.id), 1);
|
||||
}
|
||||
},
|
||||
edit(node, data) {
|
||||
edit(node, data, isAppend) {
|
||||
this.$set(data, 'isEdit', true);
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nameInput.focus();
|
||||
if (!isAppend) {
|
||||
this.$nextTick(() => {
|
||||
this.filter(this.filterText);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
append(node, data) {
|
||||
|
@ -166,7 +179,7 @@ export default {
|
|||
this.$set(data, 'children', [])
|
||||
}
|
||||
data.children.push(newChild);
|
||||
this.edit(node, newChild);
|
||||
this.edit(node, newChild, true);
|
||||
node.expanded = true;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nameInput.focus();
|
||||
|
|
|
@ -51,6 +51,9 @@
|
|||
if (this.projectId) {
|
||||
this.result = this.$get("/case/node/list/" + this.projectId, response => {
|
||||
this.treeNodes = response.data;
|
||||
if (this.$refs.nodeTree) {
|
||||
this.$refs.nodeTree.filter();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -168,7 +168,7 @@ export default {
|
|||
deletePath: "/test/case/delete",
|
||||
selectRows: new Set(),
|
||||
buttons: [
|
||||
{name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch},
|
||||
{name: this.$t('test_track.case.batch_unlink'), handleClick: this.handleDeleteBatch},
|
||||
{name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute}
|
||||
],
|
||||
typeArr: [
|
||||
|
|
|
@ -117,7 +117,7 @@
|
|||
runData: [],
|
||||
buttons: [
|
||||
{
|
||||
name: this.$t('api_test.definition.request.batch_delete'), handleClick: this.handleDeleteBatch
|
||||
name: this.$t('test_track.case.batch_unlink'), handleClick: this.handleDeleteBatch
|
||||
},
|
||||
{
|
||||
name: this.$t('api_test.automation.batch_execute'), handleClick: this.handleBatchExecute
|
||||
|
|
|
@ -20,32 +20,15 @@
|
|||
|
||||
<el-row type="flex" class="head-bar">
|
||||
|
||||
<el-col>
|
||||
<el-col :span="4">
|
||||
<el-button plain size="mini"
|
||||
icon="el-icon-back"
|
||||
@click="cancel">{{ $t('test_track.return') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
|
||||
<el-col class="head-right">
|
||||
<span class="head-right-tip" v-if="index + 1 === testCases.length">
|
||||
{{ $t('test_track.plan_view.pre_case') }} : {{
|
||||
testCases[index - 1] ? testCases[index - 1].name : ''
|
||||
}}
|
||||
</span>
|
||||
<span class="head-right-tip" v-if="index + 1 !== testCases.length">
|
||||
{{ $t('test_track.plan_view.next_case') }} : {{
|
||||
testCases[index + 1] ? testCases[index + 1].name : ''
|
||||
}}
|
||||
</span>
|
||||
|
||||
<el-button plain size="mini" icon="el-icon-arrow-up"
|
||||
:disabled="index + 1 <= 1"
|
||||
@click="handlePre()"/>
|
||||
<span> {{ index + 1 }}/{{ testCases.length }} </span>
|
||||
<el-button plain size="mini" icon="el-icon-arrow-down"
|
||||
:disabled="index + 1 >= testCases.length"
|
||||
@click="handleNext()"/>
|
||||
<el-col class="head-right" :span="20">
|
||||
<ms-previous-next-button :index="index" @pre="handlePre" @next="handleNext" :list="testCases"/>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
@ -370,10 +353,12 @@ import PerformanceTestResult from "../test/PerformanceTestResult";
|
|||
import {listenGoBack, removeGoBackListener} from "@/common/js/utils";
|
||||
import TestCaseAttachment from "@/business/components/track/case/components/TestCaseAttachment";
|
||||
import CaseComment from "@/business/components/track/case/components/CaseComment";
|
||||
import MsPreviousNextButton from "../../../../../common/components/MsPreviousNextButton";
|
||||
|
||||
export default {
|
||||
name: "FunctionalTestCaseEdit",
|
||||
components: {
|
||||
MsPreviousNextButton,
|
||||
CaseComment,
|
||||
PerformanceTestResult,
|
||||
PerformanceTestDetail,
|
||||
|
@ -722,10 +707,6 @@ export default {
|
|||
float: right;
|
||||
}
|
||||
|
||||
.head-right-tip {
|
||||
color: darkgrey;
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
height: 100%;
|
||||
}
|
||||
|
|
|
@ -226,10 +226,7 @@ export default {
|
|||
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.treeNodes = [];
|
||||
this.selectNodeIds = [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -921,12 +921,15 @@ export default {
|
|||
table_coloum: {
|
||||
index: "Index",
|
||||
scenario: "Scene",
|
||||
task_type: "Task Type",
|
||||
run_rule: "Rule",
|
||||
task_status: "Status",
|
||||
next_execution_time: "Next Execution Time",
|
||||
create_user: "Creator",
|
||||
update_time: "Update time",
|
||||
},
|
||||
scenario_schedule: "Scenario",
|
||||
test_plan_schedule: "Test plan",
|
||||
confirm: {
|
||||
close_title: "Do you want to close this scheduled task?",
|
||||
}
|
||||
|
@ -1044,7 +1047,7 @@ export default {
|
|||
batch_operate: 'Batch operation',
|
||||
please_select_attr: 'Please select attributes',
|
||||
please_select_attr_value: 'Please select the value corresponding to the attribute',
|
||||
batch_edit_case: 'Batch editing test cases',
|
||||
batch_edit_case: 'Batch editing',
|
||||
batch_move_case: 'Batch move',
|
||||
batch_delete_case: 'Batch delete',
|
||||
batch_unlink: 'Batch Unlink',
|
||||
|
|
|
@ -924,12 +924,15 @@ export default {
|
|||
table_coloum: {
|
||||
index: "序号",
|
||||
scenario: "场景名称",
|
||||
task_type: "任务类型",
|
||||
run_rule: "运行规则",
|
||||
task_status: "任务状态",
|
||||
next_execution_time: "下次执行时间",
|
||||
create_user: "创建人",
|
||||
update_time: "更新时间",
|
||||
},
|
||||
scenario_schedule: "场景",
|
||||
test_plan_schedule: "测试计划",
|
||||
confirm: {
|
||||
close_title: "要关闭这条定时任务吗?",
|
||||
}
|
||||
|
@ -1047,9 +1050,9 @@ export default {
|
|||
batch_operate: '批量操作',
|
||||
please_select_attr: '请选择属性',
|
||||
please_select_attr_value: '请选择属性对应的值',
|
||||
batch_edit_case: '批量编辑用例',
|
||||
batch_move_case: '批量移动用例',
|
||||
batch_delete_case: '批量删除用例',
|
||||
batch_edit_case: '批量编辑',
|
||||
batch_move_case: '批量移动',
|
||||
batch_delete_case: '批量删除',
|
||||
batch_unlink: '批量取消关联',
|
||||
project_name: '所属项目',
|
||||
status: '评审状态',
|
||||
|
|
|
@ -922,12 +922,15 @@ export default {
|
|||
table_coloum: {
|
||||
index: "序號",
|
||||
scenario: "場景名稱",
|
||||
task_type: "任務類型",
|
||||
run_rule: "運行規則",
|
||||
task_status: "任務狀態",
|
||||
next_execution_time: "下次執行時間",
|
||||
create_user: "創建人",
|
||||
update_time: "更新時間",
|
||||
},
|
||||
scenario_schedule: "場景",
|
||||
test_plan_schedule: "測試計畫",
|
||||
confirm: {
|
||||
close_title: "要關閉這條定時任務嗎?",
|
||||
}
|
||||
|
@ -1045,9 +1048,9 @@ export default {
|
|||
batch_operate: '批量操作',
|
||||
please_select_attr: '請選擇屬性',
|
||||
please_select_attr_value: '請選擇屬性對應的值',
|
||||
batch_edit_case: '批量編輯用例',
|
||||
batch_move_case: '批量移動用例',
|
||||
batch_delete_case: '批量刪除用例',
|
||||
batch_edit_case: '批量編輯',
|
||||
batch_move_case: '批量移動',
|
||||
batch_delete_case: '批量刪除',
|
||||
batch_unlink: '批量取消關聯',
|
||||
project_name: '所屬項目',
|
||||
status: '評審狀態',
|
||||
|
|
Loading…
Reference in New Issue