feat(接口自动化): 完成场景批量执行,基础报告生成

This commit is contained in:
fit2-zhao 2020-12-07 19:03:51 +08:00
parent 1aab595ab0
commit b848830976
35 changed files with 1171 additions and 371 deletions

View File

@ -1,16 +1,22 @@
package io.metersphere.api.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.DeleteAPIReportRequest;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.dto.automation.APIScenarioReportResult;
import io.metersphere.api.service.ApiScenarioReportService;
import io.metersphere.commons.constants.RoleConstants;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.commons.utils.SessionUtils;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
@RestController
@RequestMapping(value = "/api/scenario/report")
@ -20,8 +26,41 @@ public class APIScenarioReportController {
@Resource
private ApiScenarioReportService apiReportService;
@GetMapping("/get/{reportId}")
public APIReportResult get(@PathVariable String reportId) {
@GetMapping("/get/{reportId}/{infoDb}")
public APIReportResult get(@PathVariable String reportId,@PathVariable Boolean infoDb) {
if(infoDb){
return apiReportService.get(reportId);
}
return apiReportService.getCacheResult(reportId);
}
@PostMapping("/list/{goPage}/{pageSize}")
public Pager<List<APIScenarioReportResult>> list(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody QueryAPIReportRequest request) {
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
request.setWorkspaceId(SessionUtils.getCurrentWorkspaceId());
return PageUtils.setPageInfo(page, apiReportService.list(request));
}
@PostMapping("/add")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public String add(@RequestBody APIScenarioReportResult node) {
return apiReportService.add(node);
}
@PostMapping("/update")
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
public String update(@RequestBody APIScenarioReportResult node) {
return apiReportService.update(node);
}
@PostMapping("/delete")
public void delete(@RequestBody DeleteAPIReportRequest request) {
apiReportService.delete(request);
}
@PostMapping("/batch/delete")
public void deleteAPIReportBatch(@RequestBody DeleteAPIReportRequest reportRequest) {
apiReportService.deleteAPIReportBatch(reportRequest);
}
}

View File

@ -4,6 +4,7 @@ import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.dto.automation.SaveApiScenarioRequest;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.service.ApiAutomationService;
@ -36,13 +37,13 @@ public class ApiAutomationController {
}
@PostMapping(value = "/create")
public void create(@RequestBody SaveApiScenarioRequest request) {
apiAutomationService.create(request);
public void create(@RequestPart("request") SaveApiScenarioRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
apiAutomationService.create(request, bodyFiles);
}
@PostMapping(value = "/update")
public void update(@RequestBody SaveApiScenarioRequest request) {
apiAutomationService.update(request);
public void update(@RequestPart("request") SaveApiScenarioRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
apiAutomationService.update(request, bodyFiles);
}
@GetMapping("/delete/{id}")
@ -70,9 +71,15 @@ public class ApiAutomationController {
return apiAutomationService.getApiScenarios(ids);
}
@PostMapping(value = "/run")
@PostMapping(value = "/run/debug")
public void runDebug(@RequestPart("request") RunDefinitionRequest request, @RequestPart(value = "files") List<MultipartFile> bodyFiles) {
apiAutomationService.run(request, bodyFiles);
}
@PostMapping(value = "/run")
public void run(@RequestBody RunScenarioRequest request) {
apiAutomationService.run(request);
}
}

View File

@ -0,0 +1,18 @@
package io.metersphere.api.dto.automation;
import io.metersphere.base.domain.ApiScenarioReport;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class APIScenarioReportResult extends ApiScenarioReport {
private String testName;
private String projectName;
private String userName;
private String content;
}

View File

@ -0,0 +1,19 @@
package io.metersphere.api.dto.automation;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
public class RunScenarioRequest {
private String id;
private String reportId;
private String environmentId;
private List<String> scenarioIds;
}

View File

@ -3,6 +3,8 @@ package io.metersphere.api.dto.automation;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Setter
@Getter
public class SaveApiScenarioRequest {
@ -35,4 +37,6 @@ public class SaveApiScenarioRequest {
private String description;
private String scenarioDefinition;
List<String> bodyUploadIds;
}

View File

@ -4,6 +4,9 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.alibaba.fastjson.annotation.JSONType;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.service.ApiAutomationService;
import io.metersphere.api.service.ApiTestEnvironmentService;
@ -15,6 +18,7 @@ import lombok.EqualsAndHashCode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.jorphan.collections.HashTree;
import java.util.LinkedList;
import java.util.List;
@Data
@ -41,14 +45,21 @@ public class MsScenario extends MsTestElement {
if (this.getReferenced() != null && this.getReferenced().equals("Deleted")) {
return;
} else if (this.getReferenced() != null && this.getReferenced().equals("REF")) {
ApiAutomationService apiAutomationService = CommonBeanFactory.getBean(ApiAutomationService.class);
ApiScenario scenario = apiAutomationService.getApiScenario(this.getId());
JSONObject element = JSON.parseObject(scenario.getScenarioDefinition());
List<MsTestElement> dataArr = JSON.parseArray(element.getString("hashTree"), MsTestElement.class);
if (hashTree == null) {
hashTree = dataArr;
} else {
hashTree.addAll(dataArr);
try {
ApiAutomationService apiAutomationService = CommonBeanFactory.getBean(ApiAutomationService.class);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
ApiScenario scenario = apiAutomationService.getApiScenario(this.getId());
JSONObject element = JSON.parseObject(scenario.getScenarioDefinition());
LinkedList<MsTestElement> elements = mapper.readValue(element.getString("hashTree"), new TypeReference<LinkedList<MsTestElement>>() {
});
if (hashTree == null) {
hashTree = elements;
} else {
hashTree.addAll(elements);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
if (CollectionUtils.isNotEmpty(hashTree)) {

View File

@ -51,7 +51,7 @@ import java.util.List;
MsJSR223PreProcessor.class, MsTestPlan.class, MsThreadGroup.class, AuthManager.class, MsAssertions.class,
MsExtract.class, MsTCPSampler.class, MsDubboSampler.class, MsJDBCSampler.class, MsConstantTimer.class, MsIfController.class, MsScenario.class}, typeKey = "type")
@Data
public abstract class MsTestElement {
public class MsTestElement {
private String type;
@JSONField(ordinal = 1)
private String id;

View File

@ -50,4 +50,5 @@ public class MsThreadGroup extends MsTestElement {
threadGroup.setSamplerController(loopController);
return threadGroup;
}
}

View File

@ -169,7 +169,7 @@ public class APIBackendListenerClient extends AbstractBackendListenerClient impl
}
} else if (StringUtils.equals(this.runMode, ApiRunMode.SCENARIO.name())) {
// 执行报告不需要存储由用户确认后在存储
testResult.setTestId(debugReportId);
testResult.setTestId(testId);
apiScenarioReportService.complete(testResult);
} else {
apiTestService.changeStatus(testId, APITestStatus.Completed);

View File

@ -2,13 +2,16 @@ package io.metersphere.api.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.automation.ApiScenarioDTO;
import io.metersphere.api.dto.automation.ApiScenarioRequest;
import io.metersphere.api.dto.automation.SaveApiScenarioRequest;
import io.metersphere.api.dto.automation.ScenarioStatus;
import io.metersphere.api.dto.automation.*;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.dto.definition.request.MsThreadGroup;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.*;
@ -24,6 +27,7 @@ import io.metersphere.i18n.Translator;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;
import org.aspectj.util.FileUtil;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -32,9 +36,9 @@ import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
@Service
@ -83,8 +87,9 @@ public class ApiAutomationService {
apiScenarioMapper.deleteByExample(example);
}
public void create(SaveApiScenarioRequest request) {
public void create(SaveApiScenarioRequest request, List<MultipartFile> bodyFiles) {
checkNameExist(request);
final ApiScenario scenario = new ApiScenario();
scenario.setId(request.getId());
scenario.setName(request.getName());
@ -111,10 +116,16 @@ public class ApiAutomationService {
}
scenario.setDescription(request.getDescription());
apiScenarioMapper.insert(scenario);
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
createBodyFiles(bodyUploadIds, bodyFiles);
}
public void update(SaveApiScenarioRequest request) {
public void update(SaveApiScenarioRequest request, List<MultipartFile> bodyFiles) {
checkNameExist(request);
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
createBodyFiles(bodyUploadIds, bodyFiles);
final ApiScenario scenario = new ApiScenario();
scenario.setId(request.getId());
scenario.setName(request.getName());
@ -176,7 +187,7 @@ public class ApiAutomationService {
}
private void createBodyFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (!bodyUploadIds.isEmpty()) {
if (!bodyUploadIds.isEmpty() && !bodyFiles.isEmpty()) {
File testDir = new File(BODY_FILE_DIR);
if (!testDir.exists()) {
testDir.mkdirs();
@ -208,6 +219,63 @@ public class ApiAutomationService {
}
}
private void createAPIReportResult(String id) {
APIReportResult report = new APIReportResult();
report.setId(id);
report.setTestId(id);
report.setName("");
report.setTriggerMode(null);
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(APITestStatus.Running.name());
report.setUserId(SessionUtils.getUserId());
apiReportService.addResult(report);
}
/**
* 场景测试执行
*
* @param request
* @return
*/
public String run(RunScenarioRequest request) {
List<ApiScenario> apiScenarios = extApiScenarioMapper.selectIds(request.getScenarioIds());
MsTestPlan testPlan = new MsTestPlan();
testPlan.setHashTree(new LinkedList<>());
HashTree jmeterTestPlanHashTree = new ListedHashTree();
EnvironmentConfig config = null;
for (ApiScenario item : apiScenarios) {
MsThreadGroup group = new MsThreadGroup();
group.setLabel(item.getName());
group.setName(item.getName());
try {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
JSONObject element = JSON.parseObject(item.getScenarioDefinition());
String environmentId = element.getString("environmentId");
if (environmentId != null) {
ApiTestEnvironmentWithBLOBs environment = environmentService.get(environmentId);
config = JSONObject.parseObject(environment.getConfig(), EnvironmentConfig.class);
}
LinkedList<MsTestElement> elements = mapper.readValue(element.getString("hashTree"), new TypeReference<LinkedList<MsTestElement>>() {
});
group.setHashTree(elements);
testPlan.getHashTree().add(group);
} catch (Exception ex) {
ex.printStackTrace();
}
}
testPlan.toHashTree(jmeterTestPlanHashTree, testPlan.getHashTree(), config);
// 调用执行方法
jMeterService.runDefinition(request.getId(), jmeterTestPlanHashTree, request.getReportId(), ApiRunMode.SCENARIO.name());
createAPIReportResult(request.getId());
return request.getId();
}
/**
* 场景测试执行
*
@ -228,17 +296,7 @@ public class ApiAutomationService {
// 调用执行方法
jMeterService.runDefinition(request.getId(), hashTree, request.getReportId(), ApiRunMode.SCENARIO.name());
APIReportResult report = new APIReportResult();
report.setId(UUID.randomUUID().toString());
report.setTestId(request.getReportId());
report.setName("RUN");
report.setTriggerMode(null);
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(APITestStatus.Running.name());
report.setUserId(SessionUtils.getUserId());
apiReportService.addResult(report);
createAPIReportResult(request.getId());
return request.getId();
}
}

View File

@ -2,23 +2,39 @@ package io.metersphere.api.service;
import com.alibaba.fastjson.JSONObject;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.DeleteAPIReportRequest;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.dto.automation.APIScenarioReportResult;
import io.metersphere.api.jmeter.TestResult;
import io.metersphere.base.domain.ApiTestReportDetail;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiScenarioReportDetailMapper;
import io.metersphere.base.mapper.ApiScenarioReportMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.i18n.Translator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.security.util.Cache;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiScenarioReportService {
private static Cache cache = Cache.newHardMemoryCache(0, 3600 * 24);
@Resource
private ExtApiScenarioReportMapper extApiScenarioReportMapper;
@Resource
private ApiScenarioReportMapper apiScenarioReportMapper;
@Resource
private ApiScenarioReportDetailMapper apiScenarioReportDetailMapper;
public void complete(TestResult result) {
Object obj = cache.get(result.getTestId());
@ -64,4 +80,103 @@ public class ApiScenarioReportService {
}
return null;
}
public APIReportResult get(String reportId) {
APIReportResult reportResult = extApiScenarioReportMapper.get(reportId);
ApiScenarioReportDetail detail = apiScenarioReportDetailMapper.selectByPrimaryKey(reportId);
if (detail != null) {
reportResult.setContent(new String(detail.getContent(), StandardCharsets.UTF_8));
}
return reportResult;
}
public List<APIScenarioReportResult> list(QueryAPIReportRequest request) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
return extApiScenarioReportMapper.list(request);
}
private void checkNameExist(APIScenarioReportResult request) {
ApiScenarioReportExample example = new ApiScenarioReportExample();
example.createCriteria().andNameEqualTo(request.getName()).andProjectIdEqualTo(request.getProjectId()).andIdNotEqualTo(request.getId());
if (apiScenarioReportMapper.countByExample(example) > 0) {
MSException.throwException(Translator.get("load_test_already_exists"));
}
}
public ApiScenarioReport createReport(APIScenarioReportResult test) {
checkNameExist(test);
ApiScenarioReport report = new ApiScenarioReport();
report.setId(UUID.randomUUID().toString());
report.setProjectId(test.getProjectId());
report.setName(test.getName());
report.setTriggerMode(test.getTriggerMode());
report.setDescription(test.getDescription());
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(test.getStatus());
report.setUserId(test.getUserId());
apiScenarioReportMapper.insert(report);
return report;
}
public ApiScenarioReport updateReport(APIScenarioReportResult test) {
checkNameExist(test);
ApiScenarioReport report = new ApiScenarioReport();
report.setId(test.getId());
report.setProjectId(test.getProjectId());
report.setName(test.getName());
report.setTriggerMode(test.getTriggerMode());
report.setDescription(test.getDescription());
report.setCreateTime(System.currentTimeMillis());
report.setUpdateTime(System.currentTimeMillis());
report.setStatus(test.getStatus());
report.setUserId(test.getUserId());
apiScenarioReportMapper.updateByPrimaryKey(report);
return report;
}
public String add(APIScenarioReportResult test) {
ApiScenarioReport report = createReport(test);
ApiScenarioReportDetail detail = new ApiScenarioReportDetail();
detail.setContent(test.getContent().getBytes(StandardCharsets.UTF_8));
detail.setReportId(report.getId());
detail.setProjectId(test.getProjectId());
apiScenarioReportDetailMapper.insert(detail);
return report.getId();
}
public String update(APIScenarioReportResult test) {
ApiScenarioReport report = updateReport(test);
ApiScenarioReportDetail detail = apiScenarioReportDetailMapper.selectByPrimaryKey(test.getId());
if (detail == null) {
detail = new ApiScenarioReportDetail();
detail.setContent(test.getContent().getBytes(StandardCharsets.UTF_8));
detail.setReportId(report.getId());
detail.setProjectId(test.getProjectId());
apiScenarioReportDetailMapper.insert(detail);
} else {
detail.setContent(test.getContent().getBytes(StandardCharsets.UTF_8));
detail.setReportId(report.getId());
detail.setProjectId(test.getProjectId());
apiScenarioReportDetailMapper.updateByPrimaryKey(detail);
}
return report.getId();
}
public void delete(DeleteAPIReportRequest request) {
apiScenarioReportDetailMapper.deleteByPrimaryKey(request.getId());
apiScenarioReportMapper.deleteByPrimaryKey(request.getId());
}
public void deleteAPIReportBatch(DeleteAPIReportRequest reportRequest) {
ApiScenarioReportDetailExample detailExample = new ApiScenarioReportDetailExample();
detailExample.createCriteria().andReportIdIn(reportRequest.getIds());
apiScenarioReportDetailMapper.deleteByExample(detailExample);
ApiScenarioReportExample apiTestReportExample = new ApiScenarioReportExample();
apiTestReportExample.createCriteria().andIdIn(reportRequest.getIds());
apiScenarioReportMapper.deleteByExample(apiTestReportExample);
}
}

View File

@ -1,13 +1,14 @@
package io.metersphere.base.domain;
import java.io.Serializable;
import lombok.Data;
import java.io.Serializable;
@Data
public class ApiScenarioReport implements Serializable {
private String id;
private String scenarioId;
private String projectId;
private String name;

View File

@ -7,7 +7,7 @@ import lombok.Data;
public class ApiScenarioReportDetail implements Serializable {
private String reportId;
private String scenarioId;
private String projectId;
private byte[] content;

View File

@ -174,73 +174,73 @@ public class ApiScenarioReportDetailExample {
return (Criteria) this;
}
public Criteria andScenarioIdIsNull() {
addCriterion("scenario_id is null");
public Criteria andProjectIdIsNull() {
addCriterion("project_id is null");
return (Criteria) this;
}
public Criteria andScenarioIdIsNotNull() {
addCriterion("scenario_id is not null");
public Criteria andProjectIdIsNotNull() {
addCriterion("project_id is not null");
return (Criteria) this;
}
public Criteria andScenarioIdEqualTo(String value) {
addCriterion("scenario_id =", value, "scenarioId");
public Criteria andProjectIdEqualTo(String value) {
addCriterion("project_id =", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotEqualTo(String value) {
addCriterion("scenario_id <>", value, "scenarioId");
public Criteria andProjectIdNotEqualTo(String value) {
addCriterion("project_id <>", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdGreaterThan(String value) {
addCriterion("scenario_id >", value, "scenarioId");
public Criteria andProjectIdGreaterThan(String value) {
addCriterion("project_id >", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdGreaterThanOrEqualTo(String value) {
addCriterion("scenario_id >=", value, "scenarioId");
public Criteria andProjectIdGreaterThanOrEqualTo(String value) {
addCriterion("project_id >=", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdLessThan(String value) {
addCriterion("scenario_id <", value, "scenarioId");
public Criteria andProjectIdLessThan(String value) {
addCriterion("project_id <", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdLessThanOrEqualTo(String value) {
addCriterion("scenario_id <=", value, "scenarioId");
public Criteria andProjectIdLessThanOrEqualTo(String value) {
addCriterion("project_id <=", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdLike(String value) {
addCriterion("scenario_id like", value, "scenarioId");
public Criteria andProjectIdLike(String value) {
addCriterion("project_id like", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotLike(String value) {
addCriterion("scenario_id not like", value, "scenarioId");
public Criteria andProjectIdNotLike(String value) {
addCriterion("project_id not like", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdIn(List<String> values) {
addCriterion("scenario_id in", values, "scenarioId");
public Criteria andProjectIdIn(List<String> values) {
addCriterion("project_id in", values, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotIn(List<String> values) {
addCriterion("scenario_id not in", values, "scenarioId");
public Criteria andProjectIdNotIn(List<String> values) {
addCriterion("project_id not in", values, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdBetween(String value1, String value2) {
addCriterion("scenario_id between", value1, value2, "scenarioId");
public Criteria andProjectIdBetween(String value1, String value2) {
addCriterion("project_id between", value1, value2, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotBetween(String value1, String value2) {
addCriterion("scenario_id not between", value1, value2, "scenarioId");
public Criteria andProjectIdNotBetween(String value1, String value2) {
addCriterion("project_id not between", value1, value2, "projectId");
return (Criteria) this;
}
}

View File

@ -174,73 +174,73 @@ public class ApiScenarioReportExample {
return (Criteria) this;
}
public Criteria andScenarioIdIsNull() {
addCriterion("scenario_id is null");
public Criteria andProjectIdIsNull() {
addCriterion("project_id is null");
return (Criteria) this;
}
public Criteria andScenarioIdIsNotNull() {
addCriterion("scenario_id is not null");
public Criteria andProjectIdIsNotNull() {
addCriterion("project_id is not null");
return (Criteria) this;
}
public Criteria andScenarioIdEqualTo(String value) {
addCriterion("scenario_id =", value, "scenarioId");
public Criteria andProjectIdEqualTo(String value) {
addCriterion("project_id =", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotEqualTo(String value) {
addCriterion("scenario_id <>", value, "scenarioId");
public Criteria andProjectIdNotEqualTo(String value) {
addCriterion("project_id <>", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdGreaterThan(String value) {
addCriterion("scenario_id >", value, "scenarioId");
public Criteria andProjectIdGreaterThan(String value) {
addCriterion("project_id >", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdGreaterThanOrEqualTo(String value) {
addCriterion("scenario_id >=", value, "scenarioId");
public Criteria andProjectIdGreaterThanOrEqualTo(String value) {
addCriterion("project_id >=", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdLessThan(String value) {
addCriterion("scenario_id <", value, "scenarioId");
public Criteria andProjectIdLessThan(String value) {
addCriterion("project_id <", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdLessThanOrEqualTo(String value) {
addCriterion("scenario_id <=", value, "scenarioId");
public Criteria andProjectIdLessThanOrEqualTo(String value) {
addCriterion("project_id <=", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdLike(String value) {
addCriterion("scenario_id like", value, "scenarioId");
public Criteria andProjectIdLike(String value) {
addCriterion("project_id like", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotLike(String value) {
addCriterion("scenario_id not like", value, "scenarioId");
public Criteria andProjectIdNotLike(String value) {
addCriterion("project_id not like", value, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdIn(List<String> values) {
addCriterion("scenario_id in", values, "scenarioId");
public Criteria andProjectIdIn(List<String> values) {
addCriterion("project_id in", values, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotIn(List<String> values) {
addCriterion("scenario_id not in", values, "scenarioId");
public Criteria andProjectIdNotIn(List<String> values) {
addCriterion("project_id not in", values, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdBetween(String value1, String value2) {
addCriterion("scenario_id between", value1, value2, "scenarioId");
public Criteria andProjectIdBetween(String value1, String value2) {
addCriterion("project_id between", value1, value2, "projectId");
return (Criteria) this;
}
public Criteria andScenarioIdNotBetween(String value1, String value2) {
addCriterion("scenario_id not between", value1, value2, "scenarioId");
public Criteria andProjectIdNotBetween(String value1, String value2) {
addCriterion("project_id not between", value1, value2, "projectId");
return (Criteria) this;
}

View File

@ -3,7 +3,7 @@
<mapper namespace="io.metersphere.base.mapper.ApiScenarioReportDetailMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ApiScenarioReportDetail">
<id column="report_id" jdbcType="VARCHAR" property="reportId" />
<result column="scenario_id" jdbcType="VARCHAR" property="scenarioId" />
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.ApiScenarioReportDetail">
<result column="content" jdbcType="LONGVARBINARY" property="content" />
@ -67,7 +67,7 @@
</where>
</sql>
<sql id="Base_Column_List">
report_id, scenario_id
report_id, project_id
</sql>
<sql id="Blob_Column_List">
content
@ -121,9 +121,9 @@
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.ApiScenarioReportDetail">
insert into api_scenario_report_detail (report_id, scenario_id, content
insert into api_scenario_report_detail (report_id, project_id, content
)
values (#{reportId,jdbcType=VARCHAR}, #{scenarioId,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARBINARY}
values (#{reportId,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARBINARY}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.base.domain.ApiScenarioReportDetail">
@ -132,8 +132,8 @@
<if test="reportId != null">
report_id,
</if>
<if test="scenarioId != null">
scenario_id,
<if test="projectId != null">
project_id,
</if>
<if test="content != null">
content,
@ -143,8 +143,8 @@
<if test="reportId != null">
#{reportId,jdbcType=VARCHAR},
</if>
<if test="scenarioId != null">
#{scenarioId,jdbcType=VARCHAR},
<if test="projectId != null">
#{projectId,jdbcType=VARCHAR},
</if>
<if test="content != null">
#{content,jdbcType=LONGVARBINARY},
@ -163,8 +163,8 @@
<if test="record.reportId != null">
report_id = #{record.reportId,jdbcType=VARCHAR},
</if>
<if test="record.scenarioId != null">
scenario_id = #{record.scenarioId,jdbcType=VARCHAR},
<if test="record.projectId != null">
project_id = #{record.projectId,jdbcType=VARCHAR},
</if>
<if test="record.content != null">
content = #{record.content,jdbcType=LONGVARBINARY},
@ -177,7 +177,7 @@
<update id="updateByExampleWithBLOBs" parameterType="map">
update api_scenario_report_detail
set report_id = #{record.reportId,jdbcType=VARCHAR},
scenario_id = #{record.scenarioId,jdbcType=VARCHAR},
project_id = #{record.projectId,jdbcType=VARCHAR},
content = #{record.content,jdbcType=LONGVARBINARY}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
@ -186,7 +186,7 @@
<update id="updateByExample" parameterType="map">
update api_scenario_report_detail
set report_id = #{record.reportId,jdbcType=VARCHAR},
scenario_id = #{record.scenarioId,jdbcType=VARCHAR}
project_id = #{record.projectId,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -194,8 +194,8 @@
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.ApiScenarioReportDetail">
update api_scenario_report_detail
<set>
<if test="scenarioId != null">
scenario_id = #{scenarioId,jdbcType=VARCHAR},
<if test="projectId != null">
project_id = #{projectId,jdbcType=VARCHAR},
</if>
<if test="content != null">
content = #{content,jdbcType=LONGVARBINARY},
@ -205,13 +205,13 @@
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.ApiScenarioReportDetail">
update api_scenario_report_detail
set scenario_id = #{scenarioId,jdbcType=VARCHAR},
set project_id = #{projectId,jdbcType=VARCHAR},
content = #{content,jdbcType=LONGVARBINARY}
where report_id = #{reportId,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.ApiScenarioReportDetail">
update api_scenario_report_detail
set scenario_id = #{scenarioId,jdbcType=VARCHAR}
set project_id = #{projectId,jdbcType=VARCHAR}
where report_id = #{reportId,jdbcType=VARCHAR}
</update>
</mapper>

View File

@ -3,7 +3,7 @@
<mapper namespace="io.metersphere.base.mapper.ApiScenarioReportMapper">
<resultMap id="BaseResultMap" type="io.metersphere.base.domain.ApiScenarioReport">
<id column="id" jdbcType="VARCHAR" property="id" />
<result column="scenario_id" jdbcType="VARCHAR" property="scenarioId" />
<result column="project_id" jdbcType="VARCHAR" property="projectId" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="description" jdbcType="VARCHAR" property="description" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
@ -71,7 +71,7 @@
</where>
</sql>
<sql id="Base_Column_List">
id, scenario_id, `name`, description, create_time, update_time, `status`, user_id,
id, project_id, `name`, description, create_time, update_time, `status`, user_id,
trigger_mode
</sql>
<select id="selectByExample" parameterType="io.metersphere.base.domain.ApiScenarioReportExample" resultMap="BaseResultMap">
@ -105,11 +105,11 @@
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.base.domain.ApiScenarioReport">
insert into api_scenario_report (id, scenario_id, `name`,
insert into api_scenario_report (id, project_id, `name`,
description, create_time, update_time,
`status`, user_id, trigger_mode
)
values (#{id,jdbcType=VARCHAR}, #{scenarioId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{description,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, #{updateTime,jdbcType=BIGINT},
#{status,jdbcType=VARCHAR}, #{userId,jdbcType=VARCHAR}, #{triggerMode,jdbcType=VARCHAR}
)
@ -120,8 +120,8 @@
<if test="id != null">
id,
</if>
<if test="scenarioId != null">
scenario_id,
<if test="projectId != null">
project_id,
</if>
<if test="name != null">
`name`,
@ -149,8 +149,8 @@
<if test="id != null">
#{id,jdbcType=VARCHAR},
</if>
<if test="scenarioId != null">
#{scenarioId,jdbcType=VARCHAR},
<if test="projectId != null">
#{projectId,jdbcType=VARCHAR},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR},
@ -187,8 +187,8 @@
<if test="record.id != null">
id = #{record.id,jdbcType=VARCHAR},
</if>
<if test="record.scenarioId != null">
scenario_id = #{record.scenarioId,jdbcType=VARCHAR},
<if test="record.projectId != null">
project_id = #{record.projectId,jdbcType=VARCHAR},
</if>
<if test="record.name != null">
`name` = #{record.name,jdbcType=VARCHAR},
@ -219,7 +219,7 @@
<update id="updateByExample" parameterType="map">
update api_scenario_report
set id = #{record.id,jdbcType=VARCHAR},
scenario_id = #{record.scenarioId,jdbcType=VARCHAR},
project_id = #{record.projectId,jdbcType=VARCHAR},
`name` = #{record.name,jdbcType=VARCHAR},
description = #{record.description,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
@ -234,8 +234,8 @@
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.ApiScenarioReport">
update api_scenario_report
<set>
<if test="scenarioId != null">
scenario_id = #{scenarioId,jdbcType=VARCHAR},
<if test="projectId != null">
project_id = #{projectId,jdbcType=VARCHAR},
</if>
<if test="name != null">
`name` = #{name,jdbcType=VARCHAR},
@ -263,7 +263,7 @@
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.ApiScenarioReport">
update api_scenario_report
set scenario_id = #{scenarioId,jdbcType=VARCHAR},
set project_id = #{projectId,jdbcType=VARCHAR},
`name` = #{name,jdbcType=VARCHAR},
description = #{description,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},

View File

@ -0,0 +1,15 @@
package io.metersphere.base.mapper.ext;
import io.metersphere.api.dto.APIReportResult;
import io.metersphere.api.dto.QueryAPIReportRequest;
import io.metersphere.api.dto.automation.APIScenarioReportResult;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ExtApiScenarioReportMapper {
List<APIScenarioReportResult> list(@Param("request") QueryAPIReportRequest request);
APIReportResult get(@Param("reportId") String reportId);
}

View File

@ -0,0 +1,169 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.metersphere.base.mapper.ext.ExtApiScenarioReportMapper">
<resultMap id="BaseResultMap" type="io.metersphere.api.dto.automation.APIScenarioReportResult"
extends="io.metersphere.base.mapper.ApiScenarioReportMapper.BaseResultMap">
<result column="test_name" property="testName"/>
<result column="project_name" property="projectName"/>
<result column="user_name" property="userName"/>
</resultMap>
<sql id="condition">
<choose>
<when test='${object}.operator == "like"'>
like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "not like"'>
not like CONCAT('%', #{${object}.value},'%')
</when>
<when test='${object}.operator == "in"'>
in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "not in"'>
not in
<foreach collection="${object}.value" item="v" separator="," open="(" close=")">
#{v}
</foreach>
</when>
<when test='${object}.operator == "between"'>
between #{${object}.value[0]} and #{${object}.value[1]}
</when>
<when test='${object}.operator == "gt"'>
&gt; #{${object}.value}
</when>
<when test='${object}.operator == "lt"'>
&lt; #{${object}.value}
</when>
<when test='${object}.operator == "ge"'>
&gt;= #{${object}.value}
</when>
<when test='${object}.operator == "le"'>
&lt;= #{${object}.value}
</when>
<when test='${object}.operator == "current user"'>
= '${@io.metersphere.commons.utils.SessionUtils@getUserId()}'
</when>
<otherwise>
= #{${object}.value}
</otherwise>
</choose>
</sql>
<sql id="combine">
<if test='${condition}.name != null and (${name} == null or ${name} == "")'>
and r.name
<include refid="condition">
<property name="object" value="${condition}.name"/>
</include>
</if>
<if test="${condition}.testName != null">
and t.name
<include refid="condition">
<property name="object" value="${condition}.testName"/>
</include>
</if>
<if test="${condition}.projectName != null">
and project.name
<include refid="condition">
<property name="object" value="${condition}.projectName"/>
</include>
</if>
<if test="${condition}.createTime != null">
and r.create_time
<include refid="condition">
<property name="object" value="${condition}.createTime"/>
</include>
</if>
<if test="${condition}.status != null">
and r.status
<include refid="condition">
<property name="object" value="${condition}.status"/>
</include>
</if>
<if test="${condition}.triggerMode != null">
and r.trigger_mode
<include refid="condition">
<property name="object" value="${condition}.triggerMode"/>
</include>
</if>
<if test="${condition}.creator != null">
and r.user_id
<include refid="condition">
<property name="object" value="${condition}.creator"/>
</include>
</if>
</sql>
<select id="list" resultMap="BaseResultMap">
SELECT r.name AS test_name,
r.name, r.description, r.id, r.project_id, r.create_time, r.update_time, r.status, r.trigger_mode,
project.name AS project_name, user.name AS user_name
FROM api_scenario_report r
LEFT JOIN project ON project.id = r.project_id
LEFT JOIN user ON user.id = r.user_id
<where>
<if test="request.combine != null">
<include refid="combine">
<property name="condition" value="request.combine"/>
<property name="name" value="request.name"/>
</include>
</if>
<if test="request.name != null">
and r.name like CONCAT('%', #{request.name},'%')
</if>
<if test="request.userId != null">
AND r.user_id = #{request.userId,jdbcType=VARCHAR}
</if>
<if test="request.projectId != null">
AND project.id = #{request.projectId}
</if>
<if test="request.workspaceId != null">
AND project.workspace_id = #{request.workspaceId,jdbcType=VARCHAR}
</if>
<if test="request.filters != null and request.filters.size() > 0">
<foreach collection="request.filters.entrySet()" index="key" item="values">
<if test="values != null and values.size() > 0">
<choose>
<when test="key=='status'">
and r.status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<otherwise>
and r.trigger_mode in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</otherwise>
</choose>
</if>
</foreach>
</if>
AND r.status != 'Debug'
</where>
<if test="request.orders != null and request.orders.size() > 0">
order by
<foreach collection="request.orders" separator="," item="order">
r.${order.name} ${order.type}
</foreach>
</if>
</select>
<select id="get" resultType="io.metersphere.api.dto.APIReportResult">
SELECT r.*,r.id As testId, r.name AS test_name, project.name AS project_name, user.name AS user_name
FROM api_scenario_report r
LEFT JOIN project ON project.id = r.project_id
LEFT JOIN user ON user.id = r.user_id
<where>
r.id = #{reportId}
</where>
ORDER BY r.update_time DESC
</select>
</mapper>

View File

@ -64,10 +64,8 @@
<!--要生成的数据库表 -->
<table tableName="schedule"/>
<table tableName="notice"/>
<table tableName="message_task"/>
<table tableName="test_plan"/>
<table tableName="api_scenario_report"/>
<table tableName="api_scenario_report_detail"/>
</context>
</generatorConfiguration>

View File

@ -4,12 +4,12 @@
<el-card>
<section class="report-container" v-if="this.report.testId">
<ms-api-report-view-header :report="report" @reportExport="handleExport"/>
<ms-api-report-view-header :report="report" @reportExport="handleExport" @reportSave="handleSave"/>
<main v-if="this.isNotRunning">
<ms-metric-chart :content="content" :totalTime="totalTime"/>
<div @click="active">
<ms-scenario-results :scenarios="content.scenarios" v-on:requestResult="requestResult"/>
<div>
<ms-scenario-results :scenarios="content.scenarios" v-on:requestResult="requestResult"/>
</div>
<el-collapse-transition>
<div v-show="isActive" style="width: 99%">
@ -64,10 +64,14 @@
requestType: undefined,
}
},
props: ['reportId'],
activated() {
this.isRequestResult = false;
},
props: {
reportId: String,
currentProjectId: String,
infoDb: Boolean,
},
watch: {
reportId() {
this.getReport();
@ -91,7 +95,7 @@
getReport() {
this.init();
if (this.reportId) {
let url = "/api/scenario/report/get/" + this.reportId;
let url = "/api/scenario/report/get/" + this.reportId + "/" + this.infoDb;
this.$get(url, response => {
this.report = response.data || {};
if (response.data) {
@ -99,7 +103,6 @@
try {
this.content = JSON.parse(this.report.content);
} catch (e) {
// console.log(this.report.content)
throw e;
}
this.getFails();
@ -136,6 +139,7 @@
}
},
requestResult(requestResult) {
this.active();
this.isRequestResult = false;
this.requestType = undefined;
if (requestResult.request.body.indexOf('[Callable Statement]') > -1) {
@ -155,6 +159,27 @@
reset();
});
},
handleSave() {
if (!this.report.name) {
this.$warning(this.$t('api_test.automation.report_name_info'));
return;
}
if (!this.currentProjectId) {
this.$warning(this.$t('api_test.select_project'));
return;
}
this.loading = true;
this.report.projectId = this.currentProjectId;
let url = "/api/scenario/report/add";
if (this.infoDb === true) {
url = "/api/scenario/report/update";
}
this.result = this.$post(url, this.report, response => {
this.$success(this.$t('commons.save_success'));
this.loading = false;
this.$emit('refresh');
});
},
exportReportReset() {
this.$router.go(0);
}

View File

@ -0,0 +1,228 @@
<template>
<ms-container>
<ms-main-container>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<ms-table-header :is-tester-permission="true" :condition.sync="condition" @search="search"
:title="$t('api_report.title')"
:show-create="false"/>
</template>
<el-table border :data="tableData" class="adjust-table table-content" @sort-change="sort"
@select-all="handleSelectAll"
@select="handleSelect"
@filter-change="filter" @row-click="handleView">
<el-table-column
type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="scope">
<show-more-btn :is-show="scope.row.showMore" :buttons="buttons" :size="selectRows.size"/>
</template>
</el-table-column>
<el-table-column :label="$t('commons.name')" width="200" show-overflow-tooltip prop="name">
</el-table-column>
<!--
<el-table-column prop="testName" :label="$t('api_report.test_name')" width="200" show-overflow-tooltip/>
-->
<el-table-column prop="projectName" :label="$t('load_test.project_name')" width="150" show-overflow-tooltip/>
<el-table-column prop="userName" :label="$t('api_test.creator')" width="150" show-overflow-tooltip/>
<el-table-column prop="createTime" width="250" :label="$t('commons.create_time')" sortable>
<template v-slot:default="scope">
<span>{{ scope.row.createTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="triggerMode" width="150" :label="$t('commons.trigger_mode.name')"
column-key="triggerMode" :filters="triggerFilters">
<template v-slot:default="scope">
<report-trigger-mode-item :trigger-mode="scope.row.triggerMode"/>
</template>
</el-table-column>
<el-table-column prop="status" :label="$t('commons.status')"
column-key="status"
:filters="statusFilters">
<template v-slot:default="{row}">
<ms-api-report-status :row="row"/>
</template>
</el-table-column>
<el-table-column width="150" :label="$t('commons.operating')">
<template v-slot:default="scope">
<ms-table-operator-button :tip="$t('api_report.detail')" icon="el-icon-s-data"
@exec="handleView(scope.row)" type="primary"/>
<ms-table-operator-button :is-tester-permission="true" :tip="$t('api_report.delete')"
icon="el-icon-delete" @exec="handleDelete(scope.row)" type="danger"/>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</ms-main-container>
<el-drawer :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr" :withHeader="false" :title="$t('test_track.plan_view.test_result')" :modal="false" size="90%">
<ms-api-report-detail :report-id="reportId" :currentProjectId="currentProjectId" :info-db="true" @refresh="search"/>
</el-drawer>
</ms-container>
</template>
<script>
import MsTablePagination from "../../../common/pagination/TablePagination";
import MsTableHeader from "../../../common/components/MsTableHeader";
import MsContainer from "../../../common/components/MsContainer";
import MsMainContainer from "../../../common/components/MsMainContainer";
import MsApiReportStatus from "./ApiReportStatus";
import {_filter, _sort} from "@/common/js/utils";
import MsTableOperatorButton from "../../../common/components/MsTableOperatorButton";
import ReportTriggerModeItem from "../../../common/tableItem/ReportTriggerModeItem";
import {REPORT_CONFIGS} from "../../../common/components/search/search-components";
import {ApiEvent, LIST_CHANGE} from "@/business/components/common/head/ListEvent";
import ShowMoreBtn from "../../../track/case/components/ShowMoreBtn";
import MsApiReportDetail from "./ApiReportDetail";
export default {
components: {
ReportTriggerModeItem,
MsTableOperatorButton,
MsApiReportStatus, MsMainContainer, MsContainer, MsTableHeader, MsTablePagination, ShowMoreBtn, MsApiReportDetail
},
data() {
return {
result: {},
reportId: "",
debugVisible: false,
condition: {
components: REPORT_CONFIGS
},
tableData: [],
multipleSelection: [],
currentPage: 1,
pageSize: 5,
total: 0,
loading: false,
currentProjectId: "",
statusFilters: [
{text: 'Saved', value: 'Saved'},
{text: 'Starting', value: 'Starting'},
{text: 'Running', value: 'Running'},
{text: 'Reporting', value: 'Reporting'},
{text: 'Completed', value: 'Completed'},
{text: 'Error', value: 'Error'},
{text: 'Success', value: 'Success'},
],
triggerFilters: [
{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'}
],
buttons: [
{
name: this.$t('api_report.batch_delete'), handleClick: this.handleBatchDelete
}
],
selectRows: new Set(),
}
},
watch: {
'$route': 'init',
},
methods: {
search() {
if (this.testId !== 'all') {
this.condition.testId = this.testId;
}
let url = "/api/scenario/report/list/" + this.currentPage + "/" + this.pageSize;
this.result = this.$post(url, this.condition, response => {
let data = response.data;
this.total = data.itemCount;
this.tableData = data.listObject;
this.selectRows.clear();
});
},
handleSelectionChange(val) {
this.multipleSelection = val;
},
handleView(report) {
this.reportId = report.id;
this.currentProjectId = report.projectId;
this.debugVisible = true;
},
handleDelete(report) {
this.$alert(this.$t('api_report.delete_confirm') + report.name + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
this.result = this.$post("/api/scenario/report/delete", {id: report.id}, () => {
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
}
});
},
init() {
this.testId = this.$route.params.testId;
this.search();
},
sort(column) {
_sort(column, this.condition);
this.init();
},
filter(filters) {
_filter(filters, this.condition);
this.init();
},
handleSelect(selection, row) {
if (this.selectRows.has(row)) {
this.$set(row, "showMore", false);
this.selectRows.delete(row);
} else {
this.$set(row, "showMore", true);
this.selectRows.add(row);
}
},
handleSelectAll(selection) {
if (selection.length > 0) {
this.tableData.forEach(item => {
this.$set(item, "showMore", true);
this.selectRows.add(item);
});
} else {
this.selectRows.clear();
this.tableData.forEach(row => {
this.$set(row, "showMore", false);
})
}
},
handleBatchDelete() {
this.$alert(this.$t('api_report.delete_batch_confirm') + "", '', {
confirmButtonText: this.$t('commons.confirm'),
callback: (action) => {
if (action === 'confirm') {
let ids = Array.from(this.selectRows).map(row => row.id);
this.$post('/api/scenario/report/batch/delete', {ids: ids}, () => {
this.selectRows.clear();
this.$success(this.$t('commons.delete_success'));
this.search();
// 广 head
ApiEvent.$emit(LIST_CHANGE);
});
}
}
});
}
},
created() {
this.init();
}
}
</script>
<style scoped>
.table-content {
width: 100%;
}
</style>

View File

@ -2,13 +2,17 @@
<header class="report-header">
<el-row>
<el-col>
<span>{{ report.projectName === null || report.projectName ==='' ? "场景执行报告": report.projectName}} / </span>
<router-link :to="path">{{ report.testName }}</router-link>
<span><el-input size="mini" style="width: 200px" v-model="report.name"/> </span>
<span class="time"> {{ report.createTime | timestampFormatDate }}</span>
<el-button :disabled="isReadOnly" class="export-button" plain type="primary" size="mini" @click="handleExport(report.name)"
style="margin-left: 1200px">
<el-button :disabled="isReadOnly" class="export-button" plain type="primary" size="mini" @click="handleExport(report.name)" style="margin-right: 10px">
{{$t('test_track.plan_view.export_report')}}
</el-button>
<el-button :disabled="isReadOnly" class="export-button" plain type="primary" size="mini" @click="handleSave(report.name)" style="margin-right: 10px">
{{$t('commons.save')}}
</el-button>
</el-col>
</el-row>
</header>
@ -38,6 +42,9 @@
methods: {
handleExport(name) {
this.$emit('reportExport', name);
},
handleSave(name) {
this.$emit('reportSave', name);
}
}
}

View File

@ -1,10 +1,24 @@
<template>
<div class="scenario-result">
<div v-for="(request, index) in scenario.requestResults" :key="index">
<ms-request-result :key="index" :request="request" :indexNumber="index"
v-on:requestResult="requestResult"
:scenarioName="scenario.name"/>
<div @click="active">
<el-row :gutter="10" type="flex" align="middle" class="info">
<el-col :span="16">
<i class="icon el-icon-arrow-right" :class="{'is-active': isActive}"/>
{{scenario.name}}
</el-col>
</el-row>
</div>
<el-collapse-transition>
<div v-show="isActive">
<div v-for="(request, index) in scenario.requestResults" :key="index">
<ms-request-result :key="index" :request="request" :indexNumber="index"
v-on:requestResult="requestResult"
:scenarioName="scenario.name"/>
</div>
</div>
</el-collapse-transition>
</div>
</template>

View File

@ -1,62 +1,72 @@
<template>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<ms-table-header :condition.sync="condition" @search="search" title=""
:show-create="false"/>
</template>
<div>
<el-card class="table-card" v-loading="result.loading">
<template v-slot:header>
<ms-table-header :condition.sync="condition" @search="search" title=""
:show-create="false"/>
</template>
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table" @select-all="select" @select="select">
<el-table-column type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="{row}">
<show-more-btn :is-show="isSelect(row)" :buttons="buttons" :size="selection.length"/>
</template>
</el-table-column>
<el-table-column prop="name" :label="$t('api_test.automation.scenario_name')"
show-overflow-tooltip/>
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag v-if="scope.row.level == 'P0'" type="info" effect="plain" content="P0"/>
<ms-tag v-if="scope.row.level == 'P1'" type="warning" effect="plain" content="P1"/>
<ms-tag v-if="scope.row.level == 'P2'" type="success" effect="plain" content="P2"/>
<ms-tag v-if="scope.row.level == 'P3'" type="danger" effect="plain" content="P3"/>
</template>
<el-table ref="scenarioTable" border :data="tableData" class="adjust-table" @select-all="select" @select="select">
<el-table-column type="selection"/>
<el-table-column width="40" :resizable="false" align="center">
<template v-slot:default="{row}">
<show-more-btn :is-show="isSelect(row)" :buttons="buttons" :size="selection.length"/>
</template>
</el-table-column>
<el-table-column prop="name" :label="$t('api_test.automation.scenario_name')"
show-overflow-tooltip/>
<el-table-column prop="level" :label="$t('api_test.automation.case_level')"
show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag v-if="scope.row.level == 'P0'" type="info" effect="plain" content="P0"/>
<ms-tag v-if="scope.row.level == 'P1'" type="warning" effect="plain" content="P1"/>
<ms-tag v-if="scope.row.level == 'P2'" type="success" effect="plain" content="P2"/>
<ms-tag v-if="scope.row.level == 'P3'" type="danger" effect="plain" content="P3"/>
</template>
</el-table-column>
<el-table-column prop="tagName" :label="$t('api_test.automation.tag')" show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag type="success" effect="plain" v-if="scope.row.tagName!=undefined" :content="scope.row.tagName"/>
</template>
</el-table-column>
<el-table-column prop="userId" :label="$t('api_test.automation.creator')" show-overflow-tooltip/>
<el-table-column prop="updateTime" :label="$t('api_test.automation.update_time')" width="180">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="stepTotal" :label="$t('api_test.automation.step')" show-overflow-tooltip/>
<el-table-column prop="status" :label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}">
<el-link type="success" v-if="row.status === 'Success'">{{ $t('api_test.automation.success') }}</el-link>
<el-link type="danger" v-if="row.status === 'Fail'">{{ $t('api_test.automation.fail') }}</el-link>
<el-link type="warning" v-if="row.status === 'Trash'">{{ $t('api_test.automation.trash') }}</el-link>
</template>
</el-table-column>
<el-table-column prop="passingRate" :label="$t('api_test.automation.passing_rate')"
show-overflow-tooltip/>
<el-table-column :label="$t('commons.operating')" width="180">
<template v-slot:default="{row}">
<el-button type="text" @click="edit(row)">{{ $t('api_test.automation.edit') }}</el-button>
<el-button type="text" @click="execute(row)">{{ $t('api_test.automation.execute') }}</el-button>
<el-button type="text" @click="copy(row)">{{ $t('api_test.automation.copy') }}</el-button>
<el-button type="text" @click="remove(row)">{{ $t('api_test.automation.remove') }}</el-button>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
</el-card>
</el-table-column>
<el-table-column prop="tagName" :label="$t('api_test.automation.tag')" show-overflow-tooltip>
<template v-slot:default="scope">
<ms-tag type="success" effect="plain" v-if="scope.row.tagName!=undefined" :content="scope.row.tagName"/>
</template>
</el-table-column>
<el-table-column prop="userId" :label="$t('api_test.automation.creator')" show-overflow-tooltip/>
<el-table-column prop="updateTime" :label="$t('api_test.automation.update_time')" width="180">
<template v-slot:default="scope">
<span>{{ scope.row.updateTime | timestampFormatDate }}</span>
</template>
</el-table-column>
<el-table-column prop="stepTotal" :label="$t('api_test.automation.step')" show-overflow-tooltip/>
<el-table-column prop="status" :label="$t('api_test.automation.last_result')">
<template v-slot:default="{row}">
<el-link type="success" v-if="row.status === 'Success'">{{ $t('api_test.automation.success') }}</el-link>
<el-link type="danger" v-if="row.status === 'Fail'">{{ $t('api_test.automation.fail') }}</el-link>
<el-link type="warning" v-if="row.status === 'Trash'">{{ $t('api_test.automation.trash') }}</el-link>
</template>
</el-table-column>
<el-table-column prop="passingRate" :label="$t('api_test.automation.passing_rate')"
show-overflow-tooltip/>
<el-table-column :label="$t('commons.operating')" width="180">
<template v-slot:default="{row}">
<el-button type="text" @click="edit(row)">{{ $t('api_test.automation.edit') }}</el-button>
<el-button type="text" @click="execute(row)">{{ $t('api_test.automation.execute') }}</el-button>
<el-button type="text" @click="copy(row)">{{ $t('api_test.automation.copy') }}</el-button>
<el-button type="text" @click="remove(row)">{{ $t('api_test.automation.remove') }}</el-button>
</template>
</el-table-column>
</el-table>
<ms-table-pagination :change="search" :current-page.sync="currentPage" :page-size.sync="pageSize"
:total="total"/>
<div>
<!-- 调试结果 -->
<el-drawer :visible.sync="runVisible" :destroy-on-close="true" direction="ltr" :withHeader="false" :title="$t('test_track.plan_view.test_result')" :modal="false" size="90%">
<ms-api-report-detail :report-id="reportId" :currentProjectId="currentProject!=undefined ? currentProject.id:''"/>
</el-drawer>
</div>
</el-card>
</div>
</template>
<script>
@ -65,10 +75,11 @@
import ShowMoreBtn from "@/business/components/track/case/components/ShowMoreBtn";
import MsTag from "../../../common/components/MsTag";
import {getUUID} from "@/common/js/utils";
import MsApiReportDetail from "../report/ApiReportDetail";
export default {
name: "MsApiScenarioList",
components: {ShowMoreBtn, MsTablePagination, MsTableHeader, MsTag},
components: {ShowMoreBtn, MsTablePagination, MsTableHeader, MsTag, MsApiReportDetail},
props: {
currentProject: Object,
currentModule: Object,
@ -83,6 +94,9 @@
currentPage: 1,
pageSize: 10,
total: 0,
reportId: "",
runVisible: false,
runData: [],
buttons: [
{
name: this.$t('api_test.automation.batch_add_plan'), handleClick: this.handleBatchAddCase
@ -140,7 +154,16 @@
},
handleBatchExecute() {
let url = "/api/automation/run";
let run = {};
let scenarioIds = this.selection;
run.id = getUUID();
run.scenarioIds = scenarioIds;
this.result = this.$post(url, run, response => {
let data = response.data;
this.runVisible = true;
this.reportId = run.id;
});
},
selectAllChange() {
this.handleCommand("table");
@ -156,7 +179,17 @@
this.$emit('edit', row);
},
execute(row) {
let url = "/api/automation/run";
let run = {};
let scenarioIds = [];
scenarioIds.push(row.id);
run.id = getUUID();
run.scenarioIds = scenarioIds;
this.result = this.$post(url, run, response => {
let data = response.data;
this.runVisible = true;
this.reportId = run.id;
});
},
copy(row) {
row.id = getUUID();

View File

@ -0,0 +1,106 @@
<template>
<div></div>
</template>
<script>
import {getUUID} from "@/common/js/utils";
import {createComponent} from "../../definition/components/jmeter/components";
export default {
name: 'MsDebugRun',
components: {},
props: {
environment: String,
debug: Boolean,
reportId: String,
runData: Object,
},
data() {
return {
result: {},
loading: false,
runId: "",
reqNumber: 0,
}
},
watch: {
//
reportId() {
this.run()
}
},
methods: {
setFiles(item, bodyUploadFiles, obj) {
if (item.body) {
item.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
});
}
});
item.body.binary.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
});
}
});
}
},
recursiveFile(arr, bodyUploadFiles, obj) {
arr.forEach(item => {
this.setFiles(item, bodyUploadFiles, obj);
if (item.hashTree != undefined && item.hashTree.length > 0) {
this.recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
});
},
getBodyUploadFiles(obj) {
let bodyUploadFiles = [];
obj.bodyUploadIds = [];
let request = this.runData;
request.hashTree.forEach(item => {
this.setFiles(item, bodyUploadFiles, obj);
if (item.hashTree != undefined && item.hashTree.length > 0) {
this.recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
})
return bodyUploadFiles;
},
run() {
let testPlan = createComponent('TestPlan');
let threadGroup = createComponent('ThreadGroup');
threadGroup.hashTree = [];
threadGroup.name = this.runData.name;
threadGroup.hashTree.push(this.runData);
testPlan.hashTree.push(threadGroup);
let reqObj = {id: this.reportId, reportId: this.reportId, environmentId: this.environment, testElement: testPlan};
let bodyFiles = this.getBodyUploadFiles(reqObj);
console.log(bodyFiles)
let url = "/api/automation/run/debug";
this.$fileUpload(url, null, bodyFiles, reqObj, response => {
this.runId = response.data;
this.$emit('runRefresh', {});
}, erro => {
});
}
}
}
</script>

View File

@ -261,7 +261,7 @@
@runRefresh="runRefresh" ref="runTest"/>
<!-- 调试结果 -->
<el-drawer :visible.sync="debugVisible" :destroy-on-close="true" direction="ltr" :withHeader="false" :title="$t('test_track.plan_view.test_result')" :modal="false" size="90%">
<ms-api-report-detail :report-id="reportId"/>
<ms-api-report-detail :report-id="reportId" :currentProjectId="currentProject.id"/>
</el-drawer>
</div>
</el-card>
@ -284,7 +284,7 @@
import {getUUID} from "@/common/js/utils";
import ApiEnvironmentConfig from "../../definition/components/environment/ApiEnvironmentConfig";
import MsAddTag from "./AddTag";
import MsRun from "./Run";
import MsRun from "./DebugRun";
import MsImportApiScenario from "./ImportApiScenario";
import MsApiScenarioComponent from "./ApiScenarioComponent";
import MsApiReportDetail from "../report/ApiReportDetail";
@ -333,7 +333,7 @@
expandedNode: [],
scenarioDefinition: [],
path: "/api/automation/create",
debugData: [],
debugData: {},
reportId: "",
}
},
@ -539,9 +539,7 @@
this.$error(this.$t('api_test.environment.select_environment'));
return;
}
let scenario = {id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", referenced: 'Created', environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition};
this.debugData = [];
this.debugData.push(scenario);
this.debugData = {id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", referenced: 'Created', environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition};
this.reportId = getUUID().substring(0, 8);
},
getEnvironments() {
@ -592,11 +590,68 @@
});
return path[0].path;
},
setFiles(item, bodyUploadFiles, obj) {
if (item.body) {
item.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
});
}
});
item.body.binary.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
});
}
});
}
},
recursiveFile(arr, bodyUploadFiles, obj) {
arr.forEach(item => {
this.setFiles(item, bodyUploadFiles, obj);
if (item.hashTree != undefined && item.hashTree.length > 0) {
this.recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
});
},
getBodyUploadFiles(obj) {
let bodyUploadFiles = [];
obj.bodyUploadIds = [];
this.scenarioDefinition.forEach(item => {
this.setFiles(item, bodyUploadFiles, obj);
if (item.hashTree != undefined && item.hashTree.length > 0) {
this.recursiveFile(item.hashTree, bodyUploadFiles, obj);
}
})
return bodyUploadFiles;
},
editScenario() {
this.$refs['currentScenario'].validate((valid) => {
if (valid) {
this.setParameter();
this.result = this.$post(this.path, this.currentScenario, () => {
let bodyFiles = this.getBodyUploadFiles(this.currentScenario);
console.log(bodyFiles)
console.log(this.currentScenario.bodyUploadIds)
this.$fileUpload(this.path, null, bodyFiles, this.currentScenario, () => {
this.$success(this.$t('commons.save_success'));
this.path = "/api/automation/update";
this.currentScenario.tagId = JSON.parse(this.currentScenario.tagId);

View File

@ -1,118 +0,0 @@
<template>
<div></div>
</template>
<script>
import {getUUID} from "@/common/js/utils";
import ThreadGroup from "../../definition/components/jmeter/components/thread-group";
import TestPlan from "../../definition/components/jmeter/components/test-plan";
export default {
name: 'MsRun',
components: {},
props: {
environment: String,
debug: Boolean,
reportId: String,
runData: Array,
},
data() {
return {
result: {},
loading: false,
runId: "",
reqNumber: 0,
}
},
watch: {
//
reportId() {
this.run()
}
},
methods: {
getResult() {
if (this.runId) {
let url = "";
if (this.debug) {
url = "/api/definition/report/get/" + this.runId + "/" + "debug";
} else {
url = "/api/definition/report/get/" + this.runId + "/" + "run";
}
this.$get(url, response => {
if (response.data) {
let data = JSON.parse(response.data.content);
this.$emit('runRefresh', data);
} else {
if (this.reqNumber < 60) {
this.reqNumber++;
setTimeout(this.getResult, 2000);
} else {
this.$error("获取报告超时");
this.$emit('runRefresh', {});
}
}
});
}
},
getBodyUploadFiles(obj) {
let bodyUploadFiles = [];
obj.bodyUploadIds = [];
this.runData.forEach(request => {
if (request.body) {
request.body.kvs.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
});
}
});
request.body.binary.forEach(param => {
if (param.files) {
param.files.forEach(item => {
if (item.file) {
if (!item.id) {
let fileId = getUUID().substring(0, 12);
item.name = item.file.name;
item.id = fileId;
}
obj.bodyUploadIds.push(item.id);
bodyUploadFiles.push(item.file);
}
});
}
});
}
});
return bodyUploadFiles;
},
run() {
let testPlan = new TestPlan();
this.runData.forEach(item => {
let threadGroup = new ThreadGroup();
threadGroup.hashTree = [];
threadGroup.name = item.name;
threadGroup.hashTree.push(item);
testPlan.hashTree.push(threadGroup);
})
console.log("====",testPlan)
let reqObj = {id: this.reportId, reportId: this.reportId, environmentId: this.environment, testElement: testPlan};
let bodyFiles = this.getBodyUploadFiles(reqObj);
let url = "/api/automation/run";
this.$fileUpload(url, null, bodyFiles, reqObj, response => {
this.runId = response.data;
this.$emit('runRefresh', {});
}, erro => {
});
}
}
}
</script>

View File

@ -12,12 +12,7 @@ export const TYPE = "TestPlan";
export default class TestPlan extends HashTreeElement {
constructor(options = DEFAULT_OPTIONS) {
super(options);
this.$type = TYPE;
this.type = TYPE;
this.functionalMode = this.initBoolProp('TestPlan.functional_mode', false);
this.serializeThreadGroups = this.initBoolProp('TestPlan.serialize_threadgroups', false);
this.tearDownOnShutdown = this.initBoolProp('TestPlan.tearDown_on_shutdown', true);
this.userDefineClasspath = this.initStringProp('TestPlan.user_define_classpath');
this.hashTree = [];
this.userDefinedVariables = [];

View File

@ -11,21 +11,7 @@ export const TYPE = "ThreadGroup";
export default class ThreadGroup extends HashTreeElement {
constructor(options = DEFAULT_OPTIONS) {
super(options);
this.$type = TYPE;
this.type = TYPE;
this.onSampleError = this.initStringProp('ThreadGroup.on_sample_error', 'continue');
this.numThreads = this.initStringProp('ThreadGroup.num_threads', 1);
this.rampTime = this.initStringProp('ThreadGroup.ramp_time', 1);
let loopController = this.initElementProp('ThreadGroup.main_controller', 'LoopController');
this.continueForever = loopController.initBoolProp('LoopController.continue_forever', false);
this.loops = loopController.initStringProp('LoopController.loops', 1);
this.sameUserOnNextIteration = this.initBoolProp('ThreadGroup.same_user_on_next_iteration', true);
this.delayedStart = this.initBoolProp('ThreadGroup.delayedStart');
this.scheduler = this.initBoolProp('ThreadGroup.scheduler', false);
this.delay = this.initStringProp('ThreadGroup.delay');
this.duration = this.initStringProp('ThreadGroup.duration');
}
}

View File

@ -15,6 +15,10 @@
{{ $t("i18n.automation") }}
</el-menu-item>
<el-menu-item :index="'/api/automation/report'">
{{ $t("i18n.report") }}
</el-menu-item>
<el-submenu :class="{'deactivation':!isProjectActivation}" v-permission="['test_manager','test_user','test_viewer']" index="3">
<template v-slot:title>{{ $t('commons.project') }}</template>
<ms-recent-list ref="projectRecent" :options="projectRecent"/>

View File

@ -48,6 +48,11 @@ export default {
path: "automation",
name: "ApiAutomation",
component: () => import('@/business/components/api/automation/ApiAutomation'),
},
{
path: "automation/report",
name: "ApiReportList",
component: () => import('@/business/components/api/automation/report/ApiReportList'),
}
]
}

View File

@ -163,7 +163,7 @@ export default {
current_user: "Current user"
}
},
monitor:"monitor"
monitor: "monitor"
},
license: {
title: 'Authorization management',
@ -543,6 +543,7 @@ export default {
customize_script: "Custom script",
customize_req: "Custom request",
reference_info: "Please select interface or use case",
report_name_info: 'Please enter the registration name',
},
environment: {
name: "Environment Name",
@ -779,14 +780,14 @@ export default {
not_exist: "Test report does not exist",
},
api_monitor: {
to:"to",
start_time:"Start Time",
end_time:"End Time",
today:"Today",
this_week:"This Week",
this_mouth:"This Mouth",
please_search:"Please Search",
date:"Date"
to: "to",
start_time: "Start Time",
end_time: "End Time",
today: "Today",
this_week: "This Week",
this_mouth: "This Mouth",
please_search: "Please Search",
date: "Date"
},
test_track: {
test_track: "Track",
@ -1096,6 +1097,7 @@ export default {
home: 'Home',
definition: 'Api Definition',
automation: 'Api Automation',
report: 'Test report',
},
ldap: {
url: 'LDAP URL',

View File

@ -570,7 +570,8 @@ export default {
follow_people: "关注人",
select_table: "选择可见数据",
select_all: "选择全部数据"
}
},
report_name_info: '请输入报名名称',
},
environment: {
name: "环境名称",
@ -809,14 +810,14 @@ export default {
not_exist: "测试报告不存在",
},
api_monitor: {
to:"至",
start_time:"开始日期",
end_time:"结束日期",
today:"今日",
this_week:"本周",
this_mouth:"本月",
please_search:"请搜索",
date:"日期"
to: "至",
start_time: "开始日期",
end_time: "结束日期",
today: "今日",
this_week: "本周",
this_mouth: "本月",
please_search: "请搜索",
date: "日期"
},
test_track: {
test_track: "测试跟踪",
@ -1125,6 +1126,7 @@ export default {
home: '首页',
definition: '接口定义',
automation: '接口自动化',
report: '测试报告',
},
ldap: {
url: 'LDAP地址',

View File

@ -543,8 +543,8 @@ export default {
scenario_import: "場景導入",
customize_script: "自定義腳本",
customize_req: "自定義請求",
reference_info: "請選擇接口或用例"
reference_info: "請選擇接口或用例",
report_name_info: '请输入报名名称',
},
environment: {
name: "環境名稱",
@ -1098,6 +1098,7 @@ export default {
home: '首頁',
definition: '接口定義',
automation: '接口自動化',
report: '測試報告',
},
ldap: {
url: 'LDAP地址',