Merge branch 'master' into v1.7
This commit is contained in:
commit
90d8161b5e
|
@ -158,7 +158,8 @@ public class MsHTTPSamplerProxy extends MsTestElement {
|
|||
if (!path.startsWith("/")) {
|
||||
path = "/" + path;
|
||||
}
|
||||
path = sampler.getProtocol() + "://" + sampler.getDomain() + ":" + sampler.getPort() + path;
|
||||
String port = sampler.getPort() != 80 ? ":" + sampler.getPort() : "";
|
||||
path = sampler.getProtocol() + "://" + sampler.getDomain() + port + path;
|
||||
}
|
||||
sampler.setPath(path);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.apache.jmeter.testelement.TestElement;
|
|||
import org.apache.jorphan.collections.HashTree;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
|
@ -51,7 +50,7 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
@JSONField(ordinal = 28)
|
||||
private String dataSourceId;
|
||||
@JSONField(ordinal = 29)
|
||||
private String protocol="SQL";
|
||||
private String protocol = "SQL";
|
||||
|
||||
@Override
|
||||
public void toHashTree(HashTree tree, List<MsTestElement> hashTree, ParameterConfig config) {
|
||||
|
@ -62,7 +61,8 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
this.getRefElement(this);
|
||||
}
|
||||
if (StringUtils.isNotEmpty(dataSourceId)) {
|
||||
initDataSource();
|
||||
this.dataSource = null;
|
||||
this.initDataSource();
|
||||
}
|
||||
if (this.dataSource == null) {
|
||||
MSException.throwException("数据源为空无法执行");
|
||||
|
@ -79,14 +79,16 @@ public class MsJDBCSampler extends MsTestElement {
|
|||
|
||||
private void initDataSource() {
|
||||
ApiTestEnvironmentService environmentService = CommonBeanFactory.getBean(ApiTestEnvironmentService.class);
|
||||
ApiTestEnvironmentWithBLOBs environment = environmentService.get(this.dataSourceId);
|
||||
ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentId);
|
||||
if (environment != null && environment.getConfig() != null) {
|
||||
EnvironmentConfig config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
if (CollectionUtils.isNotEmpty(config.getDatabaseConfigs())) {
|
||||
List<DatabaseConfig> databaseConfigs = config.getDatabaseConfigs().stream().filter((DatabaseConfig d) -> this.dataSourceId.equals(d.getId())).collect(Collectors.toList());
|
||||
if (CollectionUtils.isNotEmpty(databaseConfigs)) {
|
||||
this.dataSource = databaseConfigs.get(0);
|
||||
}
|
||||
EnvironmentConfig envConfig = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
|
||||
if (CollectionUtils.isNotEmpty(envConfig.getDatabaseConfigs())) {
|
||||
envConfig.getDatabaseConfigs().forEach(item -> {
|
||||
if (item.getId().equals(this.dataSourceId)) {
|
||||
this.dataSource = item;
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -227,12 +227,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
sendTask(report, reportUrl, testResult);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void sendTask(ApiTestReport report, String reportUrl, TestResult testResult) {
|
||||
|
@ -297,6 +292,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
requestResult.setHeaders(result.getRequestHeaders());
|
||||
requestResult.setRequestSize(result.getSentBytes());
|
||||
requestResult.setStartTime(result.getStartTime());
|
||||
requestResult.setEndTime(result.getEndTime());
|
||||
requestResult.setTotalAssertions(result.getAssertionResults().length);
|
||||
requestResult.setSuccess(result.isSuccessful());
|
||||
requestResult.setError(result.getErrorCount());
|
||||
|
@ -336,7 +332,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
|
|||
requestResult.addPassAssertions();
|
||||
}
|
||||
//xpath 提取错误会添加断言错误
|
||||
if (StringUtils.isBlank(responseAssertionResult.getMessage()) || !responseAssertionResult.getMessage().contains("The required item type of the first operand of")) {
|
||||
if (StringUtils.isBlank(responseAssertionResult.getMessage()) || !responseAssertionResult.getName().endsWith("XPath2Extractor")) {
|
||||
responseResult.getAssertions().add(responseAssertionResult);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ public class RequestResult {
|
|||
|
||||
private long startTime;
|
||||
|
||||
private long endTime;
|
||||
|
||||
private int error;
|
||||
|
||||
private boolean success;
|
||||
|
|
|
@ -217,7 +217,7 @@ public class ApiAutomationService {
|
|||
ids.add(scenarioId);
|
||||
deleteApiScenarioReport(ids);
|
||||
|
||||
scheduleService.deleteByResourceId(scenarioId);
|
||||
scheduleService.deleteScheduleAndJobByResourceId(scenarioId,ScheduleGroup.API_SCENARIO_TEST.name());
|
||||
TestPlanApiScenarioExample example = new TestPlanApiScenarioExample();
|
||||
example.createCriteria().andApiScenarioIdEqualTo(scenarioId);
|
||||
List<TestPlanApiScenario> testPlanApiScenarioList = testPlanApiScenarioMapper.selectByExample(example);
|
||||
|
@ -282,6 +282,10 @@ public class ApiAutomationService {
|
|||
|
||||
public void removeToGc(List<String> apiIds) {
|
||||
extApiScenarioMapper.removeToGc(apiIds);
|
||||
//将这些场景的定时任务删除掉
|
||||
for (String id : apiIds) {
|
||||
scheduleService.deleteScheduleAndJobByResourceId(id,ScheduleGroup.API_SCENARIO_TEST.name());
|
||||
}
|
||||
}
|
||||
|
||||
public void reduction(List<SaveApiScenarioRequest> requests) {
|
||||
|
|
|
@ -323,7 +323,9 @@ public class ApiDefinitionService {
|
|||
apiDefinitionMapper.updateByPrimaryKeyWithBLOBs(apiDefinition);
|
||||
}
|
||||
} else if (StringUtils.equals("incrementalMerge", apiTestImportRequest.getModeId())) {
|
||||
batchMapper.insert(apiDefinition);
|
||||
if (CollectionUtils.isEmpty(sameRequest)) {
|
||||
batchMapper.insert(apiDefinition);
|
||||
}
|
||||
} else {
|
||||
if (CollectionUtils.isEmpty(sameRequest)) {
|
||||
batchMapper.insert(apiDefinition);
|
||||
|
|
|
@ -163,6 +163,7 @@ public class ApiScenarioReportService {
|
|||
String passRate = new DecimalFormat("0%").format((float) scenarioResult.getSuccess() / (scenarioResult.getSuccess() + scenarioResult.getError()));
|
||||
testPlanApiScenario.setPassRate(passRate);
|
||||
testPlanApiScenario.setReportId(report.getId());
|
||||
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
|
||||
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
|
||||
}
|
||||
returnReport = report;
|
||||
|
@ -220,6 +221,7 @@ public class ApiScenarioReportService {
|
|||
apiScenarioReportDetailMapper.insert(detail);
|
||||
|
||||
testPlanApiScenario.setReportId(report.getId());
|
||||
testPlanApiScenario.setUpdateTime(System.currentTimeMillis());
|
||||
testPlanApiScenarioMapper.updateByPrimaryKeySelective(testPlanApiScenario);
|
||||
|
||||
lastReport = report;
|
||||
|
@ -343,13 +345,41 @@ public class ApiScenarioReportService {
|
|||
ids = allIds.stream().filter(id -> !reportRequest.getUnSelectIds().contains(id)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample();
|
||||
detailExample.createCriteria().andReportIdIn(ids);
|
||||
apiScenarioReportDetailMapper.deleteByExample(detailExample);
|
||||
//为预防数量太多,调用删除方法时引起SQL过长的Bug,此处采取分批执行的方式。
|
||||
//每次处理的数据数量
|
||||
int handleCount = 7000;
|
||||
//每次处理的集合
|
||||
List<String> handleIdList = new ArrayList<>(handleCount);
|
||||
while (ids.size() > handleCount){
|
||||
handleIdList = new ArrayList<>(handleCount);
|
||||
List<String> otherIdList = new ArrayList<>();
|
||||
for (int index = 0;index < ids.size();index++){
|
||||
if(index<handleCount){
|
||||
handleIdList.add(ids.get(index));
|
||||
}else{
|
||||
otherIdList.add(ids.get(index));
|
||||
}
|
||||
}
|
||||
//处理本次的数据
|
||||
ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample();
|
||||
detailExample.createCriteria().andReportIdIn(handleIdList);
|
||||
apiScenarioReportDetailMapper.deleteByExample(detailExample);
|
||||
ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample();
|
||||
apiTestReportExample.createCriteria().andIdIn(handleIdList);
|
||||
apiScenarioReportMapper.deleteByExample(apiTestReportExample);
|
||||
//转存剩余的数据
|
||||
ids = otherIdList;
|
||||
}
|
||||
|
||||
ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample();
|
||||
apiTestReportExample.createCriteria().andIdIn(ids);
|
||||
apiScenarioReportMapper.deleteByExample(apiTestReportExample);
|
||||
//处理最后剩余的数据
|
||||
if(!ids.isEmpty()){
|
||||
ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample();
|
||||
detailExample.createCriteria().andReportIdIn(ids);
|
||||
apiScenarioReportDetailMapper.deleteByExample(detailExample);
|
||||
ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample();
|
||||
apiTestReportExample.createCriteria().andIdIn(ids);
|
||||
apiScenarioReportMapper.deleteByExample(apiTestReportExample);
|
||||
}
|
||||
}
|
||||
|
||||
public long countByProjectID(String projectId) {
|
||||
|
|
|
@ -134,7 +134,9 @@ public class HistoricalDataUpgradeService {
|
|||
request1.getBody().setType(Body.FORM_DATA);
|
||||
}
|
||||
if ("json".equals(request1.getBody().getFormat())) {
|
||||
request1.getBody().setType(Body.JSON);
|
||||
if ("Raw".equals(request1.getBody().getType())) {
|
||||
request1.getBody().setType(Body.JSON);
|
||||
}
|
||||
if (CollectionUtils.isEmpty(request1.getHeaders())) {
|
||||
List<KeyValue> headers = new LinkedList<>();
|
||||
headers.add(new KeyValue("Content-Type", "application/json"));
|
||||
|
|
|
@ -33,5 +33,4 @@ public interface ScheduleMapper {
|
|||
int updateByPrimaryKeyWithBLOBs(Schedule record);
|
||||
|
||||
int updateByPrimaryKey(Schedule record);
|
||||
|
||||
}
|
|
@ -219,7 +219,8 @@
|
|||
</if>
|
||||
<if test="request.name != null">
|
||||
and (api_definition.name like CONCAT('%', #{request.name},'%')
|
||||
or api_definition.tags like CONCAT('%', #{request.name},'%'))
|
||||
or api_definition.tags like CONCAT('%', #{request.name},'%')
|
||||
or api_definition.num like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.protocol != null">
|
||||
AND api_definition.protocol = #{request.protocol}
|
||||
|
|
|
@ -143,7 +143,9 @@
|
|||
</if>
|
||||
|
||||
<if test="request.name != null">
|
||||
and (api_scenario.name like CONCAT('%', #{request.name},'%') or api_scenario.tags like CONCAT('%', #{request.name},'%'))
|
||||
and (api_scenario.name like CONCAT('%', #{request.name},'%')
|
||||
or api_scenario.tags like CONCAT('%', #{request.name},'%')
|
||||
or api_scenario.num like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.workspaceId != null">
|
||||
AND project.workspace_id = #{request.workspaceId}
|
||||
|
|
|
@ -284,7 +284,9 @@
|
|||
</foreach>
|
||||
</if>
|
||||
<if test="request.name != null and request.name!=''">
|
||||
and (t1.name like CONCAT('%', #{request.name},'%') or t1.tags like CONCAT('%', #{request.name},'%'))
|
||||
and (t1.name like CONCAT('%', #{request.name},'%')
|
||||
or t1.tags like CONCAT('%', #{request.name},'%')
|
||||
or t1.num like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.createTime > 0">
|
||||
and t1.create_time >= #{request.createTime}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
select
|
||||
t.id, t.environment_id, t.create_time, t.update_time, t.last_result, t.pass_rate, t.report_id,
|
||||
c.id as case_id, c.project_id, c.user_id,c.api_scenario_module_id, c.module_path, c.name, c.level,
|
||||
c.status, c.principal, c.step_total, c.follow_people, c.schedule, c.description,
|
||||
c.status, c.principal, c.step_total, c.follow_people, c.schedule, c.description, c.tags, c.num,
|
||||
p.name as project_name, p.id as project_id, u.name as user_name
|
||||
from
|
||||
test_plan_api_scenario t
|
||||
|
@ -44,7 +44,9 @@
|
|||
</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.num like CONCAT('%', #{request.name},'%')
|
||||
or c.tags like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.status != null and request.status!=''">
|
||||
and t.last_result like CONCAT('%', #{request.status},'%')
|
||||
|
|
|
@ -122,7 +122,7 @@
|
|||
|
||||
<select id="list" resultType="io.metersphere.track.dto.TestPlanCaseDTO">
|
||||
select test_plan_test_case.id as id, test_case.id as caseId, test_case.name, test_case.priority,
|
||||
test_case.type,test_case.test_id as testId,test_case.node_id,
|
||||
test_case.type,test_case.test_id as testId,test_case.node_id, test_case.tags,
|
||||
test_case.node_path, test_case.method, test_case.num, test_plan_test_case.executor, test_plan_test_case.status,
|
||||
test_plan_test_case.update_time, test_case_node.name as model, project.name as projectName,
|
||||
test_plan_test_case.plan_id as planId
|
||||
|
@ -139,7 +139,7 @@
|
|||
</if>
|
||||
<if test="request.name != null">
|
||||
and (test_case.name like CONCAT('%', #{request.name},'%') or test_case.num like
|
||||
CONCAT('%',#{request.name},'%'))
|
||||
CONCAT('%',#{request.name},'%') or test_case.tags like CONCAT('%', #{request.name},'%'))
|
||||
</if>
|
||||
<if test="request.id != null">
|
||||
and test_case.id = #{request.id}
|
||||
|
|
|
@ -28,6 +28,7 @@ public class ShiroUtils {
|
|||
filterChainDefinitionMap.put("/authsource/list/allenable", "anon");
|
||||
filterChainDefinitionMap.put("/sso/signin", "anon");
|
||||
filterChainDefinitionMap.put("/sso/callback", "anon");
|
||||
filterChainDefinitionMap.put("/license/valid", "anon");
|
||||
|
||||
// for swagger
|
||||
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
|
||||
|
|
|
@ -105,25 +105,27 @@ public class TestPlanTestJob extends MsScheduleJob {
|
|||
//需要更新这里来保证PlanCase的状态能正常更改
|
||||
apiTestCaseService.run(blobs,UUID.randomUUID().toString(),testPlanReport.getId(),this.resourceId,ApiRunMode.SCHEDULE_API_PLAN.name());
|
||||
}
|
||||
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------");
|
||||
//执行场景执行任务
|
||||
SchedulePlanScenarioExecuteRequest scenarioRequest = new SchedulePlanScenarioExecuteRequest();
|
||||
String senarionReportID = UUID.randomUUID().toString();
|
||||
scenarioRequest.setId(senarionReportID);
|
||||
scenarioRequest.setReportId(senarionReportID);
|
||||
scenarioRequest.setProjectId(projectID);
|
||||
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
|
||||
scenarioRequest.setExecuteType(ExecuteType.Saved.name());
|
||||
Map<String, Map<String,String>> testPlanScenarioIdMap = new HashMap<>();
|
||||
testPlanScenarioIdMap.put(resourceId, this.planScenarioIdMap);
|
||||
scenarioRequest.setTestPlanScenarioIDMap(testPlanScenarioIdMap);
|
||||
scenarioRequest.setReportUserID(this.userId);
|
||||
scenarioRequest.setTestPlanID(this.resourceId);
|
||||
scenarioRequest.setRunMode(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
|
||||
scenarioRequest.setTestPlanReportId(testPlanReport.getId());
|
||||
testPlanService.runScenarioCase(scenarioRequest);
|
||||
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
|
||||
|
||||
//执行场景执行任务
|
||||
if(!planScenarioIdMap.isEmpty()){
|
||||
LogUtil.info("-------------- testplan schedule ---------- api case over -----------------");
|
||||
SchedulePlanScenarioExecuteRequest scenarioRequest = new SchedulePlanScenarioExecuteRequest();
|
||||
String senarionReportID = UUID.randomUUID().toString();
|
||||
scenarioRequest.setId(senarionReportID);
|
||||
scenarioRequest.setReportId(senarionReportID);
|
||||
scenarioRequest.setProjectId(projectID);
|
||||
scenarioRequest.setTriggerMode(ReportTriggerMode.SCHEDULE.name());
|
||||
scenarioRequest.setExecuteType(ExecuteType.Saved.name());
|
||||
Map<String, Map<String,String>> testPlanScenarioIdMap = new HashMap<>();
|
||||
testPlanScenarioIdMap.put(resourceId, this.planScenarioIdMap);
|
||||
scenarioRequest.setTestPlanScenarioIDMap(testPlanScenarioIdMap);
|
||||
scenarioRequest.setReportUserID(this.userId);
|
||||
scenarioRequest.setTestPlanID(this.resourceId);
|
||||
scenarioRequest.setRunMode(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
|
||||
scenarioRequest.setTestPlanReportId(testPlanReport.getId());
|
||||
testPlanService.runScenarioCase(scenarioRequest);
|
||||
LogUtil.info("-------------- testplan schedule ---------- scenario case over -----------------");
|
||||
}
|
||||
//执行性能测试任务
|
||||
List<String> performaneReportIDList = new ArrayList<>();
|
||||
for (Map.Entry<String,String> entry: this.performanceIdMap.entrySet()) {
|
||||
|
|
|
@ -20,10 +20,13 @@ public class MailNoticeSender extends AbstractNoticeSender {
|
|||
private MailService mailService;
|
||||
|
||||
private void sendMail(MessageDetail messageDetail, String context, NoticeModel noticeModel) throws MessagingException {
|
||||
LogUtil.info("发送邮件开始 ");
|
||||
JavaMailSenderImpl javaMailSender = mailService.getMailSender();
|
||||
MimeMessage mimeMessage = javaMailSender.createMimeMessage();
|
||||
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
|
||||
helper.setFrom(javaMailSender.getUsername());
|
||||
LogUtil.info("发件人地址"+javaMailSender.getUsername());
|
||||
LogUtil.info("helper"+helper);
|
||||
helper.setSubject("MeterSphere " + noticeModel.getSubject());
|
||||
List<String> emails = super.getUserEmails(messageDetail.getUserIds());
|
||||
String[] users = emails.toArray(new String[0]);
|
||||
|
@ -38,6 +41,7 @@ public class MailNoticeSender extends AbstractNoticeSender {
|
|||
String context = super.getHtmlContext(messageDetail, noticeModel);
|
||||
try {
|
||||
sendMail(messageDetail, context, noticeModel);
|
||||
LogUtil.info("发送邮件结束");
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
|
|
|
@ -53,14 +53,14 @@ public class MailService {
|
|||
props.put("mail.smtp.starttls.enable", result);
|
||||
props.put("mail.smtp.starttls.required", result);
|
||||
break;
|
||||
case "smtp.anon":
|
||||
/* case "smtp.anon":
|
||||
boolean isAnon = BooleanUtils.toBoolean(p.getParamValue());
|
||||
if (isAnon) {
|
||||
props.put("mail.smtp.auth", "false");
|
||||
javaMailSender.setUsername(null);
|
||||
javaMailSender.setPassword(null);
|
||||
}
|
||||
break;
|
||||
break;*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -18,10 +18,7 @@ import io.metersphere.commons.utils.SessionUtils;
|
|||
import io.metersphere.controller.request.OrderRequest;
|
||||
import io.metersphere.controller.request.QueryScheduleRequest;
|
||||
import io.metersphere.dto.ScheduleDao;
|
||||
import io.metersphere.job.sechedule.ApiScenarioTestJob;
|
||||
import io.metersphere.job.sechedule.ApiTestJob;
|
||||
import io.metersphere.job.sechedule.ScheduleManager;
|
||||
import io.metersphere.job.sechedule.TestPlanTestJob;
|
||||
import io.metersphere.job.sechedule.*;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.SchedulerException;
|
||||
|
@ -98,6 +95,25 @@ public class ScheduleService {
|
|||
return scheduleMapper.deleteByExample(scheduleExample);
|
||||
}
|
||||
|
||||
public int deleteScheduleAndJobByResourceId(String resourceId,String group) {
|
||||
ScheduleExample scheduleExample = new ScheduleExample();
|
||||
scheduleExample.createCriteria().andResourceIdEqualTo(resourceId);
|
||||
removeJob(resourceId,group);
|
||||
return scheduleMapper.deleteByExample(scheduleExample);
|
||||
}
|
||||
|
||||
public void removeJob(String resourceId,String group) {
|
||||
if(StringUtils.equals(ScheduleGroup.API_SCENARIO_TEST.name(),group)){
|
||||
scheduleManager.removeJob(ApiScenarioTestJob.getJobKey(resourceId), ApiScenarioTestJob.getTriggerKey(resourceId));
|
||||
}else if(StringUtils.equals(ScheduleGroup.TEST_PLAN_TEST.name(),group)){
|
||||
scheduleManager.removeJob(TestPlanTestJob.getJobKey(resourceId), TestPlanTestJob.getTriggerKey(resourceId));
|
||||
}else if(StringUtils.equals(ScheduleGroup.SWAGGER_IMPORT.name(),group)){
|
||||
scheduleManager.removeJob(SwaggerUrlImportJob.getJobKey(resourceId), SwaggerUrlImportJob.getTriggerKey(resourceId));
|
||||
}else{
|
||||
scheduleManager.removeJob(ApiTestJob.getJobKey(resourceId), ApiTestJob.getTriggerKey(resourceId));
|
||||
}
|
||||
}
|
||||
|
||||
public List<Schedule> listSchedule() {
|
||||
ScheduleExample example = new ScheduleExample();
|
||||
return scheduleMapper.selectByExample(example);
|
||||
|
|
|
@ -115,6 +115,7 @@ public class TestPlanApiCaseService {
|
|||
TestPlanApiCase apiCase = new TestPlanApiCase();
|
||||
apiCase.setId(id);
|
||||
apiCase.setStatus(status);
|
||||
apiCase.setUpdateTime(System.currentTimeMillis());
|
||||
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
|
||||
}
|
||||
|
||||
|
|
|
@ -281,6 +281,18 @@ public class TestPlanReportService {
|
|||
component.afterBuild(testCaseReportMetricDTO);
|
||||
});
|
||||
|
||||
if (StringUtils.equals(ReportTriggerMode.SCHEDULE.name(),triggerMode)
|
||||
&&StringUtils.equals(resourceRunMode, ApiRunMode.SCHEDULE_PERFORMANCE_TEST.name())) {
|
||||
//如果是性能测试作为触发,由于延迟原因可能会出现报告已经结束但是状态还是进行中的状态
|
||||
List<TestCaseReportStatusResultDTO> loadResult = testCaseReportMetricDTO.getExecuteResult().getLoadResult();
|
||||
for (TestCaseReportStatusResultDTO dto: loadResult) {
|
||||
if(StringUtils.equals(dto.getStatus(),TestPlanTestCaseStatus.Underway.name())){
|
||||
dto.setStatus(TestPlanTestCaseStatus.Pass.name());
|
||||
}
|
||||
}
|
||||
testCaseReportMetricDTO.getExecuteResult().setLoadResult(loadResult);
|
||||
}
|
||||
|
||||
TestPlanReportDataExample example = new TestPlanReportDataExample();
|
||||
example.createCriteria().andTestPlanReportIdEqualTo(planReportId);
|
||||
List<TestPlanReportDataWithBLOBs> testPlanReportDataList = testPlanReportDataMapper.selectByExampleWithBLOBs(example);
|
||||
|
@ -437,7 +449,7 @@ public class TestPlanReportService {
|
|||
if(loadTestReportFromDatabase == null){
|
||||
//检查错误数据
|
||||
if(errorDataCheckMap.containsKey(loadTestReportId)){
|
||||
if(errorDataCheckMap.get(loadTestReportId)>20){
|
||||
if(errorDataCheckMap.get(loadTestReportId)>10){
|
||||
performaneReportIDList.remove(loadTestReportId);
|
||||
}else {
|
||||
errorDataCheckMap.put(loadTestReportId,errorDataCheckMap.get(loadTestReportId)+1);
|
||||
|
|
|
@ -28,6 +28,7 @@ import io.metersphere.dto.BaseSystemConfigDTO;
|
|||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.notice.sender.NoticeModel;
|
||||
import io.metersphere.notice.service.NoticeSendService;
|
||||
import io.metersphere.service.ScheduleService;
|
||||
import io.metersphere.service.SystemParameterService;
|
||||
import io.metersphere.track.Factory.ReportComponentFactory;
|
||||
import io.metersphere.track.domain.ReportComponent;
|
||||
|
@ -62,6 +63,8 @@ public class TestPlanService {
|
|||
@Resource
|
||||
ExtTestPlanMapper extTestPlanMapper;
|
||||
@Resource
|
||||
ScheduleService scheduleService;
|
||||
@Resource
|
||||
ExtTestPlanTestCaseMapper extTestPlanTestCaseMapper;
|
||||
@Resource
|
||||
TestCaseMapper testCaseMapper;
|
||||
|
@ -315,6 +318,10 @@ public class TestPlanService {
|
|||
testPlanProjectService.deleteTestPlanProjectByPlanId(planId);
|
||||
testPlanApiCaseService.deleteByPlanId(planId);
|
||||
testPlanScenarioCaseService.deleteByPlanId(planId);
|
||||
|
||||
//删除定时任务
|
||||
scheduleService.deleteScheduleAndJobByResourceId(planId,ScheduleGroup.TEST_PLAN_TEST.name());
|
||||
|
||||
int num = testPlanMapper.deleteByPrimaryKey(planId);
|
||||
List<String> relatedUsers = new ArrayList<>();
|
||||
AddTestPlanRequest testPlans = new AddTestPlanRequest();
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 387ca56312b62ae5edb3d7f34afa08946d86d621
|
||||
Subproject commit 26d36f3f81e889f58eed7c6903252a129b301d98
|
|
@ -1,11 +1,11 @@
|
|||
create table swagger_url_project
|
||||
(
|
||||
id varchar(255) not null,
|
||||
project_id varchar(255) null,
|
||||
id varchar(30) not null,
|
||||
project_id varchar(30) null,
|
||||
swagger_url varchar(255) null,
|
||||
module_id varchar(255) null,
|
||||
module_id varchar(30) null,
|
||||
module_path varchar(255) null,
|
||||
mode_id varchar(255) null,
|
||||
mode_id varchar(30) null,
|
||||
primary key (id)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4;
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 355 KiB After Width: | Height: | Size: 169 KiB |
|
@ -14,7 +14,7 @@
|
|||
|
||||
<ms-main-container>
|
||||
<el-tabs v-model="activeName" @tab-click="addTab" @tab-remove="removeTab">
|
||||
<el-tab-pane name="default" :label="$t('api_test.automation.scenario_test')">
|
||||
<el-tab-pane name="default" :label="$t('api_test.automation.scenario_list')">
|
||||
<ms-api-scenario-list
|
||||
:module-tree="nodeTree"
|
||||
:module-options="moduleOptions"
|
||||
|
@ -97,6 +97,7 @@
|
|||
renderComponent: true,
|
||||
isHide: true,
|
||||
activeName: 'default',
|
||||
redirectFlag: 'none',
|
||||
currentModule: null,
|
||||
moduleOptions: [],
|
||||
tabs: [],
|
||||
|
@ -150,6 +151,15 @@
|
|||
},
|
||||
changeRedirectParam(redirectIDParam) {
|
||||
this.redirectID = redirectIDParam;
|
||||
if(redirectIDParam!=null){
|
||||
if(this.redirectFlag == "none"){
|
||||
this.activeName = "default";
|
||||
this.addListener();
|
||||
this.redirectFlag = "redirected";
|
||||
}
|
||||
}else{
|
||||
this.redirectFlag = "none";
|
||||
}
|
||||
},
|
||||
addTab(tab) {
|
||||
if (!getCurrentProjectID()) {
|
||||
|
@ -227,8 +237,18 @@
|
|||
this.$refs.apiScenarioList.search(data);
|
||||
},
|
||||
refresh(data) {
|
||||
this.setTabTitle(data);
|
||||
this.$refs.apiScenarioList.search(data);
|
||||
},
|
||||
setTabTitle(data) {
|
||||
for (let index in this.tabs) {
|
||||
let tab = this.tabs[index];
|
||||
if (tab.name === this.activeName) {
|
||||
tab.label = data.name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
editScenario(row) {
|
||||
this.addTab({name: 'edit', currentScenario: row});
|
||||
},
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
throw e;
|
||||
}
|
||||
this.getFails();
|
||||
this.computeTotalTime();
|
||||
this.loading = false;
|
||||
} else {
|
||||
setTimeout(this.getReport, 2000)
|
||||
|
@ -146,12 +147,30 @@
|
|||
failScenario.requestResults.push(failRequest);
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
computeTotalTime() {
|
||||
if (this.content.scenarios) {
|
||||
let startTime = 99991611737506593;
|
||||
let endTime = 0;
|
||||
this.content.scenarios.forEach((scenario) => {
|
||||
scenario.requestResults.forEach((request) => {
|
||||
if (request.startTime && Number(request.startTime) < startTime) {
|
||||
startTime = request.startTime;
|
||||
}
|
||||
if (request.endTime && Number(request.endTime) > endTime) {
|
||||
endTime = request.endTime;
|
||||
}
|
||||
})
|
||||
})
|
||||
if (startTime < endTime) {
|
||||
this.totalTime = endTime - startTime + 100;
|
||||
}
|
||||
}
|
||||
},
|
||||
requestResult(requestResult) {
|
||||
this.active();
|
||||
this.isRequestResult = false;
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<div class="request-result">
|
||||
<div>
|
||||
<el-row :gutter="10" type="flex" align="middle" class="info">
|
||||
<el-row :gutter="8" type="flex" align="middle" class="info">
|
||||
<el-col :span="2">
|
||||
<div class="method">
|
||||
{{request.method}}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="10">
|
||||
<el-col :span="8">
|
||||
<el-tooltip effect="dark" :content="request.url" placement="bottom" :open-delay="800">
|
||||
<div class="url">{{request.url}}</div>
|
||||
</el-tooltip>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="url"> {{$t('api_report.start_time')}}:{{request.startTime | timestampFormatDate(true) }}
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<el-collapse-transition>
|
||||
|
@ -19,7 +23,7 @@
|
|||
<el-tabs v-model="activeName" v-show="isActive" v-if="hasSub">
|
||||
<el-tab-pane :label="$t('api_report.sub_result')" name="sub">
|
||||
<ms-request-sub-result class="sub-result" v-for="(sub, index) in request.subRequestResults"
|
||||
:key="index" :indexNumber="index" :request="sub"/>
|
||||
:key="index" :indexNumber="index" :request="sub"/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('api_report.request_result')" name="result">
|
||||
<ms-response-text :request-type="requestType" :response="request.responseResult" :request="request"/>
|
||||
|
@ -43,7 +47,7 @@
|
|||
|
||||
export default {
|
||||
name: "MsRequestResultTail",
|
||||
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult,MsRequestSubResult},
|
||||
components: {MsResponseText, MsRequestText, MsAssertionResults, MsRequestMetric, MsRequestResult, MsRequestSubResult},
|
||||
props: {
|
||||
request: Object,
|
||||
scenarioName: String,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<el-card class="table-card" v-loading="loading">
|
||||
<template v-slot:header>
|
||||
<ms-table-header :condition.sync="condition" @search="selectByParam" title=""
|
||||
:show-create="false"/>
|
||||
:show-create="false" :tip="$t('commons.search_by_id_name_tag')"/>
|
||||
</template>
|
||||
|
||||
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table ms-select-all-fixed"
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
</div>
|
||||
<!-- 场景步骤内容 -->
|
||||
<div v-loading="loading">
|
||||
<el-tree node-key="resourceId" :props="props" :data="scenarioDefinition"
|
||||
<el-tree node-key="resourceId" :props="props" :data="scenarioDefinition" class="ms-tree"
|
||||
:default-expanded-keys="expandedNode"
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
|
@ -286,8 +286,7 @@
|
|||
},
|
||||
response: {}
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
created() {
|
||||
if (!this.currentScenario.apiScenarioModuleId) {
|
||||
this.currentScenario.apiScenarioModuleId = "";
|
||||
|
@ -848,7 +847,7 @@
|
|||
if (this.currentScenario.tags instanceof String) {
|
||||
this.currentScenario.tags = JSON.parse(this.currentScenario.tags);
|
||||
}
|
||||
this.$emit('refresh');
|
||||
this.$emit('refresh', this.currentScenario);
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -857,6 +856,9 @@
|
|||
if (this.currentScenario.tags != undefined && !(this.currentScenario.tags instanceof Array)) {
|
||||
this.currentScenario.tags = JSON.parse(this.currentScenario.tags);
|
||||
}
|
||||
if (!this.currentScenario.variables) {
|
||||
this.currentScenario.variables = [];
|
||||
}
|
||||
if (this.currentScenario.id) {
|
||||
this.result = this.$get("/api/automation/getApiScenario/" + this.currentScenario.id, response => {
|
||||
if (response.data) {
|
||||
|
@ -1037,15 +1039,27 @@
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
.ms-expanded >>> .el-tree-node__expand-icon.expanded {
|
||||
color: #7C3985;
|
||||
.ms-tree >>> .el-tree-node__expand-icon.expanded {
|
||||
-webkit-transform: rotate(0deg);
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.ms-el-icon-caret-right .el-icon-caret-right {
|
||||
color: #7C3985;
|
||||
.ms-tree >>> .el-icon-caret-right:before {
|
||||
content: '\e723';
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.ms-is-leaf >>> .is-leaf {
|
||||
.ms-tree >>> .el-tree-node__expand-icon.is-leaf {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.ms-tree >>> .el-tree-node__expand-icon {
|
||||
color: #7C3985;
|
||||
}
|
||||
|
||||
.ms-tree >>> .el-tree-node__expand-icon.expanded.el-icon-caret-right:before {
|
||||
color: #7C3985;
|
||||
content: "\e722";
|
||||
font-size: 20px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -72,4 +72,11 @@
|
|||
font-size: 12px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.el-button--text .el-link.el-link--default {
|
||||
font-size: 12px;
|
||||
color: #4b1980;
|
||||
font-weight: 400;
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -193,7 +193,10 @@ export default {
|
|||
}
|
||||
if (this.currentProtocol != null) {
|
||||
this.condition.protocol = this.currentProtocol;
|
||||
}else{
|
||||
this.condition.protocol = "HTTP";
|
||||
}
|
||||
|
||||
let url = '/api/definition/list/';
|
||||
if (this.isTestPlan) {
|
||||
url = '/api/definition/list/relevance/';
|
||||
|
|
|
@ -37,15 +37,17 @@
|
|||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header">
|
||||
<el-collapse-transition>
|
||||
<div v-if="data.active && showCollapse" :draggable="draggable">
|
||||
<fieldset :disabled="data.disabled" class="ms-fieldset">
|
||||
<el-divider></el-divider>
|
||||
<slot></slot>
|
||||
</fieldset>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</div>
|
||||
|
||||
<el-collapse-transition>
|
||||
<div v-if="data.active && showCollapse" :draggable="draggable">
|
||||
<fieldset :disabled="data.disabled" style="border: 0px">
|
||||
<el-divider></el-divider>
|
||||
<slot></slot>
|
||||
</fieldset>
|
||||
</div>
|
||||
</el-collapse-transition>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
|
@ -148,5 +150,12 @@
|
|||
.enable-switch {
|
||||
margin-right: 10px;
|
||||
}
|
||||
fieldset {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
min-width: 100%;
|
||||
min-inline-size: 0px;
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -76,18 +76,20 @@
|
|||
</el-select>
|
||||
<el-input size="small" v-model="controller.whileController.value" :placeholder="$t('api_test.value')" v-if="!hasEmptyOperator" style="width: 20%;margin-left: 20px"/>
|
||||
<span class="ms-span ms-radio">{{$t('loop.timeout')}}</span>
|
||||
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="1" :step="1000"/>
|
||||
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="3000" :step="1000"/>
|
||||
<span class="ms-span ms-radio">ms</span>
|
||||
</div>
|
||||
|
||||
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in requestResult.scenarios" :key="index">
|
||||
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
|
||||
<api-response-component :result="result"/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div>
|
||||
<el-tabs v-model="activeName" closable class="ms-tabs">
|
||||
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in requestResult.scenarios" :key="index">
|
||||
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
|
||||
<api-response-component :result="result"/>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
</div>
|
||||
|
||||
</api-base-component>
|
||||
|
||||
|
@ -331,6 +333,11 @@
|
|||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.ms-tabs >>> .el-icon-close:before {
|
||||
content: "";
|
||||
|
||||
}
|
||||
|
||||
.icon.is-active {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
this.$refs.nameInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -8,12 +8,22 @@
|
|||
<el-form :model="form" :rules="rules" ref="from">
|
||||
<el-form-item
|
||||
prop="cronValue">
|
||||
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
|
||||
:placeholder="$t('schedule.please_input_cron_expression')"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" @click="saveCron" v-tester>{{
|
||||
$t('commons.save')
|
||||
}}
|
||||
</el-button>
|
||||
<el-row>
|
||||
<el-col :span="18">
|
||||
<el-input :disabled="isReadOnly" v-model="form.cronValue" class="inp"
|
||||
:placeholder="$t('schedule.please_input_cron_expression')"/>
|
||||
<el-button :disabled="isReadOnly" type="primary" @click="saveCron" v-tester>{{
|
||||
$t('commons.save')
|
||||
}}
|
||||
</el-button>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<schedule-switch :schedule="schedule" @scheduleChange="scheduleChange"></schedule-switch>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-link :disabled="isReadOnly" type="primary" @click="showCronDialog">
|
||||
|
@ -44,6 +54,7 @@ import Crontab from "@/business/components/common/cron/Crontab";
|
|||
import CrontabResult from "@/business/components/common/cron/CrontabResult";
|
||||
import {cronValidate} from "@/common/js/cron";
|
||||
import MsScheduleNotification from "./ScheduleNotification";
|
||||
import ScheduleSwitch from "@/business/components/api/automation/schedule/ScheduleSwitch";
|
||||
|
||||
function defaultCustomValidate() {
|
||||
return {pass: true};
|
||||
|
@ -55,7 +66,7 @@ const noticeTemplate = requireComponent.keys().length > 0 ? requireComponent("./
|
|||
|
||||
export default {
|
||||
name: "MsScheduleMaintain",
|
||||
components: {CrontabResult, Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default},
|
||||
components: {CrontabResult, ScheduleSwitch,Crontab, MsScheduleNotification, "NoticeTemplate": noticeTemplate.default},
|
||||
|
||||
props: {
|
||||
customValidate: {
|
||||
|
@ -100,6 +111,7 @@ export default {
|
|||
form: {
|
||||
cronValue: ""
|
||||
},
|
||||
paramRow:{},
|
||||
activeName: 'first',
|
||||
rules: {
|
||||
cronValue: [{required: true, validator: validateCron, trigger: 'blur'}],
|
||||
|
@ -110,6 +122,35 @@ export default {
|
|||
currentUser: () => {
|
||||
return getCurrentUser();
|
||||
},
|
||||
scheduleChange(){
|
||||
let flag = this.schedule.enable;
|
||||
this.$confirm(this.$t('api_test.home_page.running_task_list.confirm.close_title'), this.$t('commons.prompt'), {
|
||||
confirmButtonText: this.$t('commons.confirm'),
|
||||
cancelButtonText: this.$t('commons.cancel'),
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
let param = {};
|
||||
param.taskID = this.schedule.id;
|
||||
param.enable = flag;
|
||||
this.updateTask(param);
|
||||
}).catch(() => {
|
||||
});
|
||||
|
||||
},
|
||||
updateTask(param){
|
||||
this.result = this.$post('/api/schedule/updateEnableByPrimyKey', param, response => {
|
||||
let paramTestId = "";
|
||||
if (this.paramRow.redirectFrom == 'testPlan') {
|
||||
paramTestId = this.paramRow.id;
|
||||
this.scheduleTaskType = "TEST_PLAN_TEST";
|
||||
} else {
|
||||
paramTestId = this.paramRow.id;
|
||||
this.scheduleTaskType = "API_SCENARIO_TEST";
|
||||
}
|
||||
this.taskID = paramTestId;
|
||||
this.findSchedule(paramTestId);
|
||||
});
|
||||
},
|
||||
initUserList() {
|
||||
let param = {
|
||||
name: '',
|
||||
|
@ -132,6 +173,7 @@ export default {
|
|||
open(row) {
|
||||
//测试计划页面跳转来的
|
||||
let paramTestId = "";
|
||||
this.paramRow = row;
|
||||
if (row.redirectFrom == 'testPlan') {
|
||||
paramTestId = row.id;
|
||||
this.scheduleTaskType = "TEST_PLAN_TEST";
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
<template>
|
||||
<div class="schedule-config">
|
||||
<div>
|
||||
<span class="cron-ico">
|
||||
<i class="el-icon-date" size="small"></i>
|
||||
<span class="character">SCHEDULER</span>
|
||||
</span>
|
||||
<!-- <el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" @change="scheduleChange"/>-->
|
||||
<!-- <el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" />-->
|
||||
<el-switch :disabled="!schedule.value" v-model="schedule.enable" @change="scheduleChange"/>
|
||||
</div>
|
||||
<div>
|
||||
<span>
|
||||
{{ $t('schedule.next_execution_time') }}:
|
||||
<span :class="{'disable-character': !schedule.enable}"
|
||||
v-if="!schedule.enable">{{ $t('schedule.not_set') }}</span>
|
||||
<crontab-result v-if="schedule.enable" :enable-simple-mode="true" :ex="schedule.value" ref="crontabResult"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CrontabResult from "@/business/components/common/cron/CrontabResult";
|
||||
|
||||
export default {
|
||||
name: "ScheduleSwitch",
|
||||
components: {CrontabResult},
|
||||
data() {
|
||||
return {
|
||||
}
|
||||
},
|
||||
props: {
|
||||
testId: String,
|
||||
schedule: Object,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
scheduleChange() {
|
||||
this.$emit('scheduleChange');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.schedule-config {
|
||||
float: right;
|
||||
width: 250px;
|
||||
height: 15px;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
.el-icon-date {
|
||||
font-size: 20px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.character {
|
||||
font-weight: bold;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.disable-character {
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.el-switch {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.cron-ico {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
</style>
|
|
@ -263,7 +263,6 @@
|
|||
this.apiTabs = [];
|
||||
this.apiDefaultTab = tabs.name;
|
||||
this.apiTabs.push(tabs);
|
||||
this.refresh();
|
||||
},
|
||||
handleTabRemove(targetName) {
|
||||
let tabs = this.apiTabs;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
</div>
|
||||
</el-row>
|
||||
|
||||
<el-dialog :title="$t('api_test.request.assertions.script')" :visible.sync="visible" width="900px">
|
||||
<el-dialog :title="$t('api_test.request.assertions.script')" :visible.sync="visible" width="900px" append-to-body>
|
||||
<el-row type="flex" justify="space-between" align="middle" class="quick-script-block">
|
||||
<div class="assertion-item input">
|
||||
<el-input size="small" v-model="assertion.variable"
|
||||
|
@ -55,206 +55,206 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {AssertionJSR223} from "../../model/ApiTestModel";
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
import MsJsr233Processor from "../../../automation/scenario/component/Jsr233Processor";
|
||||
import {AssertionJSR223} from "../../model/ApiTestModel";
|
||||
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
|
||||
import MsJsr233Processor from "../../../automation/scenario/common/Jsr233ProcessorContent";
|
||||
|
||||
export default {
|
||||
name: "MsApiAssertionJsr223",
|
||||
components: {MsJsr233Processor, MsDialogFooter},
|
||||
props: {
|
||||
assertion: {
|
||||
default: () => {
|
||||
return new AssertionJSR223();
|
||||
}
|
||||
},
|
||||
edit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
index: Number,
|
||||
list: Array,
|
||||
callback: Function,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
operators: {
|
||||
EQ: {
|
||||
label: "commons.adv_search.operators.equals",
|
||||
value: "=="
|
||||
},
|
||||
NE: {
|
||||
label: "commons.adv_search.operators.not_equals",
|
||||
value: "!="
|
||||
},
|
||||
CONTAINS: {
|
||||
label: "commons.adv_search.operators.like",
|
||||
value: "contains"
|
||||
},
|
||||
NOT_CONTAINS: {
|
||||
label: "commons.adv_search.operators.not_like",
|
||||
value: "not contains"
|
||||
},
|
||||
GT: {
|
||||
label: "commons.adv_search.operators.gt",
|
||||
value: ">"
|
||||
},
|
||||
LT: {
|
||||
label: "commons.adv_search.operators.lt",
|
||||
value: "<"
|
||||
},
|
||||
IS_EMPTY: {
|
||||
label: "commons.adv_search.operators.is_empty",
|
||||
value: "is empty"
|
||||
},
|
||||
IS_NOT_EMPTY: {
|
||||
label: "commons.adv_search.operators.is_not_empty",
|
||||
value: "is not empty"
|
||||
export default {
|
||||
name: "MsApiAssertionJsr223",
|
||||
components: {MsJsr233Processor, MsDialogFooter},
|
||||
props: {
|
||||
assertion: {
|
||||
default: () => {
|
||||
return new AssertionJSR223();
|
||||
}
|
||||
},
|
||||
templates: [
|
||||
{
|
||||
title: this.$t('api_test.request.assertions.set_failure_status'),
|
||||
value: 'AssertionResult.setFailure(true)',
|
||||
edit: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
index: Number,
|
||||
list: Array,
|
||||
callback: Function,
|
||||
isReadOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
operators: {
|
||||
EQ: {
|
||||
label: "commons.adv_search.operators.equals",
|
||||
value: "=="
|
||||
},
|
||||
NE: {
|
||||
label: "commons.adv_search.operators.not_equals",
|
||||
value: "!="
|
||||
},
|
||||
CONTAINS: {
|
||||
label: "commons.adv_search.operators.like",
|
||||
value: "contains"
|
||||
},
|
||||
NOT_CONTAINS: {
|
||||
label: "commons.adv_search.operators.not_like",
|
||||
value: "not contains"
|
||||
},
|
||||
GT: {
|
||||
label: "commons.adv_search.operators.gt",
|
||||
value: ">"
|
||||
},
|
||||
LT: {
|
||||
label: "commons.adv_search.operators.lt",
|
||||
value: "<"
|
||||
},
|
||||
IS_EMPTY: {
|
||||
label: "commons.adv_search.operators.is_empty",
|
||||
value: "is empty"
|
||||
},
|
||||
IS_NOT_EMPTY: {
|
||||
label: "commons.adv_search.operators.is_not_empty",
|
||||
value: "is not empty"
|
||||
}
|
||||
},
|
||||
{
|
||||
title: this.$t('api_test.request.assertions.set_failure_msg'),
|
||||
value: 'AssertionResult.setFailureMessage("msg")',
|
||||
templates: [
|
||||
{
|
||||
title: this.$t('api_test.request.assertions.set_failure_status'),
|
||||
value: 'AssertionResult.setFailure(true)',
|
||||
},
|
||||
{
|
||||
title: this.$t('api_test.request.assertions.set_failure_msg'),
|
||||
value: 'AssertionResult.setFailureMessage("msg")',
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
add() {
|
||||
this.list.push(new AssertionJSR223(this.assertion));
|
||||
this.callback();
|
||||
},
|
||||
remove() {
|
||||
this.list.splice(this.index, 1);
|
||||
},
|
||||
changeOperator(value) {
|
||||
if (value.indexOf("empty") > 0 && !!this.assertion.value) {
|
||||
this.assertion.value = "";
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
this.quickScript();
|
||||
},
|
||||
quickScript() {
|
||||
if (this.assertion.variable && this.assertion.operator) {
|
||||
let variable = this.assertion.variable;
|
||||
let operator = this.assertion.operator;
|
||||
let value = this.assertion.value || "";
|
||||
let desc = "${" + variable + "} " + operator + " '" + value + "'";
|
||||
let script = "value = vars.get(\"" + variable + "\");\n"
|
||||
switch (this.assertion.operator) {
|
||||
case "==":
|
||||
script += "result = \"" + value + "\".equals(value);\n";
|
||||
break;
|
||||
case "!=":
|
||||
script += "result = !\"" + value + "\".equals(value);\n";
|
||||
break;
|
||||
case "contains":
|
||||
script += "result = value.contains(\"" + value + "\");\n";
|
||||
break;
|
||||
case "not contains":
|
||||
script += "result = !value.contains(\"" + value + "\");\n";
|
||||
break;
|
||||
case ">":
|
||||
desc = "${" + variable + "} " + operator + " " + value;
|
||||
script += "number = Integer.parseInt(value);\n" +
|
||||
"result = number > " + value + ";\n";
|
||||
break;
|
||||
case "<":
|
||||
desc = "${" + variable + "} " + operator + " " + value;
|
||||
script += "number = Integer.parseInt(value);\n" +
|
||||
"result = number < " + value + ";\n";
|
||||
break;
|
||||
case "is empty":
|
||||
desc = "${" + variable + "} " + operator
|
||||
script += "result = value == void || value.length() == 0;\n";
|
||||
break;
|
||||
case "is not empty":
|
||||
desc = "${" + variable + "} " + operator
|
||||
script += "result = value != void && value.length() > 0;\n";
|
||||
break;
|
||||
}
|
||||
let msg = "assertion [" + desc + "]: false;"
|
||||
script += "if (!result){\n" +
|
||||
"\tmsg = \"" + msg + "\";\n" +
|
||||
"\tAssertionResult.setFailureMessage(msg);\n" +
|
||||
"\tAssertionResult.setFailure(true);\n" +
|
||||
"}";
|
||||
|
||||
methods: {
|
||||
add() {
|
||||
this.list.push(new AssertionJSR223(this.assertion));
|
||||
this.callback();
|
||||
},
|
||||
remove() {
|
||||
this.list.splice(this.index, 1);
|
||||
},
|
||||
changeOperator(value) {
|
||||
if (value.indexOf("empty") > 0 && !!this.assertion.value) {
|
||||
this.assertion.value = "";
|
||||
}
|
||||
this.quickScript();
|
||||
},
|
||||
quickScript() {
|
||||
if (this.assertion.variable && this.assertion.operator) {
|
||||
let variable = this.assertion.variable;
|
||||
let operator = this.assertion.operator;
|
||||
let value = this.assertion.value || "";
|
||||
let desc = "${" + variable + "} " + operator + " '" + value + "'";
|
||||
let script = "value = vars.get(\"" + variable + "\");\n"
|
||||
switch (this.assertion.operator) {
|
||||
case "==":
|
||||
script += "result = \"" + value + "\".equals(value);\n";
|
||||
break;
|
||||
case "!=":
|
||||
script += "result = !\"" + value + "\".equals(value);\n";
|
||||
break;
|
||||
case "contains":
|
||||
script += "result = value.contains(\"" + value + "\");\n";
|
||||
break;
|
||||
case "not contains":
|
||||
script += "result = !value.contains(\"" + value + "\");\n";
|
||||
break;
|
||||
case ">":
|
||||
desc = "${" + variable + "} " + operator + " " + value;
|
||||
script += "number = Integer.parseInt(value);\n" +
|
||||
"result = number > " + value + ";\n";
|
||||
break;
|
||||
case "<":
|
||||
desc = "${" + variable + "} " + operator + " " + value;
|
||||
script += "number = Integer.parseInt(value);\n" +
|
||||
"result = number < " + value + ";\n";
|
||||
break;
|
||||
case "is empty":
|
||||
desc = "${" + variable + "} " + operator
|
||||
script += "result = value == void || value.length() == 0;\n";
|
||||
break;
|
||||
case "is not empty":
|
||||
desc = "${" + variable + "} " + operator
|
||||
script += "result = value != void && value.length() > 0;\n";
|
||||
break;
|
||||
this.assertion.desc = desc;
|
||||
this.assertion.script = script;
|
||||
this.$refs.jsr233.reload();
|
||||
}
|
||||
let msg = "assertion [" + desc + "]: false;"
|
||||
script += "if (!result){\n" +
|
||||
"\tmsg = \"" + msg + "\";\n" +
|
||||
"\tAssertionResult.setFailureMessage(msg);\n" +
|
||||
"\tAssertionResult.setFailure(true);\n" +
|
||||
"}";
|
||||
|
||||
this.assertion.desc = desc;
|
||||
this.assertion.script = script;
|
||||
this.$refs.jsr233.reload();
|
||||
},
|
||||
detail() {
|
||||
this.visible = true;
|
||||
},
|
||||
close() {
|
||||
this.visible = false;
|
||||
},
|
||||
confirm() {
|
||||
if (!this.edit) {
|
||||
this.add();
|
||||
}
|
||||
if (!this.assertion.desc) {
|
||||
this.assertion.desc = this.assertion.script;
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
detail() {
|
||||
this.visible = true;
|
||||
},
|
||||
close() {
|
||||
this.visible = false;
|
||||
},
|
||||
confirm() {
|
||||
if (!this.edit) {
|
||||
this.add();
|
||||
computed: {
|
||||
hasEmptyOperator() {
|
||||
return !!this.assertion.operator && this.assertion.operator.indexOf("empty") > 0;
|
||||
}
|
||||
if (!this.assertion.desc) {
|
||||
this.assertion.desc = this.assertion.script;
|
||||
}
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
hasEmptyOperator() {
|
||||
return !!this.assertion.operator && this.assertion.operator.indexOf("empty") > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.assertion-item {
|
||||
display: inline-block;
|
||||
}
|
||||
.assertion-item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.assertion-item + .assertion-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.assertion-item + .assertion-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.assertion-item.input {
|
||||
width: 100%;
|
||||
}
|
||||
.assertion-item.input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.assertion-item.select {
|
||||
min-width: 150px;
|
||||
}
|
||||
.assertion-item.select {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.assertion-item.label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.assertion-item.label {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.assertion-item.btn {
|
||||
min-width: 130px;
|
||||
}
|
||||
.assertion-item.btn {
|
||||
min-width: 130px;
|
||||
}
|
||||
|
||||
.assertion-item.btn.circle {
|
||||
text-align: right;
|
||||
min-width: 80px;
|
||||
}
|
||||
.assertion-item.btn.circle {
|
||||
text-align: right;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.quick-script-block {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.quick-script-block {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
<el-dialog
|
||||
:title="$t('api_test.environment.select_environment')"
|
||||
:visible.sync="dialogVisible"
|
||||
width="25%"
|
||||
width="15%"
|
||||
:destroy-on-close="true"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form label-position="right" label-width="150px" size="medium" ref="form">
|
||||
<el-form ref="form">
|
||||
<el-form-item prop="type">
|
||||
<el-select v-model="environmentId" value-key="id" size="small" class="ms-htt-width"
|
||||
<el-select v-model="environmentId" value-key="id" class="ms-htt-width"
|
||||
:placeholder="$t('api_test.definition.request.run_env')"
|
||||
clearable>
|
||||
<el-option v-for="(environment, index) in environments" :key="index"
|
||||
|
@ -22,6 +22,7 @@
|
|||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template v-slot:footer>
|
||||
<!-- <el-button onclick="this.handleClose">{{ $t('commons.cancel') }}</el-button>-->
|
||||
<el-button type="primary" @click="createPerformance" @keydown.enter.native.prevent>
|
||||
|
|
|
@ -212,7 +212,8 @@
|
|||
this.$emit('singleRun', data);
|
||||
},
|
||||
copyCase(data) {
|
||||
let obj = {name: "copy_" + data.name, priority: data.priority, active: true, request: data.request};
|
||||
let uuid = getUUID();
|
||||
let obj = {name: "copy_" + data.name, priority: data.priority, active: true, tags: data.tags, request: data.request, uuid: uuid};
|
||||
this.$emit('copyCase', obj);
|
||||
},
|
||||
selectTestCase(item, $event) {
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
<el-container v-loading="result.loading">
|
||||
<el-main>
|
||||
<div v-for="(item,index) in apiCaseList" :key="index">
|
||||
<div v-for="(item,index) in apiCaseList" :key="item.id ? item.id : item.uuid">
|
||||
<api-case-item v-loading="singleLoading && singleRunId === item.id || batchLoadingIds.indexOf(item.id) > -1"
|
||||
@refresh="refresh"
|
||||
@singleRun="singleRun"
|
||||
|
@ -243,7 +243,8 @@
|
|||
if (!request.hashTree) {
|
||||
request.hashTree = [];
|
||||
}
|
||||
let obj = {apiDefinitionId: this.api.id, name: '', priority: 'P0', active: true, tags: []};
|
||||
let uuid = getUUID();
|
||||
let obj = {apiDefinitionId: this.api.id, name: '', priority: 'P0', active: true, tags: [], uuid: uuid};
|
||||
obj.request = request;
|
||||
this.apiCaseList.unshift(obj);
|
||||
}
|
||||
|
|
|
@ -50,8 +50,9 @@
|
|||
<el-switch
|
||||
v-model="swaggerSynchronization"
|
||||
@click.native="scheduleEdit"
|
||||
:active-text="$t('api_test.api_import.timing_synchronization')">
|
||||
>
|
||||
</el-switch>
|
||||
<span style="color: #6C317C;cursor: pointer;font-weight: bold;margin-left: 10px" @click="scheduleEditByText">{{$t('api_test.api_import.timing_synchronization')}}</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12"
|
||||
|
@ -188,6 +189,9 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
scheduleEditByText(){
|
||||
this.$refs.scheduleEdit.open(this.buildParam());
|
||||
},
|
||||
open(module) {
|
||||
this.currentModule = module;
|
||||
this.visible = true;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
@isApiListEnableChange="isApiListEnableChange">
|
||||
<el-link type="primary" style="float:right;margin-top: 5px" @click="open">{{$t('commons.adv_search.title')}}</el-link>
|
||||
|
||||
<el-input placeholder="搜索" @blur="search" @keyup.enter.native="search" class="search-input" size="small"
|
||||
<el-input :placeholder="$t('commons.search_by_id_name_tag')" @blur="search" @keyup.enter.native="search" class="search-input" size="small"
|
||||
v-model="condition.name"/>
|
||||
|
||||
<el-table v-loading="result.loading"
|
||||
|
@ -124,7 +124,7 @@ 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 {API_METHOD_COLOUR, CASE_PRIORITY, DUBBO_METHOD, REQ_METHOD, SQL_METHOD, TCP_METHOD} from "../../model/JsonData";
|
||||
|
||||
import {getBodyUploadFiles, getCurrentProjectID} from "@/common/js/utils";
|
||||
import ApiListContainer from "./ApiListContainer";
|
||||
|
@ -409,6 +409,15 @@ export default {
|
|||
// }
|
||||
},
|
||||
handleEditBatch() {
|
||||
if(this.currentProtocol =='HTTP'){
|
||||
this.valueArr.method = REQ_METHOD;
|
||||
}else if(this.currentProtocol =='TCP'){
|
||||
this.valueArr.method = TCP_METHOD;
|
||||
}else if(this.currentProtocol =='SQL'){
|
||||
this.valueArr.method = SQL_METHOD;
|
||||
}else if(this.currentProtocol =='DUBBO'){
|
||||
this.valueArr.method = DUBBO_METHOD;
|
||||
}
|
||||
this.$refs.batchEdit.open();
|
||||
},
|
||||
batchEdit(form) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
@isApiListEnableChange="isApiListEnableChange">
|
||||
|
||||
<el-link type="primary" @click="open" style="float: right;margin-top: 5px">{{$t('commons.adv_search.title')}}</el-link>
|
||||
<el-input :placeholder="$t('api_monitor.please_search')" @blur="search" class="search-input" size="small" @keyup.enter.native="search"
|
||||
<el-input :placeholder="$t('commons.search_by_id_name_tag')" @blur="search" class="search-input" size="small" @keyup.enter.native="search"
|
||||
v-model="condition.name"/>
|
||||
|
||||
<el-table v-loading="result.loading"
|
||||
|
@ -162,11 +162,10 @@
|
|||
import MsBottomContainer from "../BottomContainer";
|
||||
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
|
||||
import MsBatchEdit from "../basis/BatchEdit";
|
||||
import {API_METHOD_COLOUR, API_STATUS, REQ_METHOD} from "../../model/JsonData";
|
||||
import {API_METHOD_COLOUR, API_STATUS, REQ_METHOD,TCP_METHOD,SQL_METHOD,DUBBO_METHOD} from "../../model/JsonData";
|
||||
import {_filter, _sort, getCurrentProjectID} from "@/common/js/utils";
|
||||
import {WORKSPACE_ID} from '@/common/js/constants';
|
||||
import ApiListContainer from "./ApiListContainer";
|
||||
// import MsTableSelectAll from "../../../../common/components/table/MsTableSelectAll";
|
||||
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
|
||||
import ApiStatus from "@/business/components/api/definition/components/list/ApiStatus";
|
||||
import MsTableAdvSearchBar from "@/business/components/common/components/search/MsTableAdvSearchBar";
|
||||
|
@ -274,7 +273,12 @@
|
|||
},
|
||||
},
|
||||
created: function () {
|
||||
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
|
||||
if (this.trashEnable) {
|
||||
this.condition.filters = {status: ["Trash"]};
|
||||
}
|
||||
else {
|
||||
this.condition.filters = {status: ["Prepare", "Underway", "Completed"]};
|
||||
}
|
||||
this.initTable();
|
||||
this.getMaintainerOptions();
|
||||
},
|
||||
|
@ -336,10 +340,10 @@
|
|||
}
|
||||
if (this.condition.projectId) {
|
||||
this.result = this.$post("/api/definition/list/" + this.currentPage + "/" + this.pageSize, this.condition, response => {
|
||||
this.genProtocalFilter(this.condition.protocol);
|
||||
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);
|
||||
|
@ -348,6 +352,48 @@
|
|||
});
|
||||
}
|
||||
},
|
||||
genProtocalFilter(protocalType){
|
||||
if(protocalType === "HTTP"){
|
||||
this.methodFilters = [
|
||||
{text: 'GET', value: 'GET'},
|
||||
{text: 'POST', value: 'POST'},
|
||||
{text: 'PUT', value: 'PUT'},
|
||||
{text: 'PATCH', value: 'PATCH'},
|
||||
{text: 'DELETE', value: 'DELETE'},
|
||||
{text: 'OPTIONS', value: 'OPTIONS'},
|
||||
{text: 'HEAD', value: 'HEAD'},
|
||||
{text: 'CONNECT', value: 'CONNECT'},
|
||||
];
|
||||
}else if(protocalType === "TCP"){
|
||||
this.methodFilters = [
|
||||
{text: 'TCP', value: 'TCP'},
|
||||
];
|
||||
}else if(protocalType === "SQL"){
|
||||
this.methodFilters = [
|
||||
{text: 'SQL', value: 'SQL'},
|
||||
];
|
||||
}else if(protocalType === "DUBBO"){
|
||||
this.methodFilters = [
|
||||
{text: 'DUBBO', value: 'DUBBO'},
|
||||
{text: 'dubbo://', value: 'dubbo://'},
|
||||
];
|
||||
}else{
|
||||
this.methodFilters = [
|
||||
{text: 'GET', value: 'GET'},
|
||||
{text: 'POST', value: 'POST'},
|
||||
{text: 'PUT', value: 'PUT'},
|
||||
{text: 'PATCH', value: 'PATCH'},
|
||||
{text: 'DELETE', value: 'DELETE'},
|
||||
{text: 'OPTIONS', value: 'OPTIONS'},
|
||||
{text: 'HEAD', value: 'HEAD'},
|
||||
{text: 'CONNECT', value: 'CONNECT'},
|
||||
{text: 'DUBBO', value: 'DUBBO'},
|
||||
{text: 'dubbo://', value: 'dubbo://'},
|
||||
{text: 'SQL', value: 'SQL'},
|
||||
{text: 'TCP', value: 'TCP'},
|
||||
];
|
||||
}
|
||||
},
|
||||
getMaintainerOptions() {
|
||||
let workspaceId = localStorage.getItem(WORKSPACE_ID);
|
||||
this.$post('/user/ws/member/tester/list', {workspaceId: workspaceId}, response => {
|
||||
|
@ -466,6 +512,15 @@
|
|||
}
|
||||
},
|
||||
handleEditBatch() {
|
||||
if(this.currentProtocol =='HTTP'){
|
||||
this.valueArr.method = REQ_METHOD;
|
||||
}else if(this.currentProtocol =='TCP'){
|
||||
this.valueArr.method = TCP_METHOD;
|
||||
}else if(this.currentProtocol =='SQL'){
|
||||
this.valueArr.method = SQL_METHOD;
|
||||
}else if(this.currentProtocol =='DUBBO'){
|
||||
this.valueArr.method = DUBBO_METHOD;
|
||||
}
|
||||
this.$refs.batchEdit.open();
|
||||
},
|
||||
batchEdit(form) {
|
||||
|
|
|
@ -180,10 +180,10 @@
|
|||
}
|
||||
},
|
||||
//创建根目录的模块---供父类使用
|
||||
createRootModel(){
|
||||
createRootModel() {
|
||||
let dataArr = this.$refs.nodeTree.extendTreeNodes;
|
||||
if(dataArr.length>0){
|
||||
this.$refs.nodeTree.append({},dataArr[0]);
|
||||
if (dataArr.length > 0) {
|
||||
this.$refs.nodeTree.append({}, dataArr[0]);
|
||||
}
|
||||
},
|
||||
exportAPI() {
|
||||
|
@ -197,7 +197,6 @@
|
|||
},
|
||||
refresh() {
|
||||
this.list();
|
||||
this.$emit("refreshTable");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,22 +64,22 @@
|
|||
</el-form>
|
||||
</div>
|
||||
<!--<div v-if="showScript">-->
|
||||
<!--<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"/>-->
|
||||
<!--<!–断言规则–>-->
|
||||
<!--<div style="margin-top: 10px">-->
|
||||
<!--<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>-->
|
||||
<!--</div>-->
|
||||
<!--<!–提取规则–>-->
|
||||
<!--<div style="margin-top: 10px">-->
|
||||
<!--<ms-api-extract :is-read-only="isReadOnly" @copyRow="copyRow" @remove="remove" v-if="row.type==='Extract'" :extract="row"/>-->
|
||||
<!--</div>-->
|
||||
<!--</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"/>-->
|
||||
<!--<!–断言规则–>-->
|
||||
<!--<div style="margin-top: 10px">-->
|
||||
<!--<ms-api-assertions v-if="row.type==='Assertions'" @copyRow="copyRow" @remove="remove" :is-read-only="isReadOnly" :assertions="row"/>-->
|
||||
<!--</div>-->
|
||||
<!--<!–提取规则–>-->
|
||||
<!--<div style="margin-top: 10px">-->
|
||||
<!--<ms-api-extract :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="showScript">
|
||||
|
@ -225,6 +225,9 @@
|
|||
if (!hasEnvironment) {
|
||||
this.request.environmentId = undefined;
|
||||
}
|
||||
if (!this.request.environmentId) {
|
||||
this.request.dataSourceId = undefined;
|
||||
}
|
||||
this.initDataSource();
|
||||
});
|
||||
},
|
||||
|
@ -232,15 +235,22 @@
|
|||
this.$refs.environmentConfig.open(getCurrentProjectID());
|
||||
},
|
||||
initDataSource() {
|
||||
let flag = false;
|
||||
for (let i in this.environments) {
|
||||
if (this.environments[i].id === this.request.environmentId) {
|
||||
this.databaseConfigsOptions = [];
|
||||
this.environments[i].config.databaseConfigs.forEach(item => {
|
||||
if (item.id === this.request.dataSourceId) {
|
||||
flag = true;
|
||||
}
|
||||
this.databaseConfigsOptions.push(item);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
this.request.dataSourceId = undefined;
|
||||
}
|
||||
},
|
||||
setDataSource() {
|
||||
for (let item of this.databaseConfigsOptions) {
|
||||
|
|
|
@ -86,10 +86,9 @@
|
|||
import ApiRequestMethodSelect from "../../collapse/ApiRequestMethodSelect";
|
||||
import {REQUEST_HEADERS} from "@/common/js/constants";
|
||||
import MsApiVariable from "../../ApiVariable";
|
||||
import {createComponent} from "../../jmeter/components";
|
||||
import MsApiAssertions from "../../assertion/ApiAssertions";
|
||||
import MsApiExtract from "../../extract/ApiExtract";
|
||||
import {Assertions, Body, Extract, KeyValue} from "../../../model/ApiTestModel";
|
||||
import {Body, KeyValue} from "../../../model/ApiTestModel";
|
||||
import {getUUID} from "@/common/js/utils";
|
||||
import BatchAddParameter from "../../basis/BatchAddParameter";
|
||||
import MsApiAdvancedConfig from "./ApiAdvancedConfig";
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="$t('api_test.definition.request.response_header')" name="headers" class="pane">
|
||||
<pre>{{ response.responseResult.headers }}</pre>
|
||||
<div style="width: 400px">
|
||||
<pre>{{ response.responseResult.headers }}</pre>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<!--<el-tab-pane label="Cookie" name="cookie" class="pane cookie">-->
|
||||
<!--<pre>{{response.cookies}}</pre>-->
|
||||
|
@ -25,7 +27,9 @@
|
|||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="$t('api_test.request.extract.label')" name="label" class="pane">
|
||||
<pre>{{response.responseResult.vars}}</pre>
|
||||
<div style="width: 400px">
|
||||
<pre>{{response.responseResult.vars}}</pre>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="$t('api_report.request_body')" name="request_body" class="pane">
|
||||
|
|
|
@ -44,6 +44,15 @@ export const REQ_METHOD = [
|
|||
{id: 'HEAD', label: 'HEAD'},
|
||||
{id: 'CONNECT', label: 'CONNECT'}
|
||||
]
|
||||
export const TCP_METHOD = [
|
||||
{id: 'TCP', label: 'TCP'}
|
||||
]
|
||||
export const SQL_METHOD = [
|
||||
{id: 'SQL', label: 'SQL'}
|
||||
]
|
||||
export const DUBBO_METHOD = [
|
||||
{id: 'dubbo://', label: 'dubbo://'},
|
||||
]
|
||||
|
||||
export const CASE_PRIORITY = [
|
||||
{id: 'P0', label: 'P0'},
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-circle-plus-outline" @click="add">添加
|
||||
</el-button>
|
||||
<el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-circle-plus-outline" @click="add">{{$t("commons.add")}}</el-button>
|
||||
<el-button class="ht-btn-add" size="mini" p="$t('commons.add')" icon="el-icon-files" @click="copy">{{$t("commons.copy")}}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -68,6 +68,16 @@
|
|||
this.$emit('change', this.hostTable);
|
||||
},
|
||||
add: function (r) {
|
||||
let row = {
|
||||
ip: '',
|
||||
domain: '',
|
||||
status: 'edit',
|
||||
annotation: '',
|
||||
uuid: this.uuid(),
|
||||
}
|
||||
this.hostTable.push(row);
|
||||
},
|
||||
copy: function (r) {
|
||||
let row = {
|
||||
ip: '',
|
||||
domain: '',
|
||||
|
|
|
@ -15,23 +15,6 @@
|
|||
props: ['total', 'pageSize'],
|
||||
data() {
|
||||
return {
|
||||
gridData: [{
|
||||
date: '2016-05-02',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-04',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-01',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}, {
|
||||
date: '2016-05-03',
|
||||
name: '王小虎',
|
||||
address: '上海市普陀区金沙江路 1518 弄'
|
||||
}]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<span v-if="triggerMode === 'SCHEDULE'">{{$t('commons.trigger_mode.schedule')}}</span>
|
||||
<span v-if="triggerMode === 'TEST_PLAN_SCHEDULE'">{{$t('commons.trigger_mode.schedule')}}</span>
|
||||
<span v-if="triggerMode === 'API'">{{$t('commons.trigger_mode.api')}}</span>
|
||||
<span v-if="triggerMode === 'CASE'">用例触发</span>
|
||||
<span v-if="triggerMode === 'CASE'">{{$t('commons.trigger_mode.case')}}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ export default {
|
|||
{text: this.$t('commons.trigger_mode.manual'), value: 'MANUAL'},
|
||||
{text: this.$t('commons.trigger_mode.schedule'), value: 'SCHEDULE'},
|
||||
{text: this.$t('commons.trigger_mode.api'), value: 'API'},
|
||||
{text: this.$t('commons.trigger_mode.case'), value: 'CASE'},
|
||||
],
|
||||
buttons: [
|
||||
{
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<el-tab-pane v-if="hasLicense()" :label="$t('display.title')" name="display">
|
||||
<ms-display/>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="hasLicense()" :label="'认证设置'" name="auth">
|
||||
<el-tab-pane v-if="hasLicense()" :label="$t('auth_source.title')" name="auth">
|
||||
<ms-auth/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
if (size) {
|
||||
this.size = size;
|
||||
} else {
|
||||
this.size = this.$parent.selectRows.size;
|
||||
// this.size = this.$parent.selectRows.size;
|
||||
this.size = this.$parent.selectDataCounts;
|
||||
}
|
||||
listenGoBack(this.handleClose);
|
||||
},
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
:filter-node-method="filterNode"
|
||||
:expand-on-click-node="false"
|
||||
highlight-current
|
||||
style="overflow: auto"
|
||||
@node-click="nodeClick"
|
||||
ref="tree"
|
||||
>
|
||||
|
|
|
@ -31,14 +31,14 @@
|
|||
@select="handleSelect"
|
||||
@cell-mouse-enter="showPopover"
|
||||
row-key="id"
|
||||
class="test-content adjust-table ms-select-all"
|
||||
class="test-content adjust-table ms-select-all-fixed"
|
||||
ref="table" @row-click="handleEdit">
|
||||
|
||||
<el-table-column
|
||||
width="50"
|
||||
type="selection"/>
|
||||
|
||||
<ms-table-select-all
|
||||
<ms-table-header-select-popover v-show="total>0"
|
||||
:page-size="pageSize > total ? total : pageSize"
|
||||
:total="total"
|
||||
@selectPageAll="isSelectDataAll(false)"
|
||||
|
@ -46,7 +46,7 @@
|
|||
|
||||
<el-table-column width="40" :resizable="false" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectRows.size"/>
|
||||
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectDataCounts"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
|
@ -78,6 +78,7 @@
|
|||
prop="priority"
|
||||
:filters="priorityFilters"
|
||||
column-key="priority"
|
||||
min-width="100px"
|
||||
:label="$t('test_track.case.priority')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
|
@ -98,6 +99,7 @@
|
|||
prop="method"
|
||||
column-key="method"
|
||||
:filters="methodFilters"
|
||||
min-width="100px"
|
||||
:label="$t('test_track.case.method')"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
|
@ -108,6 +110,7 @@
|
|||
<el-table-column
|
||||
:filters="statusFilters"
|
||||
column-key="status"
|
||||
min-width="100px"
|
||||
:label="$t('test_track.case.status')">
|
||||
<template v-slot:default="scope">
|
||||
<span class="el-dropdown-link">
|
||||
|
@ -127,6 +130,7 @@
|
|||
<el-table-column
|
||||
prop="nodePath"
|
||||
:label="$t('test_track.case.module')"
|
||||
min-width="150px"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
|
||||
|
@ -134,12 +138,13 @@
|
|||
prop="updateTime"
|
||||
sortable="custom"
|
||||
:label="$t('commons.update_time')"
|
||||
min-width="150px"
|
||||
show-overflow-tooltip>
|
||||
<template v-slot:default="scope">
|
||||
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
<el-table-column fixed="right"
|
||||
:label="$t('commons.operating')" min-width="150">
|
||||
<template v-slot:default="scope">
|
||||
<ms-table-operator :is-tester-permission="true" @editClick="handleEdit(scope.row)"
|
||||
|
@ -169,6 +174,7 @@
|
|||
<script>
|
||||
|
||||
import MsCreateBox from '../../../settings/CreateBox';
|
||||
import MsTableHeaderSelectPopover from "@/business/components/common/components/table/MsTableHeaderSelectPopover";
|
||||
import TestCaseImport from '../components/TestCaseImport';
|
||||
import TestCaseExport from '../components/TestCaseExport';
|
||||
import MsTablePagination from '../../../../components/common/pagination/TablePagination';
|
||||
|
@ -191,7 +197,6 @@ import TestCaseDetail from "./TestCaseDetail";
|
|||
import ReviewStatus from "@/business/components/track/case/components/ReviewStatus";
|
||||
import {getCurrentProjectID} from "../../../../../common/js/utils";
|
||||
import MsTag from "@/business/components/common/components/MsTag";
|
||||
import MsTableSelectAll from "../../../common/components/table/MsTableSelectAll";
|
||||
import {_handleSelect, _handleSelectAll} from "../../../../../common/js/tableUtils";
|
||||
import BatchMove from "./BatchMove";
|
||||
|
||||
|
@ -199,7 +204,7 @@ export default {
|
|||
name: "TestCaseList",
|
||||
components: {
|
||||
BatchMove,
|
||||
MsTableSelectAll,
|
||||
MsTableHeaderSelectPopover,
|
||||
MsTableButton,
|
||||
MsTableOperatorButton,
|
||||
MsTableOperator,
|
||||
|
@ -287,6 +292,7 @@ export default {
|
|||
},
|
||||
currentCaseId: null,
|
||||
projectId: "",
|
||||
selectDataCounts: 0,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -323,6 +329,7 @@ export default {
|
|||
this.condition.nodeIds = [];
|
||||
this.condition.selectAll = false;
|
||||
this.condition.unSelectIds = [];
|
||||
this.selectDataCounts = 0;
|
||||
if (this.planId) {
|
||||
// param.planId = this.planId;
|
||||
this.condition.planId = this.planId;
|
||||
|
@ -430,6 +437,7 @@ export default {
|
|||
_handleSelect(this, selection, row, this.selectRows);
|
||||
this.setUnSelectIds();
|
||||
},
|
||||
|
||||
importTestCase() {
|
||||
if (!getCurrentProjectID()) {
|
||||
this.$warning(this.$t('commons.check_project_tip'));
|
||||
|
@ -548,6 +556,11 @@ export default {
|
|||
this.condition.unSelectIds = allIDs.filter(function (val) {
|
||||
return ids.indexOf(val) === -1
|
||||
});
|
||||
if (this.condition.selectAll) {
|
||||
this.selectDataCounts = this.total - this.condition.unSelectIds.length;
|
||||
} else {
|
||||
this.selectDataCounts = this.selectRows.size;
|
||||
}
|
||||
},
|
||||
moveSave(param) {
|
||||
param.condition = this.condition;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px">
|
||||
<template v-slot:default="scope">
|
||||
<div v-for="itemName in scope.row.tagNames" :key="itemName">
|
||||
<div v-for="itemName in scope.row.tags" :key="itemName">
|
||||
<ms-tag type="success" effect="plain" :content="itemName"/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -89,9 +89,6 @@
|
|||
selectRows: new Set()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.search();
|
||||
},
|
||||
watch: {
|
||||
selectNodeIds() {
|
||||
this.search();
|
||||
|
@ -102,6 +99,9 @@
|
|||
},
|
||||
methods: {
|
||||
search() {
|
||||
if (!this.projectId) {
|
||||
return;
|
||||
}
|
||||
this.selectRows = new Set();
|
||||
this.loading = true;
|
||||
|
||||
|
@ -122,6 +122,11 @@
|
|||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
this.tableData = data.listObject;
|
||||
this.tableData.forEach(item => {
|
||||
if (item.tags && item.tags.length > 0) {
|
||||
item.tags = JSON.parse(item.tags);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
handleSelectAll(selection) {
|
||||
|
|
|
@ -75,6 +75,9 @@
|
|||
methods: {
|
||||
open() {
|
||||
this.$refs.baseRelevance.open();
|
||||
if (this.$refs.apiScenarioList) {
|
||||
this.$refs.apiScenarioList.search();
|
||||
}
|
||||
},
|
||||
setProject(projectId) {
|
||||
this.projectId = projectId;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
<show-more-btn :is-show="isSelect(row)" :buttons="buttons" :size="selectRows.length"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="num" label="ID"/>
|
||||
<el-table-column prop="name" :label="$t('api_test.automation.scenario_name')"
|
||||
show-overflow-tooltip/>
|
||||
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
|
||||
|
@ -29,7 +30,7 @@
|
|||
</el-table-column>
|
||||
<el-table-column prop="tagNames" :label="$t('api_test.automation.tag')" width="200px">
|
||||
<template v-slot:default="scope">
|
||||
<div v-for="itemName in scope.row.tagNames" :key="itemName">
|
||||
<div v-for="(itemName,index) in scope.row.tags" :key="index">
|
||||
<ms-tag type="success" effect="plain" :content="itemName"/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -159,6 +160,11 @@
|
|||
let data = response.data;
|
||||
this.total = data.itemCount;
|
||||
this.tableData = data.listObject;
|
||||
this.tableData.forEach(item => {
|
||||
if (item.tags && item.tags.length > 0) {
|
||||
item.tags = JSON.parse(item.tags);
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
:condition="condition"
|
||||
@search="$emit('refresh')"
|
||||
:show-create="false"
|
||||
:tip="$t('commons.search_by_name_or_id')">
|
||||
:tip="$t('commons.search_by_id_name_tag')">
|
||||
<template v-slot:title>
|
||||
场景用例
|
||||
</template>
|
||||
|
|
|
@ -76,6 +76,14 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="tags" :label="$t('commons.tag')">
|
||||
<template v-slot:default="scope">
|
||||
<div v-for="(tag, index) in scope.row.showTags" :key="tag + '_' + index">
|
||||
<ms-tag type="success" effect="plain" :content="tag"/>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
prop="method"
|
||||
:filters="methodFilters"
|
||||
|
@ -179,8 +187,9 @@
|
|||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
min-width="100"
|
||||
:label="$t('commons.operating')">
|
||||
fixed="right"
|
||||
min-width="100"
|
||||
:label="$t('commons.operating')">
|
||||
<template v-slot:default="scope">
|
||||
<ms-table-operator-button :is-tester-permission="true" :tip="$t('commons.edit')" icon="el-icon-edit"
|
||||
@exec="handleEdit(scope.row)"/>
|
||||
|
@ -231,6 +240,7 @@ import ShowMoreBtn from "../../../../case/components/ShowMoreBtn";
|
|||
import BatchEdit from "../../../../case/components/BatchEdit";
|
||||
import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
|
||||
import {hub} from "@/business/components/track/plan/event-bus";
|
||||
import MsTag from "@/business/components/common/components/MsTag";
|
||||
|
||||
export default {
|
||||
name: "FunctionalTestCaseList",
|
||||
|
@ -243,7 +253,7 @@ export default {
|
|||
StatusTableItem,
|
||||
PriorityTableItem, StatusEdit, ExecutorEdit, MsTipButton, MsTablePagination,
|
||||
MsTableHeader, NodeBreadcrumb, MsTableButton, ShowMoreBtn,
|
||||
BatchEdit
|
||||
BatchEdit, MsTag
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -257,7 +267,7 @@ export default {
|
|||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
status:'default',
|
||||
status: 'default',
|
||||
selectRows: new Set(),
|
||||
testPlan: {},
|
||||
isReadOnly: false,
|
||||
|
@ -319,7 +329,7 @@ export default {
|
|||
planId: {
|
||||
type: String
|
||||
},
|
||||
clickType:String,
|
||||
clickType: String,
|
||||
selectNodeIds: {
|
||||
type: Array
|
||||
},
|
||||
|
@ -354,10 +364,10 @@ export default {
|
|||
// param.planId = this.planId;
|
||||
this.condition.planId = this.planId;
|
||||
}
|
||||
if(this.clickType){
|
||||
if(this.status =='default'){
|
||||
if (this.clickType) {
|
||||
if (this.status == 'default') {
|
||||
this.condition.status = this.clickType;
|
||||
}else{
|
||||
} else {
|
||||
this.condition.status = null;
|
||||
}
|
||||
this.status = 'all';
|
||||
|
@ -373,6 +383,7 @@ export default {
|
|||
this.tableData = data.listObject;
|
||||
for (let i = 0; i < this.tableData.length; i++) {
|
||||
if (this.tableData[i]) {
|
||||
this.$set(this.tableData[i], "showTags", JSON.parse(this.tableData[i].tags));
|
||||
this.$set(this.tableData[i], "issuesSize", 0);
|
||||
this.$get("/issues/get/" + this.tableData[i].caseId).then(response => {
|
||||
let issues = response.data.data;
|
||||
|
@ -381,7 +392,11 @@ export default {
|
|||
this.$set(this.tableData[i], "issuesContent", issues);
|
||||
}
|
||||
}).catch(() => {
|
||||
this.$set(this.tableData[i], "issuesContent", [{title: '获取缺陷失败',description: '获取缺陷失败',platform: '获取缺陷失败' }]);
|
||||
this.$set(this.tableData[i], "issuesContent", [{
|
||||
title: '获取缺陷失败',
|
||||
description: '获取缺陷失败',
|
||||
platform: '获取缺陷失败'
|
||||
}]);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -597,4 +612,7 @@ export default {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -32,7 +32,7 @@ export default {
|
|||
operating: 'Operating',
|
||||
input_limit: 'Within {0} and {1} characters',
|
||||
login: 'Sign In',
|
||||
welcome: 'Welcome back, please enter username and password to log in',
|
||||
welcome: 'One-stop open source continuous testing platform',
|
||||
username: 'Username',
|
||||
password: 'Password',
|
||||
input_username: 'Please enter username',
|
||||
|
@ -150,7 +150,8 @@ export default {
|
|||
name: "Trigger Mode",
|
||||
manual: "Manual trigger",
|
||||
schedule: "Scheduled Task",
|
||||
api: "API call"
|
||||
api: "API call",
|
||||
case: "Case"
|
||||
},
|
||||
adv_search: {
|
||||
title: 'Advanced Search',
|
||||
|
@ -602,6 +603,7 @@ export default {
|
|||
customize_req: "Customize req",
|
||||
reference_info: "Reference info",
|
||||
scenario_test: "Scenario test",
|
||||
scenario_list: "Scenario List",
|
||||
add_scenario: "Add scenario",
|
||||
scenario_name: "Scenario name",
|
||||
case_level: "Case level",
|
||||
|
@ -1499,6 +1501,7 @@ export default {
|
|||
format: "Output format",
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: 'This operation will delete the authentication source, do you want to continue? '
|
||||
delete_prompt: 'This operation will delete the authentication source, do you want to continue? ',
|
||||
title: 'Auth Source'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ export default {
|
|||
operating: '操作',
|
||||
input_limit: '长度在 {0} 到 {1} 个字符',
|
||||
login: '登录',
|
||||
welcome: '欢迎回来,请输入用户名和密码登录',
|
||||
welcome: '一站式开源持续测试平台',
|
||||
username: '姓名',
|
||||
password: '密码',
|
||||
input_username: '请输入用户姓名',
|
||||
|
@ -151,7 +151,8 @@ export default {
|
|||
name: "触发方式",
|
||||
manual: "手动触发",
|
||||
schedule: "定时任务",
|
||||
api: "API调用"
|
||||
api: "API调用",
|
||||
case: "用例触发"
|
||||
},
|
||||
adv_search: {
|
||||
title: '高级搜索',
|
||||
|
@ -603,6 +604,7 @@ export default {
|
|||
customize_req: "自定义请求",
|
||||
reference_info: "请选择接口或用例",
|
||||
scenario_test: "场景",
|
||||
scenario_list: "场景列表",
|
||||
add_scenario: "创建场景",
|
||||
scenario_name: "场景名称",
|
||||
case_level: "用例等级",
|
||||
|
@ -1502,6 +1504,7 @@ export default {
|
|||
format: "输出格式",
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: '此操作会删除认证源,是否继续?'
|
||||
delete_prompt: '此操作会删除认证源,是否继续?',
|
||||
title: '认证设置'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -33,7 +33,7 @@ export default {
|
|||
operating: '操作',
|
||||
input_limit: '長度在 {0} 到 {1} 個字符',
|
||||
login: '登錄',
|
||||
welcome: '歡迎回來,請輸入用戶名和密碼登錄',
|
||||
welcome: '壹站式開源持續測試平臺',
|
||||
username: '姓名',
|
||||
password: '密碼',
|
||||
input_username: '請輸入用戶姓名',
|
||||
|
@ -151,7 +151,8 @@ export default {
|
|||
name: "觸發方式",
|
||||
manual: "手動觸發",
|
||||
schedule: "定時任務",
|
||||
api: "API調用"
|
||||
api: "API調用",
|
||||
case: "用例觸發"
|
||||
},
|
||||
adv_search: {
|
||||
title: '高級搜索',
|
||||
|
@ -602,6 +603,7 @@ export default {
|
|||
customize_req: "自定義請求",
|
||||
reference_info: "請選擇接口或用例",
|
||||
scenario_test: "場景",
|
||||
scenario_list: "場景列表",
|
||||
add_scenario: "創建場景",
|
||||
scenario_name: "場景名稱",
|
||||
case_level: "用例等級",
|
||||
|
@ -1500,6 +1502,7 @@ export default {
|
|||
format: "輸出格式",
|
||||
},
|
||||
auth_source: {
|
||||
delete_prompt: '此操作會刪除認證源,是否繼續? '
|
||||
delete_prompt: '此操作會刪除認證源,是否繼續? ',
|
||||
title: '認證設置'
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,33 +2,36 @@
|
|||
<div class="container" v-loading="result.loading" v-if="ready">
|
||||
<el-row type="flex">
|
||||
<el-col :span="12">
|
||||
<el-form :model="form" :rules="rules" ref="form">
|
||||
<div class="logo">
|
||||
<img :src="'/display/file/loginLogo'" style="width: 224px;height: 45px;" alt="">
|
||||
|
||||
<div class="title">
|
||||
<div class="title-img">
|
||||
<img :src="'/display/file/loginLogo'" alt="">
|
||||
</div>
|
||||
<div class="title">
|
||||
<span id="s1">{{ loginTitle }}</span>
|
||||
</div>
|
||||
<div class="border"></div>
|
||||
<div class="welcome">
|
||||
{{ $t('commons.welcome') }}
|
||||
<span>Metersphere</span>
|
||||
<span>{{ $t('commons.welcome') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="form">
|
||||
<el-form-item v-slot:default>
|
||||
<el-radio-group v-model="form.authenticate" @change="redirectAuth(form.authenticate)">
|
||||
<el-radio label="LDAP" size="mini" v-if="openLdap">LDAP</el-radio>
|
||||
<el-radio label="LOCAL" size="mini" v-if="openLdap">普通登录</el-radio>
|
||||
<el-radio :label="auth.id" size="mini" v-for="auth in authSources" :key="auth.id">{{ auth.type }} {{ auth.name }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="form.username" :placeholder="$t('commons.login_username')" autofocus
|
||||
autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="form.password" :placeholder="$t('commons.password')" show-password autocomplete="off"
|
||||
maxlength="30" show-word-limit/>
|
||||
</el-form-item>
|
||||
<el-form :model="form" :rules="rules" ref="form">
|
||||
<el-form-item v-slot:default>
|
||||
<el-radio-group v-model="form.authenticate" @change="redirectAuth(form.authenticate)">
|
||||
<el-radio label="LDAP" size="mini" v-if="openLdap">LDAP</el-radio>
|
||||
<el-radio label="LOCAL" size="mini" v-if="openLdap">普通登录</el-radio>
|
||||
<el-radio :label="auth.id" size="mini" v-for="auth in authSources" :key="auth.id">{{ auth.type }} {{ auth.name }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="username">
|
||||
<el-input v-model="form.username" :placeholder="$t('commons.login_username')" autofocus
|
||||
autocomplete="off"/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input v-model="form.password" :placeholder="$t('commons.password')" show-password autocomplete="off"
|
||||
maxlength="30" show-word-limit/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="btn">
|
||||
<el-button type="primary" class="submit" @click="submit('form')">
|
||||
|
@ -38,12 +41,17 @@
|
|||
<div class="msg">
|
||||
{{ msg }}
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<div class="divider"/>
|
||||
|
||||
<el-col :span="12">
|
||||
<img :src="'/display/file/loginImage'" style="height: 560px; width: 100%">
|
||||
<img class="login-image" :src="'/display/file/loginImage'">
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -54,19 +62,11 @@ import {DEFAULT_LANGUAGE} from "@/common/js/constants";
|
|||
const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/);
|
||||
const display = requireComponent.keys().length > 0 ? requireComponent("./display/Display.vue") : {};
|
||||
const auth = requireComponent.keys().length > 0 ? requireComponent("./auth/Auth.vue") : {};
|
||||
const license = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : null;
|
||||
|
||||
export default {
|
||||
name: "Login",
|
||||
data() {
|
||||
/*let validateEmail = (rule, value, callback) => {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
let EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
if (!EMAIL_REGEX.test(value)) {
|
||||
callback(new Error('邮箱格式不正确'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};*/
|
||||
return {
|
||||
result: {},
|
||||
form: {
|
||||
|
@ -118,6 +118,10 @@ export default {
|
|||
created: function () {
|
||||
// 主页添加键盘事件,注意,不能直接在焦点事件上添加回车
|
||||
document.addEventListener("keydown", this.watchEnter);
|
||||
//
|
||||
if (license.default) {
|
||||
license.default.valid(this)
|
||||
}
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
|
@ -184,62 +188,51 @@ export default {
|
|||
|
||||
<style scoped>
|
||||
.container {
|
||||
min-width: 800px;
|
||||
max-width: 1440px;
|
||||
height: 560px;
|
||||
margin: calc((100vh - 560px) / 2) auto 0;
|
||||
width: 1440px;
|
||||
height: 810px;
|
||||
margin: calc((100vh - 810px) / 2) auto 0;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin: 30px 30px 0;
|
||||
.el-col:nth-child(3) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-top: 50px;
|
||||
font-size: 32px;
|
||||
.title img {
|
||||
width: 293px;
|
||||
margin-top: 165px;
|
||||
}
|
||||
|
||||
.title-img {
|
||||
letter-spacing: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.title > #s1 {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.title > #s2 {
|
||||
color: #151515;
|
||||
}
|
||||
|
||||
.border {
|
||||
height: 2px;
|
||||
margin: 20px auto 20px;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
background: #8B479B;
|
||||
.login-image {
|
||||
height: 365px;
|
||||
width: 567px;
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
margin-top: 50px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 75px;
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
letter-spacing: 0;
|
||||
line-height: 18px;
|
||||
color: #843697;
|
||||
line-height: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.form {
|
||||
margin-top: 30px;
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-top: 40px;
|
||||
padding: 0 40px;
|
||||
.form, .btn {
|
||||
padding: 0;
|
||||
width: 443px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.btn > .submit {
|
||||
width: 100%;
|
||||
border-radius: 0;
|
||||
border-radius: 70px;
|
||||
border-color: #8B479B;
|
||||
background-color: #8B479B;
|
||||
}
|
||||
|
@ -254,32 +247,54 @@ export default {
|
|||
background-color: rgba(139, 71, 155, 0.8);
|
||||
}
|
||||
|
||||
.msg {
|
||||
margin-top: 10px;
|
||||
padding: 0 40px;
|
||||
color: red;
|
||||
text-align: center;
|
||||
.el-form-item:first-child {
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.image {
|
||||
background: url(../assets/info.png);
|
||||
height: 560px;
|
||||
/deep/ .el-radio__input.is-checked .el-radio__inner {
|
||||
background-color: #783887;
|
||||
background: #783887;
|
||||
border-color: #783887;
|
||||
}
|
||||
|
||||
.login-logo {
|
||||
background: url(../assets/logo-dark-MeterSphere.svg);
|
||||
/deep/ .el-radio__input.is-checked + .el-radio__label {
|
||||
color: #783887;
|
||||
}
|
||||
|
||||
.logo-header {
|
||||
background: url(../assets/logo-light-MeterSphere.svg);
|
||||
/deep/ .el-input__inner {
|
||||
border-radius: 70px !important;
|
||||
background: #f6f3f8 !important;
|
||||
border-color: #f6f3f8 !important;
|
||||
/*谷歌浏览器默认填充的颜色无法替换,使用下列样式填充*/
|
||||
box-shadow: inset 0 0 0 1000px #f6f3f8 !important;
|
||||
}
|
||||
|
||||
.el-input, .el-button {
|
||||
width: 443px;
|
||||
}
|
||||
|
||||
/deep/ .el-input__inner:focus {
|
||||
border: 1px solid #783887 !important;
|
||||
}
|
||||
|
||||
.divider {
|
||||
border: 1px solid #f6f3f8;
|
||||
height: 480px;
|
||||
margin: 165px 0px;
|
||||
}
|
||||
|
||||
.welcome span:first-child {
|
||||
font-weight: bold;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Neue Haas Grotesk Text Pro", "Arial Nova", "Segoe UI", "Helvetica Neue", ".PingFang SC", "PingFang SC", "Source Han Sans SC", "Noto Sans CJK SC", "Source Han Sans CN", "Noto Sans SC", "Source Han Sans TC", "Noto Sans CJK TC", "Hiragino Sans GB", sans-serif;
|
||||
font-size: 14px;
|
||||
background-color: #F5F5F5;
|
||||
/*background-color: #F5F5F5;*/
|
||||
line-height: 26px;
|
||||
color: #2B415C;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
|
|
@ -4,6 +4,10 @@ import 'element-ui/lib/theme-chalk/index.css';
|
|||
import Login from "./Login.vue";
|
||||
import Ajax from "../common/js/ajax";
|
||||
import i18n from "../i18n/i18n";
|
||||
// 引用静态资源,去掉打包将缺失图片
|
||||
import infoImg from "../assets/info.png";
|
||||
import loginLogo from "../assets/logo-dark-MeterSphere.svg";
|
||||
import logoHeader from "../assets/logo-light-MeterSphere.svg";
|
||||
|
||||
Vue.config.productionTip = false;
|
||||
|
||||
|
|
Loading…
Reference in New Issue