fix: 解决冲突

This commit is contained in:
chenjianxing 2021-02-18 14:10:57 +08:00
commit b7e12652dd
33 changed files with 891 additions and 558 deletions

View File

@ -49,6 +49,9 @@ public class MsScenario extends MsTestElement {
@JSONField(ordinal = 24) @JSONField(ordinal = 24)
private boolean enableCookieShare; private boolean enableCookieShare;
@JSONField(ordinal = 26)
private List<KeyValue> headers;
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
@Override @Override
@ -87,7 +90,7 @@ public class MsScenario extends MsTestElement {
} }
// 场景变量和环境变量 // 场景变量和环境变量
tree.add(arguments(config)); tree.add(arguments(config));
//this.addCsvDataSet(tree, variables); this.addCsvDataSet(tree, variables);
this.addCounter(tree, variables); this.addCounter(tree, variables);
this.addRandom(tree, variables); this.addRandom(tree, variables);
if (CollectionUtils.isNotEmpty(hashTree)) { if (CollectionUtils.isNotEmpty(hashTree)) {
@ -131,6 +134,12 @@ public class MsScenario extends MsTestElement {
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=") arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
); );
} }
if (CollectionUtils.isNotEmpty(this.headers)) {
this.headers.stream().filter(KeyValue::isValid).filter(KeyValue::isEnable).forEach(keyValue ->
arguments.addArgument(keyValue.getName(), keyValue.getValue(), "=")
);
}
return arguments; return arguments;
} }

View File

@ -79,13 +79,13 @@ public class MsIfController extends MsTestElement {
} }
if (StringUtils.equals(operator, "is empty")) { if (StringUtils.equals(operator, "is empty")) {
variable = "!empty(" + variable + ")"; variable = variable + "==" + "\"\\" + this.variable + "\"" + "|| empty(" + variable + ")";
operator = ""; operator = "";
value = ""; value = "";
} }
if (StringUtils.equals(operator, "is not empty")) { if (StringUtils.equals(operator, "is not empty")) {
variable = "empty(" + variable + ")"; variable = variable + "!=" + "\"\\" + this.variable + "\"" + "&& !empty(" + variable + ")";
operator = ""; operator = "";
value = ""; value = "";
} }

View File

@ -123,13 +123,13 @@ public class MsLoopController extends MsTestElement {
} }
if (StringUtils.equals(operator, "is empty")) { if (StringUtils.equals(operator, "is empty")) {
variable = "!empty(" + variable + ")"; variable = variable + "==" + "\"\\" + this.whileController.getVariable() + "\"" + "|| empty(" + variable + ")";
operator = ""; operator = "";
value = ""; value = "";
} }
if (StringUtils.equals(operator, "is not empty")) { if (StringUtils.equals(operator, "is not empty")) {
variable = "empty(" + variable + ")"; variable = variable + "!=" + "\"\\" + this.whileController.getVariable() + "\"" + "&& !empty(" + variable + ")";
operator = ""; operator = "";
value = ""; value = "";
} }

View File

@ -62,8 +62,6 @@ public class ApiAutomationService {
@Resource @Resource
private ApiScenarioMapper apiScenarioMapper; private ApiScenarioMapper apiScenarioMapper;
@Resource @Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper; private ExtApiScenarioMapper extApiScenarioMapper;
@Resource @Resource
private TestPlanApiScenarioMapper testPlanApiScenarioMapper; private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@ -103,13 +101,13 @@ public class ApiAutomationService {
if (setDefultOrders) { if (setDefultOrders) {
request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders())); request.setOrders(ServiceUtils.getDefaultOrder(request.getOrders()));
} }
if(StringUtils.isNotEmpty(request.getExecuteStatus())){ if (StringUtils.isNotEmpty(request.getExecuteStatus())) {
Map<String,List<String>> statusFilter = new HashMap<>(); Map<String, List<String>> statusFilter = new HashMap<>();
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
list.add("Prepare"); list.add("Prepare");
list.add("Underway"); list.add("Underway");
list.add("Completed"); list.add("Completed");
statusFilter.put("status",list); statusFilter.put("status", list);
request.setFilters(statusFilter); request.setFilters(statusFilter);
} }
if (checkThisWeekData) { if (checkThisWeekData) {
@ -174,7 +172,7 @@ public class ApiAutomationService {
apiScenarioMapper.insert(scenario); apiScenarioMapper.insert(scenario);
List<String> bodyUploadIds = request.getBodyUploadIds(); List<String> bodyUploadIds = request.getBodyUploadIds();
apiDefinitionService.createBodyFiles(bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
return scenario; return scenario;
} }
@ -190,7 +188,7 @@ public class ApiAutomationService {
public void update(SaveApiScenarioRequest request, List<MultipartFile> bodyFiles) { public void update(SaveApiScenarioRequest request, List<MultipartFile> bodyFiles) {
checkNameExist(request); checkNameExist(request);
List<String> bodyUploadIds = request.getBodyUploadIds(); List<String> bodyUploadIds = request.getBodyUploadIds();
apiDefinitionService.createBodyFiles(bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
final ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs(); final ApiScenarioWithBLOBs scenario = new ApiScenarioWithBLOBs();
scenario.setId(request.getId()); scenario.setId(request.getId());
@ -480,8 +478,8 @@ public class ApiAutomationService {
public void checkScenarioIsRunnng(List<String> ids) { public void checkScenarioIsRunnng(List<String> ids) {
List<ApiScenarioReport> lastReportStatusByIds = apiReportService.selectLastReportByIds(ids); List<ApiScenarioReport> lastReportStatusByIds = apiReportService.selectLastReportByIds(ids);
for (ApiScenarioReport report : lastReportStatusByIds) { for (ApiScenarioReport report : lastReportStatusByIds) {
if(StringUtils.equals(report.getStatus(),APITestStatus.Running.name())){ if (StringUtils.equals(report.getStatus(), APITestStatus.Running.name())) {
MSException.throwException(report.getName()+" Is Running!"); MSException.throwException(report.getName() + " Is Running!");
} }
} }
} }
@ -518,7 +516,7 @@ public class ApiAutomationService {
*/ */
public String debugRun(RunDefinitionRequest request, List<MultipartFile> bodyFiles) { public String debugRun(RunDefinitionRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
apiDefinitionService.createBodyFiles(bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
EnvironmentConfig envConfig = null; EnvironmentConfig envConfig = null;
if (request.getEnvironmentId() != null) { if (request.getEnvironmentId() != null) {
ApiTestEnvironmentWithBLOBs environment = environmentService.get(request.getEnvironmentId()); ApiTestEnvironmentWithBLOBs environment = environmentService.get(request.getEnvironmentId());

View File

@ -44,7 +44,7 @@ import org.springframework.web.multipart.MultipartFile;
import sun.security.util.Cache; import sun.security.util.Cache;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.*; import java.io.File;
import java.util.*; import java.util.*;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -131,7 +131,7 @@ public class ApiDefinitionService {
public void create(SaveApiDefinitionRequest request, List<MultipartFile> bodyFiles) { public void create(SaveApiDefinitionRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
createTest(request); createTest(request);
createBodyFiles(bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
} }
public void update(SaveApiDefinitionRequest request, List<MultipartFile> bodyFiles) { public void update(SaveApiDefinitionRequest request, List<MultipartFile> bodyFiles) {
@ -141,27 +141,7 @@ public class ApiDefinitionService {
List<String> bodyUploadIds = request.getBodyUploadIds(); List<String> bodyUploadIds = request.getBodyUploadIds();
request.setBodyUploadIds(null); request.setBodyUploadIds(null);
updateTest(request); updateTest(request);
createBodyFiles(bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
}
public void createBodyFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (CollectionUtils.isNotEmpty(bodyUploadIds) && CollectionUtils.isNotEmpty(bodyFiles)) {
File testDir = new File(BODY_FILE_DIR);
if (!testDir.exists()) {
testDir.mkdirs();
}
for (int i = 0; i < bodyUploadIds.size(); i++) {
MultipartFile item = bodyFiles.get(i);
File file = new File(BODY_FILE_DIR + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(Translator.get("upload_fail"));
}
}
}
} }
public void delete(String apiId) { public void delete(String apiId) {
@ -404,7 +384,7 @@ public class ApiDefinitionService {
*/ */
public String run(RunDefinitionRequest request, List<MultipartFile> bodyFiles) { public String run(RunDefinitionRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
createBodyFiles(bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
HashTree hashTree = request.getTestElement().generateHashTree(); HashTree hashTree = request.getTestElement().generateHashTree();
String runMode = ApiRunMode.DEFINITION.name(); String runMode = ApiRunMode.DEFINITION.name();
@ -541,7 +521,7 @@ public class ApiDefinitionService {
public void editApiByParam(ApiBatchRequest request) { public void editApiByParam(ApiBatchRequest request) {
List<String> ids = request.getIds(); List<String> ids = request.getIds();
if (request.isSelectAllDate()) { if (request.isSelectAllDate()) {
ids = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(),request.getProtocol()); ids = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(), request.getProtocol());
} }
//name在这里只是查询参数 //name在这里只是查询参数
request.setName(null); request.setName(null);
@ -605,14 +585,14 @@ public class ApiDefinitionService {
public void deleteByParams(ApiDefinitionBatchProcessingRequest request) { public void deleteByParams(ApiDefinitionBatchProcessingRequest request) {
List<String> apiIds = request.getDataIds(); List<String> apiIds = request.getDataIds();
if (request.isSelectAllDate()) { if (request.isSelectAllDate()) {
apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(),request.getProtocol()); apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(), request.getProtocol());
} }
ApiDefinitionExample example = new ApiDefinitionExample(); ApiDefinitionExample example = new ApiDefinitionExample();
example.createCriteria().andIdIn(apiIds); example.createCriteria().andIdIn(apiIds);
apiDefinitionMapper.deleteByExample(example); apiDefinitionMapper.deleteByExample(example);
} }
private List<String> getAllApiIdsByFontedSelect(Map<String, List<String>> filters, String name, List<String> moduleIds, String projectId, List<String> unSelectIds,String protocol) { private List<String> getAllApiIdsByFontedSelect(Map<String, List<String>> filters, String name, List<String> moduleIds, String projectId, List<String> unSelectIds, String protocol) {
ApiDefinitionRequest request = new ApiDefinitionRequest(); ApiDefinitionRequest request = new ApiDefinitionRequest();
request.setFilters(filters); request.setFilters(filters);
request.setName(name); request.setName(name);
@ -632,7 +612,7 @@ public class ApiDefinitionService {
public void removeToGcByParams(ApiDefinitionBatchProcessingRequest request) { public void removeToGcByParams(ApiDefinitionBatchProcessingRequest request) {
List<String> apiIds = request.getDataIds(); List<String> apiIds = request.getDataIds();
if (request.isSelectAllDate()) { if (request.isSelectAllDate()) {
apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(),request.getProtocol()); apiIds = this.getAllApiIdsByFontedSelect(request.getFilters(), request.getName(), request.getModuleIds(), request.getProjectId(), request.getUnSelectIds(), request.getProtocol());
} }
extApiDefinitionMapper.removeToGc(apiIds); extApiDefinitionMapper.removeToGc(apiIds);
} }

View File

@ -29,6 +29,7 @@ import io.metersphere.service.FileService;
import io.metersphere.service.QuotaService; import io.metersphere.service.QuotaService;
import io.metersphere.service.UserService; import io.metersphere.service.UserService;
import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest; import io.metersphere.track.request.testcase.ApiCaseRelevanceRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSession;
@ -38,11 +39,10 @@ import org.apache.jorphan.collections.ListedHashTree;
import org.aspectj.util.FileUtil; import org.aspectj.util.FileUtil;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.*; import java.io.File;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -72,7 +72,7 @@ public class ApiTestCaseService {
@Resource @Resource
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
@Resource @Resource
TestPlanApiCaseMapper testPlanApiCaseMapper; private TestPlanApiCaseMapper testPlanApiCaseMapper;
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body"; private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
@ -136,7 +136,7 @@ public class ApiTestCaseService {
public ApiTestCase create(SaveApiTestCaseRequest request, List<MultipartFile> bodyFiles) { public ApiTestCase create(SaveApiTestCaseRequest request, List<MultipartFile> bodyFiles) {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
ApiTestCase test = createTest(request); ApiTestCase test = createTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
return test; return test;
} }
@ -152,31 +152,10 @@ public class ApiTestCaseService {
List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds()); List<String> bodyUploadIds = new ArrayList<>(request.getBodyUploadIds());
request.setBodyUploadIds(null); request.setBodyUploadIds(null);
ApiTestCase test = updateTest(request); ApiTestCase test = updateTest(request);
createBodyFiles(test, bodyUploadIds, bodyFiles); FileUtils.createBodyFiles(bodyUploadIds, bodyFiles);
return test; return test;
} }
private void createBodyFiles(ApiTestCase test, List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (bodyUploadIds.size() > 0) {
String dir = BODY_FILE_DIR + "/" + test.getId();
File testDir = new File(dir);
if (!testDir.exists()) {
testDir.mkdirs();
}
for (int i = 0; i < bodyUploadIds.size(); i++) {
MultipartFile item = bodyFiles.get(i);
File file = new File(testDir + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(Translator.get("upload_fail"));
}
}
}
}
public void delete(String testId) { public void delete(String testId) {
extTestPlanTestCaseMapper.deleteByTestCaseID(testId); extTestPlanTestCaseMapper.deleteByTestCaseID(testId);
deleteFileByTestId(testId); deleteFileByTestId(testId);
@ -279,19 +258,14 @@ public class ApiTestCaseService {
} }
} }
private void saveFile(String testId, MultipartFile file) {
final FileMetadata fileMetadata = fileService.saveFile(file);
ApiTestFile apiTestFile = new ApiTestFile();
apiTestFile.setTestId(testId);
apiTestFile.setFileId(fileMetadata.getId());
apiTestFileMapper.insert(apiTestFile);
}
private void deleteFileByTestId(String testId) { private void deleteFileByTestId(String testId) {
ApiTestFileExample ApiTestFileExample = new ApiTestFileExample(); ApiTestFileExample ApiTestFileExample = new ApiTestFileExample();
ApiTestFileExample.createCriteria().andTestIdEqualTo(testId); ApiTestFileExample.createCriteria().andTestIdEqualTo(testId);
final List<ApiTestFile> ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample); final List<ApiTestFile> ApiTestFiles = apiTestFileMapper.selectByExample(ApiTestFileExample);
if (CollectionUtils.isNotEmpty(ApiTestFiles)) {
apiTestFileMapper.deleteByExample(ApiTestFileExample); apiTestFileMapper.deleteByExample(ApiTestFileExample);
}
if (!CollectionUtils.isEmpty(ApiTestFiles)) { if (!CollectionUtils.isEmpty(ApiTestFiles)) {
final List<String> fileIds = ApiTestFiles.stream().map(ApiTestFile::getFileId).collect(Collectors.toList()); final List<String> fileIds = ApiTestFiles.stream().map(ApiTestFile::getFileId).collect(Collectors.toList());

View File

@ -7,6 +7,7 @@ import io.metersphere.api.dto.SaveHistoricalDataUpgrade;
import io.metersphere.api.dto.automation.ScenarioStatus; import io.metersphere.api.dto.automation.ScenarioStatus;
import io.metersphere.api.dto.definition.request.MsScenario; import io.metersphere.api.dto.definition.request.MsScenario;
import io.metersphere.api.dto.definition.request.MsTestElement; import io.metersphere.api.dto.definition.request.MsTestElement;
import io.metersphere.api.dto.definition.request.assertions.MsAssertionDuration;
import io.metersphere.api.dto.definition.request.assertions.MsAssertions; import io.metersphere.api.dto.definition.request.assertions.MsAssertions;
import io.metersphere.api.dto.definition.request.controller.MsIfController; import io.metersphere.api.dto.definition.request.controller.MsIfController;
import io.metersphere.api.dto.definition.request.extract.MsExtract; import io.metersphere.api.dto.definition.request.extract.MsExtract;
@ -84,6 +85,7 @@ public class HistoricalDataUpgradeService {
scenario.setReferenced("Upgrade"); scenario.setReferenced("Upgrade");
scenario.setId(oldScenario.getId()); scenario.setId(oldScenario.getId());
scenario.setResourceId(UUID.randomUUID().toString()); scenario.setResourceId(UUID.randomUUID().toString());
scenario.setHeaders(oldScenario.getHeaders());
LinkedList<MsTestElement> testElements = new LinkedList<>(); LinkedList<MsTestElement> testElements = new LinkedList<>();
int index = 1; int index = 1;
for (Request request : oldScenario.getRequests()) { for (Request request : oldScenario.getRequests()) {
@ -202,6 +204,23 @@ public class HistoricalDataUpgradeService {
if (StringUtils.isEmpty(msAssertions.getName())) { if (StringUtils.isEmpty(msAssertions.getName())) {
msAssertions.setName("Assertions"); msAssertions.setName("Assertions");
} }
// 给初始值
if (msAssertions.getDuration() == null) {
msAssertions.setDuration(new MsAssertionDuration());
}
if (CollectionUtils.isEmpty(msAssertions.getJsr223())) {
msAssertions.setJsr223(new LinkedList<>());
}
if (CollectionUtils.isEmpty(msAssertions.getXpath2())) {
msAssertions.setXpath2(new LinkedList<>());
}
if (CollectionUtils.isEmpty(msAssertions.getJsonPath())) {
msAssertions.setJsonPath(new LinkedList<>());
}
if (CollectionUtils.isEmpty(msAssertions.getRegex())) {
msAssertions.setRegex(new LinkedList<>());
}
msAssertions.setType("Assertions"); msAssertions.setType("Assertions");
msAssertions.setIndex(index + ""); msAssertions.setIndex(index + "");
msAssertions.setResourceId(UUID.randomUUID().toString()); msAssertions.setResourceId(UUID.randomUUID().toString());
@ -216,6 +235,16 @@ public class HistoricalDataUpgradeService {
if (StringUtils.isEmpty(extract.getName())) { if (StringUtils.isEmpty(extract.getName())) {
extract.setName("Extract"); extract.setName("Extract");
} }
// 默认给初始值
if (CollectionUtils.isEmpty(extract.getJson())) {
extract.setJson(new LinkedList<>());
}
if (CollectionUtils.isEmpty(extract.getXpath())) {
extract.setXpath(new LinkedList<>());
}
if (CollectionUtils.isEmpty(extract.getRegex())) {
extract.setRegex(new LinkedList<>());
}
extract.setType("Extract"); extract.setType("Extract");
extract.setIndex(index + ""); extract.setIndex(index + "");
extract.setHashTree(new LinkedList<>()); extract.setHashTree(new LinkedList<>());

View File

@ -0,0 +1,34 @@
package io.metersphere.commons.utils;
import io.metersphere.commons.exception.MSException;
import io.metersphere.i18n.Translator;
import org.apache.commons.collections.CollectionUtils;
import org.aspectj.util.FileUtil;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
import java.util.List;
public class FileUtils {
private static final String BODY_FILE_DIR = "/opt/metersphere/data/body";
public static void createBodyFiles(List<String> bodyUploadIds, List<MultipartFile> bodyFiles) {
if (CollectionUtils.isNotEmpty(bodyUploadIds) && CollectionUtils.isNotEmpty(bodyFiles)) {
File testDir = new File(BODY_FILE_DIR);
if (!testDir.exists()) {
testDir.mkdirs();
}
for (int i = 0; i < bodyUploadIds.size(); i++) {
MultipartFile item = bodyFiles.get(i);
File file = new File(BODY_FILE_DIR + "/" + bodyUploadIds.get(i) + "_" + item.getOriginalFilename());
try (InputStream in = item.getInputStream(); OutputStream out = new FileOutputStream(file)) {
file.createNewFile();
FileUtil.copyStream(in, out);
} catch (IOException e) {
LogUtil.error(e);
MSException.throwException(Translator.get("upload_fail"));
}
}
}
}
}

View File

@ -11,6 +11,7 @@ import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.sql.SQLException;
@RestControllerAdvice @RestControllerAdvice
@ -30,6 +31,12 @@ public class RestControllerExceptionHandler {
} }
@ExceptionHandler(SQLException.class)
public ResultHolder sqlExceptionHandler(HttpServletRequest request, HttpServletResponse response, MSException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return ResultHolder.error("SQL error happened, please check logs.");
}
@ExceptionHandler(MSException.class) @ExceptionHandler(MSException.class)
public ResultHolder msExceptionHandler(HttpServletRequest request, HttpServletResponse response, MSException e) { public ResultHolder msExceptionHandler(HttpServletRequest request, HttpServletResponse response, MSException e) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value()); response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

View File

@ -180,6 +180,7 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
List<String> projectIds = testPlanProjectService.getProjectIdsByPlanId(planId); List<String> projectIds = testPlanProjectService.getProjectIdsByPlanId(planId);
projectIds.forEach(id -> { projectIds.forEach(id -> {
Project project = projectMapper.selectByPrimaryKey(id); Project project = projectMapper.selectByPrimaryKey(id);
if (project != null) {
String name = project.getName(); String name = project.getName();
List<TestCaseNodeDTO> nodeList = getNodeDTO(id, planId); List<TestCaseNodeDTO> nodeList = getNodeDTO(id, planId);
TestCaseNodeDTO testCaseNodeDTO = new TestCaseNodeDTO(); TestCaseNodeDTO testCaseNodeDTO = new TestCaseNodeDTO();
@ -188,6 +189,7 @@ public class TestCaseNodeService extends NodeTreeService<TestCaseNodeDTO> {
testCaseNodeDTO.setLabel(name); testCaseNodeDTO.setLabel(name);
testCaseNodeDTO.setChildren(nodeList); testCaseNodeDTO.setChildren(nodeList);
list.add(testCaseNodeDTO); list.add(testCaseNodeDTO);
}
}); });
return list; return list;

View File

@ -62,15 +62,16 @@ public class TestPlanProjectService {
} }
public List<String> getPlanIdByProjectId(String projectId) { public List<String> getPlanIdByProjectId(String projectId) {
TestPlanProjectExample testPlanProjectExample = new TestPlanProjectExample(); TestPlanExample testPlanExample = new TestPlanExample();
testPlanProjectExample.createCriteria().andProjectIdEqualTo(projectId); testPlanExample.createCriteria().andProjectIdEqualTo(projectId);
List<TestPlanProject> testPlanProjects = testPlanProjectMapper.selectByExample(testPlanProjectExample); List<TestPlan> testPlans = testPlanMapper.selectByExample(testPlanExample);
if (CollectionUtils.isEmpty(testPlanProjects)) {
if (CollectionUtils.isEmpty(testPlans)) {
return null; return null;
} }
return testPlanProjects return testPlans
.stream() .stream()
.map(TestPlanProject::getTestPlanId) .map(TestPlan::getId)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
} }

View File

@ -277,12 +277,16 @@ public class XmindCaseParser {
String tc = title.replace("", ":"); String tc = title.replace("", ":");
String[] tcArr = tc.split(":"); String[] tcArr = tc.split(":");
if (tcArr.length != 2) { if (tcArr.length < 1) {
process.add(Translator.get("test_case_name") + Translator.get("incorrect_format"), title); process.add(Translator.get("test_case_name") + Translator.get("incorrect_format"), title);
return; return;
} }
// 用例名称 // 用例名称
testCase.setName(this.replace(tcArr[1], TC_REGEX)); StringBuffer name = new StringBuffer();
for (int i = 1; i < tcArr.length; i++) {
name.append(tcArr[i]);
}
testCase.setName(name.toString());
testCase.setNodePath(nodePath); testCase.setNodePath(nodePath);
// 用例等级和用例性质处理 // 用例等级和用例性质处理

View File

@ -0,0 +1,4 @@
ALTER TABLE schedule
MODIFY COLUMN id VARCHAR (255);
ALTER TABLE message_task
MODIFY COLUMN id VARCHAR (255);

View File

@ -0,0 +1,4 @@
ALTER TABLE schedule
MODIFY COLUMN resource_id VARCHAR (255);
ALTER TABLE message_task
MODIFY COLUMN test_id VARCHAR (255);

View File

@ -106,7 +106,7 @@
</el-col> </el-col>
<el-col :span="3" class="ms-col-one ms-font"> <el-col :span="3" class="ms-col-one ms-font">
<el-link class="head" @click="showScenarioParameters">{{$t('api_test.automation.scenario_total')}}</el-link> <el-link class="head" @click="showScenarioParameters">{{$t('api_test.automation.scenario_total')}}</el-link>
{{this.currentScenario.variables!=undefined?this.currentScenario.variables.length: 0}} {{ getVariableSize() }}
</el-col> </el-col>
<el-col :span="3" class="ms-col-one ms-font"> <el-col :span="3" class="ms-col-one ms-font">
<el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox> <el-checkbox v-model="enableCookieShare">共享cookie</el-checkbox>
@ -196,7 +196,7 @@
</el-drawer> </el-drawer>
<!--场景公共参数--> <!--场景公共参数-->
<ms-variable-list @setVariables="setVariables" ref="scenarioParameters"/> <ms-variable-list @setVariables="setVariables" ref="scenarioParameters" class="ms-sc-variable-header"/>
<!--外部导入--> <!--外部导入-->
<api-import ref="apiImport" :saved="false" @refresh="apiImport"/> <api-import ref="apiImport" :saved="false" @refresh="apiImport"/>
</div> </div>
@ -204,27 +204,34 @@
</template> </template>
<script> <script>
import {API_STATUS, PRIORITY} from "../../definition/model/JsonData"; import {API_STATUS, PRIORITY} from "../../definition/model/JsonData";
import {WORKSPACE_ID} from '@/common/js/constants'; import {WORKSPACE_ID} from '@/common/js/constants';
import {Assertions, Extract, IfController, JSR223Processor, ConstantTimer, LoopController} from "../../definition/model/ApiTestModel"; import {
import {parseEnvironment} from "../../definition/model/EnvironmentModel"; Assertions,
import {ELEMENTS, ELEMENT_TYPE} from "./Setting"; ConstantTimer,
import MsApiCustomize from "./ApiCustomize"; Extract,
import {getUUID, getCurrentProjectID} from "@/common/js/utils"; IfController,
import ApiEnvironmentConfig from "../../definition/components/environment/ApiEnvironmentConfig"; JSR223Processor,
import MsInputTag from "./MsInputTag"; LoopController
import MsRun from "./DebugRun"; } from "../../definition/model/ApiTestModel";
import MsApiReportDetail from "../report/ApiReportDetail"; import {parseEnvironment} from "../../definition/model/EnvironmentModel";
import MsVariableList from "./variable/VariableList"; import {ELEMENT_TYPE, ELEMENTS} from "./Setting";
import ApiImport from "../../definition/components/import/ApiImport"; import MsApiCustomize from "./ApiCustomize";
import "@/common/css/material-icons.css" import {getCurrentProjectID, getUUID} from "@/common/js/utils";
import OutsideClick from "@/common/js/outside-click"; import ApiEnvironmentConfig from "../../definition/components/environment/ApiEnvironmentConfig";
import ScenarioApiRelevance from "./api/ApiRelevance"; import MsInputTag from "./MsInputTag";
import ScenarioRelevance from "./api/ScenarioRelevance"; import MsRun from "./DebugRun";
import MsComponentConfig from "./component/ComponentConfig"; import MsApiReportDetail from "../report/ApiReportDetail";
import {handleCtrlSEvent} from "../../../../../common/js/utils"; import MsVariableList from "./variable/VariableList";
import ApiImport from "../../definition/components/import/ApiImport";
import "@/common/css/material-icons.css"
import OutsideClick from "@/common/js/outside-click";
import ScenarioApiRelevance from "./api/ApiRelevance";
import ScenarioRelevance from "./api/ScenarioRelevance";
import MsComponentConfig from "./component/ComponentConfig";
import {handleCtrlSEvent} from "../../../../../common/js/utils";
export default { export default {
name: "EditApiScenario", name: "EditApiScenario",
props: { props: {
moduleOptions: Array, moduleOptions: Array,
@ -427,8 +434,9 @@
getIdx(index) { getIdx(index) {
return index - 0.33 return index - 0.33
}, },
setVariables(v) { setVariables(v, headers) {
this.currentScenario.variables = v; this.currentScenario.variables = v;
this.currentScenario.headers = headers;
if (this.path.endsWith("/update")) { if (this.path.endsWith("/update")) {
// //
this.editScenario(); this.editScenario();
@ -503,7 +511,7 @@
this.sort(); this.sort();
}, },
nodeClick(data, node) { nodeClick(data, node) {
if (data.referenced != 'REF' && data.referenced != 'Deleted') { if (data.referenced != 'REF' && data.referenced != 'Deleted' && !data.disabled) {
this.operatingElements = ELEMENTS.get(data.type); this.operatingElements = ELEMENTS.get(data.type);
} else { } else {
this.operatingElements = []; this.operatingElements = [];
@ -678,9 +686,15 @@
if (valid) { if (valid) {
this.editScenario(); this.editScenario();
this.debugData = { this.debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", id: this.currentScenario.id,
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare, name: this.currentScenario.name,
environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition type: "scenario",
variables: this.currentScenario.variables,
referenced: 'Created',
enableCookieShare: this.enableCookieShare,
headers: this.currentScenario.headers,
environmentId: this.currentEnvironmentId,
hashTree: this.scenarioDefinition
}; };
this.reportId = getUUID().substring(0, 8); this.reportId = getUUID().substring(0, 8);
} }
@ -863,6 +877,9 @@
if (!this.currentScenario.variables) { if (!this.currentScenario.variables) {
this.currentScenario.variables = []; this.currentScenario.variables = [];
} }
if (!this.currentScenario.headers) {
this.currentScenario.headers = [];
}
if (this.currentScenario.id) { if (this.currentScenario.id) {
this.result = this.$get("/api/automation/getApiScenario/" + this.currentScenario.id, response => { this.result = this.$get("/api/automation/getApiScenario/" + this.currentScenario.id, response => {
if (response.data) { if (response.data) {
@ -887,6 +904,9 @@
} }
}) })
} }
if (obj.headers) {
this.currentScenario.headers = obj.headers;
}
this.enableCookieShare = obj.enableCookieShare; this.enableCookieShare = obj.enableCookieShare;
this.scenarioDefinition = obj.hashTree; this.scenarioDefinition = obj.hashTree;
} }
@ -905,8 +925,15 @@
this.currentScenario.modulePath = this.getPath(this.currentScenario.apiScenarioModuleId); this.currentScenario.modulePath = this.getPath(this.currentScenario.apiScenarioModuleId);
// 便 // 便
let scenario = { let scenario = {
id: this.currentScenario.id, enableCookieShare: this.enableCookieShare, name: this.currentScenario.name, variables: this.currentScenario.variables, id: this.currentScenario.id,
type: "scenario", referenced: 'Created', environmentId: this.currentEnvironmentId, hashTree: this.scenarioDefinition enableCookieShare: this.enableCookieShare,
name: this.currentScenario.name,
type: "scenario",
variables: this.currentScenario.variables,
headers: this.currentScenario.headers,
referenced: 'Created',
environmentId: this.currentEnvironmentId,
hashTree: this.scenarioDefinition
}; };
this.currentScenario.scenarioDefinition = scenario; this.currentScenario.scenarioDefinition = scenario;
if (this.currentScenario.tags instanceof Array) { if (this.currentScenario.tags instanceof Array) {
@ -924,7 +951,7 @@
this.loading = false; this.loading = false;
}, },
showScenarioParameters() { showScenarioParameters() {
this.$refs.scenarioParameters.open(this.currentScenario.variables); this.$refs.scenarioParameters.open(this.currentScenario.variables, this.currentScenario.headers);
}, },
apiImport(importData) { apiImport(importData) {
if (importData && importData.data) { if (importData && importData.data) {
@ -934,6 +961,16 @@
this.sort(); this.sort();
this.reload(); this.reload();
} }
},
getVariableSize() {
let size = 0;
if (this.currentScenario.variables) {
size += this.currentScenario.variables.length;
}
if (this.currentScenario.headers && this.currentScenario.headers.length > 1) {
size += this.currentScenario.headers.length - 1;
}
return size;
} }
} }
} }
@ -1066,4 +1103,8 @@
content: "\e722"; content: "\e722";
font-size: 20px; font-size: 20px;
} }
.ms-sc-variable-header >>> .el-dialog__body {
padding: 0px 20px;
}
</style> </style>

View File

@ -107,7 +107,8 @@
}, },
methods: { methods: {
active() { active() {
this.$set(this.data, 'active', !this.data.active); //
//this.$set(this.data, 'active', !this.data.active);
this.$emit('active'); this.$emit('active');
}, },
copyRow() { copyRow() {

View File

@ -1,12 +1,14 @@
<template> <template>
<div> <div>
<div v-if="request.protocol === 'HTTP'"> <div v-if="request.protocol === 'HTTP'">
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-if="request.url" v-model="request.url" style="width: 85%;margin-top: 10px" size="small"> <el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-if="request.url" v-model="request.url"
style="width: 85%;margin-top: 10px" size="small" @blur="urlChange">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small"> <el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/> <el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select> </el-select>
</el-input> </el-input>
<el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-else v-model="request.path" style="width: 85%;margin-top: 10px" size="small"> <el-input :placeholder="$t('api_test.definition.request.path_all_info')" v-else v-model="request.path"
style="width: 85%;margin-top: 10px" size="small" @blur="pathChange">
<el-select v-model="request.method" slot="prepend" style="width: 100px" size="small"> <el-select v-model="request.method" slot="prepend" style="width: 100px" size="small">
<el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/> <el-option v-for="item in reqOptions" :key="item.id" :label="item.label" :value="item.id"/>
</el-select> </el-select>
@ -34,6 +36,7 @@
<script> <script>
import {REQ_METHOD} from "@/business/components/api/definition/model/JsonData"; import {REQ_METHOD} from "@/business/components/api/definition/model/JsonData";
import {KeyValue} from "../../../definition/model/ApiTestModel";
export default { export default {
name: "CustomizeReqInfo", name: "CustomizeReqInfo",
@ -42,6 +45,43 @@ export default {
return { return {
reqOptions: REQ_METHOD, reqOptions: REQ_METHOD,
} }
},
methods: {
pathChange() {
if (!this.request.path || this.request.path.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.request.path));
if (url) {
this.request.path = decodeURIComponent(this.request.path.substr(0, this.request.path.indexOf("?")));
}
},
urlChange() {
if (!this.request.url || this.request.url.indexOf('?') === -1) return;
let url = this.getURL(this.addProtocol(this.request.url));
if (url) {
this.request.url = decodeURIComponent(this.request.url.substr(0, this.request.url.indexOf("?")));
}
},
addProtocol(url) {
if (url) {
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
return "https://" + url;
}
}
return url;
},
getURL(urlStr) {
try {
let url = new URL(urlStr);
url.searchParams.forEach((value, key) => {
if (key && value) {
this.request.arguments.splice(0, 0, new KeyValue({name: key, required: false, value: value}));
}
});
return url;
} catch (e) {
this.$error(this.$t('api_test.request.url_invalid'), 2000);
}
},
} }
} }
</script> </script>

View File

@ -3,6 +3,7 @@
v-loading="loading" v-loading="loading"
@copy="copyRow" @copy="copyRow"
@remove="remove" @remove="remove"
@active="active"
:is-show-name-input="!isDeletedOrRef" :is-show-name-input="!isDeletedOrRef"
:data="request" :data="request"
:draggable="true" :draggable="true"
@ -27,37 +28,50 @@
<customize-req-info :is-customize-req="isCustomizeReq" :request="request"/> <customize-req-info :is-customize-req="isCustomizeReq" :request="request"/>
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p> <p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<ms-api-request-form :isShowEnable="true" :referenced="true" :headers="request.headers " :request="request" v-if="request.protocol==='HTTP' || request.type==='HTTPSamplerProxy'"/> <ms-api-request-form :isShowEnable="true" :referenced="true" :headers="request.headers " :request="request"
v-if="request.protocol==='HTTP' || request.type==='HTTPSamplerProxy'"/>
<ms-tcp-basis-parameters :request="request" v-if="request.protocol==='TCP'|| request.type==='TCPSampler'"/> <ms-tcp-basis-parameters :request="request" v-if="request.protocol==='TCP'|| request.type==='TCPSampler'"/>
<ms-sql-basis-parameters :request="request" v-if="request.protocol==='SQL'|| request.type==='JDBCSampler'" :showScript="false"/> <ms-sql-basis-parameters :request="request" v-if="request.protocol==='SQL'|| request.type==='JDBCSampler'"
<ms-dubbo-basis-parameters :request="request" v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'" :showScript="false"/> :showScript="false"/>
<ms-dubbo-basis-parameters :request="request"
v-if="request.protocol==='DUBBO' || request.protocol==='dubbo://'|| request.type==='DubboSampler'"
:showScript="false"/>
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p> <p class="tip">{{ $t('api_test.definition.request.res_param') }} </p>
<api-response-component :currentProtocol="request.protocol" :result="request.requestResult"/> <div v-if="request.result">
<el-tabs v-model="request.activeName" closable class="ms-tabs">
<el-tab-pane :label="item.name" :name="item.name" v-for="(item,index) in request.result.scenarios" :key="index">
<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">
<api-response-component v-if="result.name===request.name" :result="result"/>
</div>
</el-tab-pane>
</el-tabs>
</div>
<api-response-component :currentProtocol="request.protocol" :result="request.requestResult" v-else/>
<!-- 保存操作 --> <!-- 保存操作 -->
<el-button type="primary" size="small" style="margin: 20px; float: right" @click="saveTestCase(item)" v-if="!request.referenced"> <el-button type="primary" size="small" style="margin: 20px; float: right" @click="saveTestCase(item)"
{{$t('commons.save')}} v-if="!request.referenced">
{{ $t('commons.save') }}
</el-button> </el-button>
</api-base-component> </api-base-component>
</template> </template>
<script> <script>
import MsSqlBasisParameters from "../../../definition/components/request/database/BasisParameters"; import MsSqlBasisParameters from "../../../definition/components/request/database/BasisParameters";
import MsTcpBasisParameters from "../../../definition/components/request/tcp/TcpBasisParameters"; import MsTcpBasisParameters from "../../../definition/components/request/tcp/TcpBasisParameters";
import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters"; import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters";
import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm"; import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm";
import {REQ_METHOD} from "../../../definition/model/JsonData"; import MsRequestResultTail from "../../../definition/components/response/RequestResultTail";
import MsRequestResultTail from "../../../definition/components/response/RequestResultTail"; import MsRun from "../../../definition/components/Run";
import MsRun from "../../../definition/components/Run"; import {getUUID} from "@/common/js/utils";
import {getUUID} from "@/common/js/utils"; import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiResponseComponent from "./ApiResponseComponent";
import ApiResponseComponent from "./ApiResponseComponent"; import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo";
import CustomizeReqInfo from "@/business/components/api/automation/scenario/common/CustomizeReqInfo";
export default { export default {
name: "MsApiComponent", name: "MsApiComponent",
props: { props: {
request: {}, request: {},
@ -155,7 +169,7 @@
return true return true
} }
return false; return false;
} },
}, },
methods: { methods: {
remove() { remove() {
@ -238,8 +252,8 @@
this.request.customizeReq = this.isCustomizeReq; this.request.customizeReq = this.isCustomizeReq;
let debugData = { let debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario",
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare, variables: this.currentScenario.variables, referenced: 'Created', headers: this.currentScenario.headers,
environmentId: this.currentEnvironmentId, hashTree: [this.request] enableCookieShare: this.enableCookieShare, environmentId: this.currentEnvironmentId, hashTree: [this.request]
}; };
this.runData.push(debugData); this.runData.push(debugData);
/*触发执行操作*/ /*触发执行操作*/
@ -247,6 +261,7 @@
}, },
runRefresh(data) { runRefresh(data) {
this.request.requestResult = data; this.request.requestResult = data;
this.request.result = undefined;
this.loading = false; this.loading = false;
}, },
reload() { reload() {
@ -289,4 +304,9 @@
.icon.is-active { .icon.is-active {
transform: rotate(90deg); transform: rotate(90deg);
} }
.ms-tabs >>> .el-icon-close:before {
content: "";
}
</style> </style>

View File

@ -3,6 +3,7 @@
v-loading="loading" v-loading="loading"
@copy="copyRow" @copy="copyRow"
@remove="remove" @remove="remove"
@active="active"
:data="scenario" :data="scenario"
:show-collapse="false" :show-collapse="false"
:is-show-name-input="!isDeletedOrRef" :is-show-name-input="!isDeletedOrRef"
@ -21,13 +22,13 @@
</template> </template>
<script> <script>
import MsSqlBasisParameters from "../../../definition/components/request/database/BasisParameters"; import MsSqlBasisParameters from "../../../definition/components/request/database/BasisParameters";
import MsTcpBasisParameters from "../../../definition/components/request/tcp/TcpBasisParameters"; import MsTcpBasisParameters from "../../../definition/components/request/tcp/TcpBasisParameters";
import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters"; import MsDubboBasisParameters from "../../../definition/components/request/dubbo/BasisParameters";
import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm"; import MsApiRequestForm from "../../../definition/components/request/http/ApiHttpRequestForm";
import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiBaseComponent from "../common/ApiBaseComponent";
export default { export default {
name: "ApiScenarioComponent", name: "ApiScenarioComponent",
props: { props: {
scenario: {}, scenario: {},

View File

@ -2,11 +2,12 @@
<api-base-component <api-base-component
@copy="copyRow" @copy="copyRow"
@remove="remove" @remove="remove"
@active="active"
:data="jsr223Processor" :data="jsr223Processor"
:draggable="draggable" :draggable="draggable"
:color="color" :color="color"
:background-color="backgroundColor" :background-color="backgroundColor"
:title="title"> :title="title" v-loading="loading">
<jsr233-processor-content <jsr233-processor-content
:jsr223-processor="jsr223Processor" :jsr223-processor="jsr223Processor"
@ -18,13 +19,13 @@
</template> </template>
<script> <script>
import MsCodeEdit from "../../../../common/components/MsCodeEdit"; import MsCodeEdit from "../../../../common/components/MsCodeEdit";
import MsInstructionsIcon from "../../../../common/components/MsInstructionsIcon"; import MsInstructionsIcon from "../../../../common/components/MsInstructionsIcon";
import MsDropdown from "../../../../common/components/MsDropdown"; import MsDropdown from "../../../../common/components/MsDropdown";
import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiBaseComponent from "../common/ApiBaseComponent";
import Jsr233ProcessorContent from "../common/Jsr233ProcessorContent"; import Jsr233ProcessorContent from "../common/Jsr233ProcessorContent";
export default { export default {
name: "MsJsr233Processor", name: "MsJsr233Processor",
components: {Jsr233ProcessorContent, ApiBaseComponent, MsDropdown, MsInstructionsIcon, MsCodeEdit}, components: {Jsr233ProcessorContent, ApiBaseComponent, MsDropdown, MsInstructionsIcon, MsCodeEdit},
props: { props: {
@ -50,6 +51,9 @@
backgroundColor: String, backgroundColor: String,
node: {}, node: {},
}, },
data() {
return {loading: false}
},
methods: { methods: {
remove() { remove() {
this.$emit('remove', this.jsr223Processor, this.node); this.$emit('remove', this.jsr223Processor, this.node);
@ -57,6 +61,16 @@
copyRow() { copyRow() {
this.$emit('copyRow', this.jsr223Processor, this.node); this.$emit('copyRow', this.jsr223Processor, this.node);
}, },
reload() {
this.loading = true
this.$nextTick(() => {
this.loading = false
})
},
active() {
this.jsr223Processor.active = !this.jsr223Processor.active;
this.reload();
},
} }
} }
</script> </script>

View File

@ -79,17 +79,17 @@
<el-input-number size="small" v-model="controller.whileController.timeout" :placeholder="$t('commons.millisecond')" :max="1000*10000000" :min="3000" :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> <span class="ms-span ms-radio">ms</span>
</div> </div>
<p class="tip">{{$t('api_test.definition.request.res_param')}} </p> <!--<p class="tip">{{$t('api_test.definition.request.res_param')}} </p>-->
<div> <!--<div>-->
<el-tabs v-model="activeName" closable class="ms-tabs"> <!--<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"> <!--<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"> <!--<div v-for="(result,i) in item.requestResults" :key="i" style="margin-bottom: 5px">-->
<api-response-component :result="result"/> <!--<api-response-component :result="result"/>-->
</div> <!--</div>-->
</el-tab-pane> <!--</el-tab-pane>-->
</el-tabs> <!--</el-tabs>-->
</div> <!--</div>-->
</api-base-component> </api-base-component>
@ -97,12 +97,12 @@
</template> </template>
<script> <script>
import ApiBaseComponent from "../common/ApiBaseComponent"; import ApiBaseComponent from "../common/ApiBaseComponent";
import ApiResponseComponent from "./ApiResponseComponent"; import ApiResponseComponent from "./ApiResponseComponent";
import MsRun from "../DebugRun"; import MsRun from "../DebugRun";
import {getUUID, getCurrentProjectID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
export default { export default {
name: "MsLoopController", name: "MsLoopController",
components: {ApiBaseComponent, ApiResponseComponent, MsRun}, components: {ApiBaseComponent, ApiResponseComponent, MsRun},
props: { props: {
@ -117,7 +117,7 @@
}, },
}, },
created() { created() {
this.initResult(); // this.initResult();
}, },
data() { data() {
return { return {
@ -202,12 +202,17 @@
this.$warning("当前循环下没有请求,不能执行") this.$warning("当前循环下没有请求,不能执行")
return; return;
} }
this.controller.active = true;
this.loading = true; this.loading = true;
this.debugData = { this.debugData = {
id: this.currentScenario.id, name: this.currentScenario.name, type: "scenario", id: this.currentScenario.id,
variables: this.currentScenario.variables, referenced: 'Created', enableCookieShare: this.enableCookieShare, name: this.currentScenario.name,
environmentId: this.currentEnvironmentId, hashTree: [this.controller] type: "scenario",
variables: this.currentScenario.variables,
headers: this.currentScenario.headers,
referenced: 'Created',
enableCookieShare: this.enableCookieShare,
environmentId: this.currentEnvironmentId,
hashTree: [this.controller]
}; };
this.reportId = getUUID().substring(0, 8); this.reportId = getUUID().substring(0, 8);
}, },
@ -224,7 +229,7 @@
}, },
changeRadio() { changeRadio() {
this.controller.active = true; this.controller.active = true;
this.initResult(); //this.initResult();
this.reload(); this.reload();
}, },
change(value) { change(value) {
@ -258,6 +263,20 @@
this.success = this.requestResult.scenarios && this.requestResult.scenarios != null ? this.requestResult.scenarios.length - this.error : 0; this.success = this.requestResult.scenarios && this.requestResult.scenarios != null ? this.requestResult.scenarios.length - this.error : 0;
} }
}, },
setResult(hashTree) {
if (hashTree) {
hashTree.forEach(item => {
if (item.type === "HTTPSamplerProxy" || item.type === "DubboSampler" || item.type === "JDBCSampler" || item.type === "TCPSampler") {
item.result = this.requestResult;
item.activeName = this.activeName;
item.requestResult = undefined;
}
if (item.hashTree && item.hashTree.length > 0) {
this.setResult(item.hashTree);
}
})
}
},
getReport() { getReport() {
if (this.reportId) { if (this.reportId) {
let url = "/api/scenario/report/get/" + this.reportId; let url = "/api/scenario/report/get/" + this.reportId;
@ -285,11 +304,15 @@
break; break;
} }
this.getFails(); this.getFails();
this.activeName = this.requestResult && this.requestResult.scenarios && this.requestResult.scenarios != null && this.requestResult.scenarios.length > 0 ? this.requestResult.scenarios[0].name : "";
//
this.setResult(this.controller.hashTree);
this.$emit('refReload');
} catch (e) { } catch (e) {
throw e; throw e;
} }
this.loading = false; this.loading = false;
this.activeName = this.requestResult && this.requestResult.scenarios && this.requestResult.scenarios != null && this.requestResult.scenarios.length > 0 ? this.requestResult.scenarios[0].name : ""; this.reload();
} else { } else {
setTimeout(this.getReport, 2000) setTimeout(this.getReport, 2000)
} }
@ -333,11 +356,6 @@
margin: 20px 0; margin: 20px 0;
} }
.ms-tabs >>> .el-icon-close:before {
content: "";
}
.icon.is-active { .icon.is-active {
transform: rotate(90deg); transform: rotate(90deg);
} }

View File

@ -1,9 +1,14 @@
<template> <template>
<el-dialog :title="$t('api_test.scenario.variables')" :close-on-click-modal="false" <el-dialog title="场景变量" :close-on-click-modal="false"
:visible.sync="visible" class="visible-dialog" width="60%" :visible.sync="visible" class="visible-dialog" width="60%"
@close="close" v-loading="loading"> @close="close" v-loading="loading">
<div> <el-tabs v-model="activeName">
<el-input placeholder="变量名称搜索" style="width: 50%;margin: 0px 0px 10px" v-model="selectVariable" size="small" @change="filter" @keyup.enter="filter"> <el-tab-pane :label="$t('api_test.scenario.variables')" name="variable">
<div style="margin-top: 10px">
<el-row style="margin-bottom: 10px">
<el-col :span="8">
<el-input placeholder="变量名称搜索" v-model="selectVariable" size="small" @change="filter"
@keyup.enter="filter">
<el-select v-model="searchType" slot="prepend" placeholder="类型" style="width: 90px" @change="filter"> <el-select v-model="searchType" slot="prepend" placeholder="类型" style="width: 90px" @change="filter">
<el-option value="CONSTANT" label="常量"></el-option> <el-option value="CONSTANT" label="常量"></el-option>
<el-option value="LIST" label="列表"></el-option> <el-option value="LIST" label="列表"></el-option>
@ -12,17 +17,37 @@
<el-option value="RANDOM" label="随机数"></el-option> <el-option value="RANDOM" label="随机数"></el-option>
</el-select> </el-select>
</el-input> </el-input>
</el-col>
<el-col :span="6">
<el-dropdown split-button type="primary" @command="handleClick" @click="handleClick('CONSTANT')"
size="small" style="margin-left: 10px">
{{ $t('commons.add') }}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="CONSTANT">常量</el-dropdown-item>
<el-dropdown-item command="LIST">列表</el-dropdown-item>
<el-dropdown-item command="CSV">CSV</el-dropdown-item>
<el-dropdown-item command="COUNTER">计数器</el-dropdown-item>
<el-dropdown-item command="RANDOM">随机数</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-button size="small" style="margin-left: 10px" @click="deleteVariable">{{ $t('commons.delete') }}
</el-button>
</el-col>
</el-row>
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<div style="border:1px #DCDFE6 solid; min-height: 400px;border-radius: 4px ;width: 100% ;"> <div style="border:1px #DCDFE6 solid; min-height: 400px;border-radius: 4px ;width: 100% ;">
<el-table ref="table" border :data="variables" class="adjust-table" @select-all="select" @select="select" <el-table ref="table" border :data="variables" class="adjust-table" @select-all="select"
@select="select"
v-loading="loading" @row-click="edit" height="400px" :row-class-name="tableRowClassName"> v-loading="loading" @row-click="edit" height="400px" :row-class-name="tableRowClassName">
<el-table-column type="selection" width="38"/> <el-table-column type="selection" width="38"/>
<el-table-column prop="num" label="ID" sortable/> <el-table-column prop="num" label="ID" sortable/>
<el-table-column prop="name" :label="$t('api_test.variable_name')" sortable show-overflow-tooltip/> <el-table-column prop="name" :label="$t('api_test.variable_name')" sortable show-overflow-tooltip/>
<el-table-column prop="type" :label="$t('test_track.case.type')"> <el-table-column prop="type" :label="$t('test_track.case.type')">
<template v-slot:default="scope"> <template v-slot:default="scope">
<span>{{types.get(scope.row.type)}}</span> <span>{{ types.get(scope.row.type) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="value" :label="$t('api_test.value')" show-overflow-tooltip/> <el-table-column prop="value" :label="$t('api_test.value')" show-overflow-tooltip/>
@ -39,36 +64,51 @@
</el-row> </el-row>
</div> </div>
</el-tab-pane>
<el-tab-pane :label="$t('api_test.scenario.headers')" name="headers">
<!-- 请求头-->
<el-tooltip class="item-tabs" effect="dark" :content="$t('api_test.request.headers')" placement="top-start"
slot="label">
<span>{{ $t('api_test.request.headers') }}
<div class="el-step__icon is-text ms-api-col ms-variable-header" v-if="headers.length>1">
<div class="el-step__icon-inner">{{ headers.length - 1 }}</div>
</div>
</span>
</el-tooltip>
<el-row>
<el-link class="ms-variable-link" @click="batchAdd" style="color: #783887"> {{ $t("commons.batch_add") }}
</el-link>
</el-row>
<div style="min-height: 400px">
<ms-api-key-value :items="headers"/>
<batch-add-parameter @batchSave="batchSave" ref="batchAddParameter"/>
</div>
</el-tab-pane>
</el-tabs>
<template v-slot:footer> <template v-slot:footer>
<div> <div>
<el-button style="margin-right:10px" @click="deleteVariable">{{$t('commons.delete')}}</el-button> <el-button type="primary" @click="save">{{ $t('commons.confirm') }}</el-button>
<el-dropdown split-button type="primary" @command="handleClick" @click="handleClick('CONSTANT')" placement="top-end">
{{$t('commons.add')}}
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="CONSTANT">常量</el-dropdown-item>
<el-dropdown-item command="LIST">列表</el-dropdown-item>
<el-dropdown-item command="CSV">CSV</el-dropdown-item>
<el-dropdown-item command="COUNTER">计数器</el-dropdown-item>
<el-dropdown-item command="RANDOM">随机数</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import MsEditConstant from "./EditConstant"; import MsEditConstant from "./EditConstant";
import MsDialogFooter from "../../../../common/components/MsDialogFooter"; import MsDialogFooter from "../../../../common/components/MsDialogFooter";
import MsTableHeader from "@/business/components/common/components/MsTableHeader"; import MsTableHeader from "@/business/components/common/components/MsTableHeader";
import MsTablePagination from "@/business/components/common/pagination/TablePagination"; import MsTablePagination from "@/business/components/common/pagination/TablePagination";
import MsEditCounter from "./EditCounter"; import MsEditCounter from "./EditCounter";
import MsEditRandom from "./EditRandom"; import MsEditRandom from "./EditRandom";
import MsEditListValue from "./EditListValue"; import MsEditListValue from "./EditListValue";
import MsEditCsv from "./EditCsv"; import MsEditCsv from "./EditCsv";
import {getUUID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsApiKeyValue from "../../../definition/components/ApiKeyValue";
import BatchAddParameter from "../../../definition/components/basis/BatchAddParameter";
import {KeyValue} from "../../../definition/model/ApiTestModel";
export default { export default {
name: "MsVariableList", name: "MsVariableList",
components: { components: {
MsEditConstant, MsEditConstant,
@ -78,11 +118,15 @@
MsEditCounter, MsEditCounter,
MsEditRandom, MsEditRandom,
MsEditListValue, MsEditListValue,
MsEditCsv MsEditCsv,
MsApiKeyValue,
BatchAddParameter
}, },
data() { data() {
return { return {
variables: [], variables: [],
headers: [],
activeName: "variable",
searchType: "", searchType: "",
selectVariable: "", selectVariable: "",
condition: {}, condition: {},
@ -103,6 +147,37 @@
} }
}, },
methods: { methods: {
batchAdd() {
this.$refs.batchAddParameter.open();
},
batchSave(data) {
if (data) {
let params = data.split("\n");
let keyValues = [];
params.forEach(item => {
let line = item.split(/|,/);
let required = false;
if (line[1] === '必填' || line[1] === 'true') {
required = true;
}
keyValues.push(new KeyValue({
name: line[0],
required: required,
value: line[2],
description: line[3],
type: "text",
valid: false,
file: false,
encode: true,
enable: true,
contentType: "text/plain"
}));
})
keyValues.forEach(item => {
this.headers.unshift(item);
})
}
},
handleClick(command) { handleClick(command) {
this.editData = {}; this.editData = {};
this.editData.type = command; this.editData.type = command;
@ -135,12 +210,16 @@
isSelect(row) { isSelect(row) {
return this.selection.includes(row.id) return this.selection.includes(row.id)
}, },
open: function (variables) { open: function (variables, headers) {
this.variables = variables; this.variables = variables;
this.headers = headers;
this.visible = true; this.visible = true;
this.editData = {type: "CONSTANT"}; this.editData = {type: "CONSTANT"};
this.addParameters(this.editData); this.addParameters(this.editData);
}, },
save() {
this.visible = false;
},
close() { close() {
this.visible = false; this.visible = false;
let saveVariables = []; let saveVariables = [];
@ -152,7 +231,7 @@
}) })
this.selectVariable = ""; this.selectVariable = "";
this.searchType = ""; this.searchType = "";
this.$emit('setVariables', saveVariables); this.$emit('setVariables', saveVariables, this.headers);
}, },
deleteVariable() { deleteVariable() {
let ids = Array.from(this.selection); let ids = Array.from(this.selection);
@ -205,7 +284,19 @@
</script> </script>
<style> <style>
.ms-variable-hidden-row { .ms-variable-hidden-row {
display: none; display: none;
} }
.ms-variable-header {
background: #783887;
color: white;
height: 18px;
border-radius: 42%;
}
.ms-variable-link {
float: right;
margin-right: 45px;
}
</style> </style>

View File

@ -52,21 +52,21 @@
</template> </template>
<script> <script>
import MsApiAssertionText from "./ApiAssertionText"; import MsApiAssertionText from "./ApiAssertionText";
import MsApiAssertionRegex from "./ApiAssertionRegex"; import MsApiAssertionRegex from "./ApiAssertionRegex";
import MsApiAssertionDuration from "./ApiAssertionDuration"; import MsApiAssertionDuration from "./ApiAssertionDuration";
import {ASSERTION_TYPE, JSONPath} from "../../model/ApiTestModel"; import {ASSERTION_TYPE, JSONPath} from "../../model/ApiTestModel";
import MsApiAssertionsEdit from "./ApiAssertionsEdit"; import MsApiAssertionsEdit from "./ApiAssertionsEdit";
import MsApiAssertionJsonPath from "./ApiAssertionJsonPath"; import MsApiAssertionJsonPath from "./ApiAssertionJsonPath";
import MsApiAssertionJsr223 from "./ApiAssertionJsr223"; import MsApiAssertionJsr223 from "./ApiAssertionJsr223";
import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList"; import MsApiJsonpathSuggestList from "./ApiJsonpathSuggestList";
import MsApiAssertionXPath2 from "./ApiAssertionXPath2"; import MsApiAssertionXPath2 from "./ApiAssertionXPath2";
import {getUUID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import ApiJsonPathSuggestButton from "./ApiJsonPathSuggestButton"; import ApiJsonPathSuggestButton from "./ApiJsonPathSuggestButton";
import MsApiJsonpathSuggest from "./ApiJsonpathSuggest"; import MsApiJsonpathSuggest from "./ApiJsonpathSuggest";
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent"; import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
export default { export default {
name: "MsApiAssertions", name: "MsApiAssertions",
components: { components: {
ApiBaseComponent, ApiBaseComponent,
@ -131,7 +131,7 @@
}) })
}, },
active() { active() {
// item.active = !item.active; this.assertions.active = !this.assertions.active;
this.reload(); this.reload();
}, },
remove() { remove() {

View File

@ -2,15 +2,18 @@
<div class="card-container" v-loading="loading"> <div class="card-container" v-loading="loading">
<el-card class="card-content"> <el-card class="card-content">
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand" <el-button v-if="scenario" style="float: right;margin-right: 20px" size="small" type="primary"
@click="handleCommand"> {{ $t('commons.test') }}
</el-button>
<el-dropdown v-else split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" style="float: right;margin-right: 20px"> @command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}} {{ $t('commons.test') }}
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item> <el-dropdown-item command="save_as">{{ $t('api_test.definition.request.save_as_case') }}</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p> <p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<!-- 请求参数 --> <!-- 请求参数 -->
<ms-basis-parameters :request="request" ref="requestForm"/> <ms-basis-parameters :request="request" ref="requestForm"/>
@ -33,21 +36,30 @@
</template> </template>
<script> <script>
import MsResponseResult from "../response/ResponseResult"; import MsResponseResult from "../response/ResponseResult";
import MsRequestMetric from "../response/RequestMetric"; import MsRequestMetric from "../response/RequestMetric";
import {getUUID, getCurrentUser} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsResponseText from "../response/ResponseText"; import MsResponseText from "../response/ResponseText";
import MsRun from "../Run"; import MsRun from "../Run";
import {createComponent} from "../jmeter/components"; import {createComponent} from "../jmeter/components";
import {REQ_METHOD} from "../../model/JsonData"; import {REQ_METHOD} from "../../model/JsonData";
import MsRequestResultTail from "../response/RequestResultTail"; import MsRequestResultTail from "../response/RequestResultTail";
import MsBasisParameters from "../request/dubbo/BasisParameters"; import MsBasisParameters from "../request/dubbo/BasisParameters";
import MsJmxStep from "../step/JmxStep"; import MsJmxStep from "../step/JmxStep";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
export default { export default {
name: "ApiConfig", name: "ApiConfig",
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters, MsJmxStep, MsApiCaseList}, components: {
MsRequestResultTail,
MsResponseResult,
MsRequestMetric,
MsResponseText,
MsRun,
MsBasisParameters,
MsJmxStep,
MsApiCaseList
},
props: { props: {
currentProtocol: String, currentProtocol: String,
scenario: Boolean, scenario: Boolean,

View File

@ -15,6 +15,8 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button v-if="scenario" size="small" type="primary" @click="handleCommand"> {{ $t('commons.test') }}
</el-button>
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand" <el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" v-if="testCase===undefined && !scenario"> @command="handleCommand" size="small" v-if="testCase===undefined && !scenario">
{{$t('commons.test')}} {{$t('commons.test')}}
@ -47,22 +49,31 @@
</template> </template>
<script> <script>
import MsApiRequestForm from "../request/http/ApiHttpRequestForm"; import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import MsResponseResult from "../response/ResponseResult"; import MsResponseResult from "../response/ResponseResult";
import MsRequestMetric from "../response/RequestMetric"; import MsRequestMetric from "../response/RequestMetric";
import {getUUID, getCurrentUser} from "@/common/js/utils"; import {getCurrentUser, getUUID} from "@/common/js/utils";
import MsResponseText from "../response/ResponseText"; import MsResponseText from "../response/ResponseText";
import MsRun from "../Run"; import MsRun from "../Run";
import {createComponent} from "../jmeter/components"; import {createComponent} from "../jmeter/components";
import {REQ_METHOD} from "../../model/JsonData"; import {REQ_METHOD} from "../../model/JsonData";
import MsRequestResultTail from "../response/RequestResultTail"; import MsRequestResultTail from "../response/RequestResultTail";
import MsJmxStep from "../step/JmxStep"; import MsJmxStep from "../step/JmxStep";
import {KeyValue} from "../../model/ApiTestModel"; import {KeyValue} from "../../model/ApiTestModel";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
export default { export default {
name: "ApiConfig", name: "ApiConfig",
components: {MsRequestResultTail, MsResponseResult, MsApiRequestForm, MsRequestMetric, MsResponseText, MsRun, MsJmxStep, MsApiCaseList}, components: {
MsRequestResultTail,
MsResponseResult,
MsApiRequestForm,
MsRequestMetric,
MsResponseText,
MsRun,
MsJmxStep,
MsApiCaseList
},
props: { props: {
currentProtocol: String, currentProtocol: String,
testCase: {}, testCase: {},
@ -198,7 +209,7 @@
}, },
urlChange() { urlChange() {
if (!this.debugForm.url) return; if (!this.debugForm.url) return;
let url = this.getURL(this.debugForm.url); let url = this.getURL(this.addProtocol(this.debugForm.url));
if (url && url.pathname) { if (url && url.pathname) {
if (this.debugForm.url.indexOf('?') != -1) { if (this.debugForm.url.indexOf('?') != -1) {
this.debugForm.url = decodeURIComponent(this.debugForm.url.substr(0, this.debugForm.url.indexOf("?"))); this.debugForm.url = decodeURIComponent(this.debugForm.url.substr(0, this.debugForm.url.indexOf("?")));
@ -209,7 +220,14 @@
} }
}, },
addProtocol(url) {
if (url) {
if (!url.toLowerCase().startsWith("https") && !url.toLowerCase().startsWith("http")) {
return "https://" + url;
}
}
return url;
},
getURL(urlStr) { getURL(urlStr) {
try { try {
let url = new URL(urlStr); let url = new URL(urlStr);

View File

@ -2,15 +2,19 @@
<div class="card-container" v-loading="loading"> <div class="card-container" v-loading="loading">
<el-card class="card-content"> <el-card class="card-content">
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand" <el-button v-if="scenario" style="float: right;margin-right: 20px" size="small" type="primary"
@click="handleCommand"> {{ $t('commons.test') }}
</el-button>
<el-dropdown v-else split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" style="float: right;margin-right: 20px"> @command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}} {{ $t('commons.test') }}
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item> <el-dropdown-item command="save_as">{{ $t('api_test.definition.request.save_as_case') }}</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<p class="tip">{{$t('api_test.definition.request.req_param')}} </p> <p class="tip">{{ $t('api_test.definition.request.req_param') }} </p>
<!-- JDBC 请求参数 --> <!-- JDBC 请求参数 -->
<ms-basis-parameters :request="request" @callback="runDebug" ref="requestForm"/> <ms-basis-parameters :request="request" @callback="runDebug" ref="requestForm"/>
@ -35,21 +39,30 @@
</template> </template>
<script> <script>
import MsResponseResult from "../response/ResponseResult"; import MsResponseResult from "../response/ResponseResult";
import MsRequestMetric from "../response/RequestMetric"; import MsRequestMetric from "../response/RequestMetric";
import {getUUID, getCurrentUser} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsResponseText from "../response/ResponseText"; import MsResponseText from "../response/ResponseText";
import MsRun from "../Run"; import MsRun from "../Run";
import {createComponent} from "../jmeter/components"; import {createComponent} from "../jmeter/components";
import {REQ_METHOD} from "../../model/JsonData"; import {REQ_METHOD} from "../../model/JsonData";
import MsRequestResultTail from "../response/RequestResultTail"; import MsRequestResultTail from "../response/RequestResultTail";
import MsBasisParameters from "../request/database/BasisParameters"; import MsBasisParameters from "../request/database/BasisParameters";
import MsJmxStep from "../step/JmxStep"; import MsJmxStep from "../step/JmxStep";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
export default { export default {
name: "ApiConfig", name: "ApiConfig",
components: {MsRequestResultTail, MsResponseResult, MsRequestMetric, MsResponseText, MsRun, MsBasisParameters, MsJmxStep,MsApiCaseList}, components: {
MsRequestResultTail,
MsResponseResult,
MsRequestMetric,
MsResponseText,
MsRun,
MsBasisParameters,
MsJmxStep,
MsApiCaseList
},
props: { props: {
currentProtocol: String, currentProtocol: String,
scenario: Boolean, scenario: Boolean,

View File

@ -11,11 +11,15 @@
<el-input-number v-model="request.port" controls-position="right" :min="0" :max="65535" size="small"/> <el-input-number v-model="request.port" controls-position="right" :min="0" :max="65535" size="small"/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-dropdown split-button type="primary" class="ms-api-buttion" @click="handleCommand" <el-button v-if="scenario" size="small" type="primary" @click="handleCommand"> {{ $t('commons.test') }}
</el-button>
<el-dropdown v-else split-button type="primary" class="ms-api-buttion" @click="handleCommand"
@command="handleCommand" size="small" style="float: right;margin-right: 20px"> @command="handleCommand" size="small" style="float: right;margin-right: 20px">
{{$t('commons.test')}} {{ $t('commons.test') }}
<el-dropdown-menu slot="dropdown"> <el-dropdown-menu slot="dropdown">
<el-dropdown-item command="save_as">{{$t('api_test.definition.request.save_as_case')}}</el-dropdown-item> <el-dropdown-item command="save_as">{{ $t('api_test.definition.request.save_as_case') }}
</el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
</el-form-item> </el-form-item>
@ -45,20 +49,20 @@
</template> </template>
<script> <script>
import MsApiRequestForm from "../request/http/ApiHttpRequestForm"; import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import MsResponseResult from "../response/ResponseResult"; import MsResponseResult from "../response/ResponseResult";
import MsRequestMetric from "../response/RequestMetric"; import MsRequestMetric from "../response/RequestMetric";
import {getUUID, getCurrentUser} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsResponseText from "../response/ResponseText"; import MsResponseText from "../response/ResponseText";
import MsRun from "../Run"; import MsRun from "../Run";
import {createComponent} from "../jmeter/components"; import {createComponent} from "../jmeter/components";
import {REQ_METHOD} from "../../model/JsonData"; import {REQ_METHOD} from "../../model/JsonData";
import MsRequestResultTail from "../response/RequestResultTail"; import MsRequestResultTail from "../response/RequestResultTail";
import TcpBasisParameters from "../request/tcp/TcpBasisParameters"; import TcpBasisParameters from "../request/tcp/TcpBasisParameters";
import MsJmxStep from "../step/JmxStep"; import MsJmxStep from "../step/JmxStep";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
export default { export default {
name: "ApiConfig", name: "ApiConfig",
components: { components: {
MsJmxStep, MsJmxStep,

View File

@ -2,19 +2,21 @@
<api-base-component <api-base-component
@copy="copyRow" @copy="copyRow"
@remove="remove" @remove="remove"
@active="active"
:data="extract" :data="extract"
:draggable="draggable" :draggable="draggable"
color="#015478" color="#015478"
background-color="#E6EEF2" background-color="#E6EEF2"
:title="$t('api_test.definition.request.extract_param')"> :title="$t('api_test.definition.request.extract_param')">
<div style="margin: 20px"> <div style="margin: 20px" v-loading="loading">
<div class="extract-description"> <div class="extract-description">
{{$t('api_test.request.extract.description')}} {{ $t('api_test.request.extract.description') }}
</div> </div>
<div class="extract-add"> <div class="extract-add">
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :span="2"> <el-col :span="2">
<el-select :disabled="isReadOnly" class="extract-item" v-model="type" :placeholder="$t('api_test.request.extract.select_type')" <el-select :disabled="isReadOnly" class="extract-item" v-model="type"
:placeholder="$t('api_test.request.extract.select_type')"
size="small"> size="small">
<el-option :label="$t('api_test.request.extract.regex')" :value="options.REGEX"/> <el-option :label="$t('api_test.request.extract.regex')" :value="options.REGEX"/>
<el-option label="JSONPath" :value="options.JSON_PATH"/> <el-option label="JSONPath" :value="options.JSON_PATH"/>
@ -25,7 +27,7 @@
<ms-api-extract-common :is-read-only="isReadOnly" :extract-type="type" :list="list" v-if="type" :callback="after"/> <ms-api-extract-common :is-read-only="isReadOnly" :extract-type="type" :list="list" v-if="type" :callback="after"/>
</el-col> </el-col>
<el-button v-if="!type" :disabled="true" type="primary" size="small">Add</el-button> <el-button v-if="!type" :disabled="true" type="primary" size="small">{{ $t('commons.add') }}</el-button>
</el-row> </el-row>
</div> </div>
@ -39,16 +41,16 @@
</template> </template>
<script> <script>
import {EXTRACT_TYPE} from "../../model/ApiTestModel"; import {EXTRACT_TYPE} from "../../model/ApiTestModel";
import MsApiExtractEdit from "./ApiExtractEdit"; import MsApiExtractEdit from "./ApiExtractEdit";
import MsApiExtractCommon from "./ApiExtractCommon"; import MsApiExtractCommon from "./ApiExtractCommon";
import {getUUID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton"; import ApiJsonPathSuggestButton from "../assertion/ApiJsonPathSuggestButton";
import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest"; import MsApiJsonpathSuggest from "../assertion/ApiJsonpathSuggest";
import {ExtractJSONPath} from "../../../test/model/ScenarioModel"; import {ExtractJSONPath} from "../../../test/model/ScenarioModel";
import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent"; import ApiBaseComponent from "../../../automation/scenario/common/ApiBaseComponent";
export default { export default {
name: "MsApiExtract", name: "MsApiExtract",
components: { components: {
ApiBaseComponent, ApiBaseComponent,
@ -99,8 +101,8 @@
this.loading = false this.loading = false
}) })
}, },
active(item) { active() {
item.active = !item.active; this.extract.active = !this.extract.active;
this.reload(); this.reload();
}, },
suggestJsonOpen() { suggestJsonOpen() {
@ -157,6 +159,10 @@
border-radius: 5px; border-radius: 5px;
} }
.icon.is-active {
transform: rotate(90deg);
}
/deep/ .el-card__body { /deep/ .el-card__body {
padding: 15px; padding: 15px;
} }

View File

@ -44,7 +44,7 @@
</template> </template>
<script> <script>
import {getUUID, uuid} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer"; import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer"; import MsBottomContainer from "../BottomContainer";
@ -89,7 +89,7 @@ export default {
reportId: "", reportId: "",
} }
}, },
props: {apiData: {}, currentProtocol: String,syncTabs: Array, projectId: String}, props: {apiData: {}, currentProtocol: String, syncTabs: Array, projectId: String},
methods: { methods: {
handleCommand(e) { handleCommand(e) {
switch (e) { switch (e) {
@ -105,7 +105,7 @@ export default {
return this.runTest(); return this.runTest();
} }
}, },
refresh(){ refresh() {
this.$emit('refresh'); this.$emit('refresh');
}, },
runTest() { runTest() {
@ -161,8 +161,9 @@ export default {
}, },
saveAsApi() { saveAsApi() {
let data = {}; let data = {};
this.api.request.id = uuid(); let req = this.api.request;
data.request = JSON.stringify(this.api.request); req.id = getUUID();
data.request = JSON.stringify(req);
data.method = this.api.method; data.method = this.api.method;
data.status = this.api.status; data.status = this.api.status;
data.userId = this.api.userId; data.userId = this.api.userId;

View File

@ -71,17 +71,17 @@
</template> </template>
<script> <script>
import MsApiRequestForm from "../request/http/ApiHttpRequestForm"; import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import {downloadFile, getUUID, getCurrentProjectID} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer"; import MsContainer from "../../../../common/components/MsContainer";
import MsRequestResultTail from "../response/RequestResultTail"; import MsRequestResultTail from "../response/RequestResultTail";
import MsRun from "../Run"; import MsRun from "../Run";
import {REQ_METHOD} from "../../model/JsonData"; import {REQ_METHOD} from "../../model/JsonData";
import EnvironmentSelect from "../environment/EnvironmentSelect"; import EnvironmentSelect from "../environment/EnvironmentSelect";
import MsJmxStep from "../step/JmxStep"; import MsJmxStep from "../step/JmxStep";
export default { export default {
name: "RunTestHTTPPage", name: "RunTestHTTPPage",
components: { components: {
EnvironmentSelect, EnvironmentSelect,
@ -186,11 +186,11 @@
}, },
saveAsApi() { saveAsApi() {
let data = {}; let data = {};
data.request = JSON.stringify(this.api.request); let req = this.api.request;
req.id = getUUID();
data.request = JSON.stringify(req);
data.method = this.api.method; data.method = this.api.method;
data.url = this.api.url; data.url = this.api.url;
let id = getUUID();
data.id = id;
data.status = this.api.status; data.status = this.api.status;
data.userId = this.api.userId; data.userId = this.api.userId;
data.description = this.api.description; data.description = this.api.description;

View File

@ -44,7 +44,7 @@
</template> </template>
<script> <script>
import {getUUID, uuid} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer"; import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer"; import MsBottomContainer from "../BottomContainer";
@ -160,8 +160,9 @@ export default {
}, },
saveAsApi() { saveAsApi() {
let data = {}; let data = {};
this.api.request.id = uuid(); let req = this.api.request;
data.request = JSON.stringify(this.api.request); req.id = getUUID();
data.request = JSON.stringify(req);
data.method = this.api.method; data.method = this.api.method;
data.status = this.api.status; data.status = this.api.status;
data.userId = this.api.userId; data.userId = this.api.userId;

View File

@ -55,7 +55,7 @@
<script> <script>
import MsApiRequestForm from "../request/http/ApiHttpRequestForm"; import MsApiRequestForm from "../request/http/ApiHttpRequestForm";
import {getUUID, uuid} from "@/common/js/utils"; import {getUUID} from "@/common/js/utils";
import MsApiCaseList from "../case/ApiCaseList"; import MsApiCaseList from "../case/ApiCaseList";
import MsContainer from "../../../../common/components/MsContainer"; import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer"; import MsBottomContainer from "../BottomContainer";
@ -98,7 +98,7 @@ export default {
reportId: "", reportId: "",
} }
}, },
props: {apiData: {}, currentProtocol: String,syncTabs: Array, projectId: String}, props: {apiData: {}, currentProtocol: String, syncTabs: Array, projectId: String},
methods: { methods: {
handleCommand(e) { handleCommand(e) {
switch (e) { switch (e) {
@ -114,7 +114,7 @@ export default {
return this.$refs['requestForm'].validate(); return this.$refs['requestForm'].validate();
} }
}, },
refresh(){ refresh() {
this.$emit('refresh'); this.$emit('refresh');
}, },
runTest() { runTest() {
@ -173,8 +173,9 @@ export default {
}, },
saveAsApi() { saveAsApi() {
let data = {}; let data = {};
this.api.request.id = uuid(); let req = this.api.request;
data.request = JSON.stringify(this.api.request); req.id = getUUID();
data.request = JSON.stringify(req);
data.method = this.api.method; data.method = this.api.method;
data.status = this.api.status; data.status = this.api.status;
data.userId = this.api.userId; data.userId = this.api.userId;

View File

@ -148,7 +148,9 @@ export default {
}, },
tooltip: { tooltip: {
show: true, show: true,
trigger: 'axis' trigger: 'axis',
// extraCssText: 'z-index: 999;',
confine: true,
}, },
legend: {}, legend: {},
xAxis: {}, xAxis: {},
@ -226,7 +228,8 @@ export default {
tooltip: { tooltip: {
show: true, show: true,
trigger: 'axis', trigger: 'axis',
extraCssText: 'z-index: 999;', // extraCssText: 'z-index: 999;',
confine: true,
formatter: function (params, ticket, callback) { formatter: function (params, ticket, callback) {
let result = ""; let result = "";
let name = params[0].name; let name = params[0].name;
@ -306,7 +309,8 @@ export default {
tooltip: { tooltip: {
show: true, show: true,
trigger: 'axis', trigger: 'axis',
extraCssText: 'z-index: 999;', // extraCssText: 'z-index: 999;',
confine: true,
formatter: function (params, ticket, callback) { formatter: function (params, ticket, callback) {
let result = ""; let result = "";
let name = params[0].name; let name = params[0].name;
@ -376,7 +380,8 @@ export default {
tooltip: { tooltip: {
show: true, show: true,
trigger: 'axis', trigger: 'axis',
extraCssText: 'z-index: 999;', // extraCssText: 'z-index: 999;',
confine: true,
formatter: function (params, ticket, callback) { formatter: function (params, ticket, callback) {
let result = ""; let result = "";
let name = params[0].name; let name = params[0].name;