Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
5fe7786d2e
|
@ -10,4 +10,5 @@ public class ExtractCommon extends ExtractType {
|
||||||
private String value; // value: ${variable}
|
private String value; // value: ${variable}
|
||||||
private String expression;
|
private String expression;
|
||||||
private String description;
|
private String description;
|
||||||
|
private Boolean multipleMatching;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,5 +11,5 @@ public interface ExtTestReviewCaseMapper {
|
||||||
|
|
||||||
List<TestReviewCaseDTO> list(@Param("request") QueryCaseReviewRequest request);
|
List<TestReviewCaseDTO> list(@Param("request") QueryCaseReviewRequest request);
|
||||||
List<String> getStatusByReviewId(String reviewId);
|
List<String> getStatusByReviewId(String reviewId);
|
||||||
List<String> findRelateTestReviewId(String userId, String workspaceId);
|
List<String> findRelateTestReviewId(@Param("userId") String userId, @Param("workspaceId") String workspaceId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,7 +167,7 @@ public class TestCaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/file/download")
|
@PostMapping("/file/download")
|
||||||
public ResponseEntity<byte[]> downloadJmx(@RequestBody FileOperationRequest fileOperationRequest) {
|
public ResponseEntity<byte[]> download(@RequestBody FileOperationRequest fileOperationRequest) {
|
||||||
byte[] bytes = fileService.loadFileAsBytes(fileOperationRequest.getId());
|
byte[] bytes = fileService.loadFileAsBytes(fileOperationRequest.getId());
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||||
|
@ -175,4 +175,13 @@ public class TestCaseController {
|
||||||
.body(bytes);
|
.body(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/file/preview/{fileId}")
|
||||||
|
public ResponseEntity<byte[]> preview(@PathVariable String fileId) {
|
||||||
|
byte[] bytes = fileService.loadFileAsBytes(fileId);
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.contentType(MediaType.parseMediaType("application/octet-stream"))
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileId + "\"")
|
||||||
|
.body(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,25 +263,22 @@ public class TestCaseService {
|
||||||
.map(TestCase::getName)
|
.map(TestCase::getName)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
List<ExcelErrData<TestCaseExcelData>> errList = null;
|
List<ExcelErrData<TestCaseExcelData>> errList = null;
|
||||||
if (multipartFile == null)
|
if (multipartFile == null) {
|
||||||
MSException.throwException(Translator.get("upload_fail"));
|
MSException.throwException(Translator.get("upload_fail"));
|
||||||
|
}
|
||||||
if (multipartFile.getOriginalFilename().endsWith(".xmind")) {
|
if (multipartFile.getOriginalFilename().endsWith(".xmind")) {
|
||||||
try {
|
try {
|
||||||
errList = new ArrayList<>();
|
|
||||||
XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames);
|
XmindCaseParser xmindParser = new XmindCaseParser(this, userId, projectId, testCaseNames);
|
||||||
String processLog = xmindParser.parse(multipartFile);
|
errList = xmindParser.parse(multipartFile);
|
||||||
if (!StringUtils.isEmpty(processLog)) {
|
if (xmindParser.getNodePaths().isEmpty() && xmindParser.getTestCase().isEmpty()) {
|
||||||
excelResponse.setSuccess(false);
|
if (errList == null) {
|
||||||
ExcelErrData excelErrData = new ExcelErrData(null, 1, Translator.get("upload_fail") + ":" + processLog);
|
errList = new ArrayList<>();
|
||||||
errList.add(excelErrData);
|
}
|
||||||
excelResponse.setErrList(errList);
|
|
||||||
} else if (xmindParser.getNodePaths().isEmpty() && xmindParser.getTestCase().isEmpty()) {
|
|
||||||
excelResponse.setSuccess(false);
|
|
||||||
ExcelErrData excelErrData = new ExcelErrData(null, 1, Translator.get("upload_fail") + ":" + Translator.get("upload_content_is_null"));
|
ExcelErrData excelErrData = new ExcelErrData(null, 1, Translator.get("upload_fail") + ":" + Translator.get("upload_content_is_null"));
|
||||||
errList.add(excelErrData);
|
errList.add(excelErrData);
|
||||||
excelResponse.setErrList(errList);
|
excelResponse.setErrList(errList);
|
||||||
} else {
|
}
|
||||||
|
if (errList.isEmpty()) {
|
||||||
if (!xmindParser.getNodePaths().isEmpty()) {
|
if (!xmindParser.getNodePaths().isEmpty()) {
|
||||||
testCaseNodeService.createNodes(xmindParser.getNodePaths(), projectId);
|
testCaseNodeService.createNodes(xmindParser.getNodePaths(), projectId);
|
||||||
}
|
}
|
||||||
|
@ -290,7 +287,6 @@ public class TestCaseService {
|
||||||
this.saveImportData(xmindParser.getTestCase(), projectId);
|
this.saveImportData(xmindParser.getTestCase(), projectId);
|
||||||
xmindParser.clear();
|
xmindParser.clear();
|
||||||
}
|
}
|
||||||
excelResponse.setSuccess(true);
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogUtil.error(e.getMessage(), e);
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
|
|
@ -6,12 +6,14 @@ import com.google.common.collect.ImmutableMap;
|
||||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||||
import io.metersphere.commons.constants.TestCaseConstants;
|
import io.metersphere.commons.constants.TestCaseConstants;
|
||||||
import io.metersphere.commons.utils.BeanUtils;
|
import io.metersphere.commons.utils.BeanUtils;
|
||||||
|
import io.metersphere.excel.domain.ExcelErrData;
|
||||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||||
import io.metersphere.i18n.Translator;
|
import io.metersphere.i18n.Translator;
|
||||||
import io.metersphere.track.service.TestCaseService;
|
import io.metersphere.track.service.TestCaseService;
|
||||||
import io.metersphere.xmind.parser.XmindParser;
|
import io.metersphere.xmind.parser.XmindParser;
|
||||||
import io.metersphere.xmind.parser.pojo.Attached;
|
import io.metersphere.xmind.parser.pojo.Attached;
|
||||||
import io.metersphere.xmind.parser.pojo.JsonRootBean;
|
import io.metersphere.xmind.parser.pojo.JsonRootBean;
|
||||||
|
import io.metersphere.xmind.utils.DetailUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
@ -32,7 +34,7 @@ public class XmindCaseParser {
|
||||||
/**
|
/**
|
||||||
* 过程校验记录
|
* 过程校验记录
|
||||||
*/
|
*/
|
||||||
private StringBuffer process;
|
private DetailUtil process;
|
||||||
/**
|
/**
|
||||||
* 已存在用例名称
|
* 已存在用例名称
|
||||||
*/
|
*/
|
||||||
|
@ -57,7 +59,7 @@ public class XmindCaseParser {
|
||||||
this.testCaseNames = testCaseNames;
|
this.testCaseNames = testCaseNames;
|
||||||
testCases = new LinkedList<>();
|
testCases = new LinkedList<>();
|
||||||
compartDatas = new ArrayList<>();
|
compartDatas = new ArrayList<>();
|
||||||
process = new StringBuffer();
|
process = new DetailUtil();
|
||||||
nodePaths = new ArrayList<>();
|
nodePaths = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,15 +92,15 @@ public class XmindCaseParser {
|
||||||
nodePaths.forEach(nodePath -> {
|
nodePaths.forEach(nodePath -> {
|
||||||
String[] nodes = nodePath.split("/");
|
String[] nodes = nodePath.split("/");
|
||||||
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||||
process.append(Translator.get("test_case_node_level_tip") +
|
process.add(Translator.get("test_case_node_level_tip") +
|
||||||
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; ");
|
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level"), nodePath);
|
||||||
}
|
}
|
||||||
String path = "";
|
String path = "";
|
||||||
for (int i = 0; i < nodes.length; i++) {
|
for (int i = 0; i < nodes.length; i++) {
|
||||||
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
||||||
process.append(Translator.get("module") + ":【" + path + "】" + Translator.get("module_not_null") + "; ");
|
process.add(Translator.get("module_not_null"), path);
|
||||||
} else if (nodes[i].trim().length() > 30) {
|
} else if (nodes[i].trim().length() > 30) {
|
||||||
process.append(nodes[i].trim() + ":" + Translator.get("test_track.length_less_than") + "30 ;");
|
process.add(Translator.get("module") + Translator.get("test_track.length_less_than") + "30", path + nodes[i].trim());
|
||||||
} else {
|
} else {
|
||||||
path += nodes[i].trim() + "/";
|
path += nodes[i].trim() + "/";
|
||||||
}
|
}
|
||||||
|
@ -109,7 +111,7 @@ public class XmindCaseParser {
|
||||||
/**
|
/**
|
||||||
* 验证用例的合规性
|
* 验证用例的合规性
|
||||||
*/
|
*/
|
||||||
private boolean validate(TestCaseWithBLOBs data) {
|
private void validate(TestCaseWithBLOBs data) {
|
||||||
String nodePath = data.getNodePath();
|
String nodePath = data.getNodePath();
|
||||||
if (!nodePath.startsWith("/")) {
|
if (!nodePath.startsWith("/")) {
|
||||||
nodePath = "/" + nodePath;
|
nodePath = "/" + nodePath;
|
||||||
|
@ -119,37 +121,36 @@ public class XmindCaseParser {
|
||||||
}
|
}
|
||||||
data.setNodePath(nodePath);
|
data.setNodePath(nodePath);
|
||||||
|
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
if (data.getName().length() > 50) {
|
if (data.getName().length() > 50) {
|
||||||
stringBuilder.append(data.getName() + ":" + Translator.get("test_case") + Translator.get("test_track.length_less_than") + "50 ;");
|
process.add(Translator.get("test_case") + Translator.get("test_track.length_less_than") + "50", nodePath + data.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(nodePath)) {
|
if (!StringUtils.isEmpty(nodePath)) {
|
||||||
String[] nodes = nodePath.split("/");
|
String[] nodes = nodePath.split("/");
|
||||||
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||||
stringBuilder.append(Translator.get("test_case_node_level_tip") +
|
process.add(Translator.get("test_case_node_level_tip") +
|
||||||
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; ");
|
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level"), nodePath);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < nodes.length; i++) {
|
for (int i = 0; i < nodes.length; i++) {
|
||||||
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
if (i != 0 && StringUtils.equals(nodes[i].trim(), "")) {
|
||||||
stringBuilder.append(Translator.get("test_case") + ":【" + data.getName() + "】" + Translator.get("module_not_null") + "; ");
|
process.add(Translator.get("test_case") + Translator.get("module_not_null"), nodePath + data.getName());
|
||||||
break;
|
break;
|
||||||
} else if (nodes[i].trim().length() > 30) {
|
} else if (nodes[i].trim().length() > 30) {
|
||||||
stringBuilder.append(nodes[i].trim() + ":" + Translator.get("module") + Translator.get("test_track.length_less_than") + "30 ;");
|
process.add(Translator.get("module") + Translator.get("test_track.length_less_than") + "30 ", nodes[i].trim());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
|
if (StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
|
||||||
stringBuilder.append(Translator.get("functional_method_tip") + "; ");
|
process.add(Translator.get("functional_method_tip"), nodePath + data.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testCaseNames.contains(data.getName())) {
|
if (testCaseNames.contains(data.getName())) {
|
||||||
boolean dbExist = testCaseService.exist(data);
|
boolean dbExist = testCaseService.exist(data);
|
||||||
if (dbExist) {
|
if (dbExist) {
|
||||||
stringBuilder.append(Translator.get("test_case_already_exists_excel") + ":" + data.getName() + "; ");
|
process.add(Translator.get("test_case_already_exists_excel"), nodePath + "/" + data.getName());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
testCaseNames.add(data.getName());
|
testCaseNames.add(data.getName());
|
||||||
|
@ -157,26 +158,19 @@ public class XmindCaseParser {
|
||||||
|
|
||||||
// 用例等级和用例性质处理
|
// 用例等级和用例性质处理
|
||||||
if (!priorityList.contains(data.getPriority())) {
|
if (!priorityList.contains(data.getPriority())) {
|
||||||
stringBuilder.append(data.getName() + ":" + Translator.get("test_case_priority") + Translator.get("incorrect_format") + "; ");
|
process.add(Translator.get("test_case_priority") + Translator.get("incorrect_format"), nodePath + data.getName());
|
||||||
}
|
}
|
||||||
if (data.getType() == null) {
|
if (data.getType() == null) {
|
||||||
stringBuilder.append(data.getName() + ":" + Translator.get("test_case_type") + Translator.get("incorrect_format") + "; ");
|
process.add(Translator.get("test_case_type") + Translator.get("incorrect_format"), nodePath + data.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 重复用例校验
|
// 重复用例校验
|
||||||
TestCaseExcelData compartData = new TestCaseExcelData();
|
TestCaseExcelData compartData = new TestCaseExcelData();
|
||||||
BeanUtils.copyBean(compartData, data);
|
BeanUtils.copyBean(compartData, data);
|
||||||
if (compartDatas.contains(compartData)) {
|
if (compartDatas.contains(compartData)) {
|
||||||
stringBuilder.append(Translator.get("test_case_already_exists_excel") + ":" + compartData.getName() + "; ");
|
process.add(Translator.get("test_case_already_exists_excel"), nodePath + "/" + compartData.getName());
|
||||||
}
|
|
||||||
if (!StringUtils.isEmpty(stringBuilder.toString())) {
|
|
||||||
process.append(stringBuilder.toString());
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
compartDatas.add(compartData);
|
compartDatas.add(compartData);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -267,7 +261,7 @@ 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 != 2) {
|
||||||
process.append(Translator.get("test_case_name") + "【 " + title + " 】" + Translator.get("incorrect_format"));
|
process.add(Translator.get("test_case_name") + Translator.get("incorrect_format"), title);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 用例名称
|
// 用例名称
|
||||||
|
@ -301,17 +295,15 @@ public class XmindCaseParser {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
testCase.setSteps(this.getSteps(steps));
|
testCase.setSteps(this.getSteps(steps));
|
||||||
|
testCases.add(testCase);
|
||||||
// 校验合规性
|
// 校验合规性
|
||||||
if (validate(testCase)) {
|
validate(testCase);
|
||||||
testCases.add(testCase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入思维导图处理
|
* 导入思维导图处理
|
||||||
*/
|
*/
|
||||||
public String parse(MultipartFile multipartFile) {
|
public List<ExcelErrData<TestCaseExcelData>> parse(MultipartFile multipartFile) {
|
||||||
try {
|
try {
|
||||||
// 获取思维导图内容
|
// 获取思维导图内容
|
||||||
List<JsonRootBean> roots = XmindParser.parseObject(multipartFile);
|
List<JsonRootBean> roots = XmindParser.parseObject(multipartFile);
|
||||||
|
@ -321,7 +313,7 @@ public class XmindCaseParser {
|
||||||
for (Attached item : root.getRootTopic().getChildren().getAttached()) {
|
for (Attached item : root.getRootTopic().getChildren().getAttached()) {
|
||||||
// 用例
|
// 用例
|
||||||
if (isAvailable(item.getTitle(), TC_REGEX)) {
|
if (isAvailable(item.getTitle(), TC_REGEX)) {
|
||||||
return replace(item.getTitle(), TC_REGEX) + ":" + Translator.get("test_case_create_module_fail");
|
return process.parse(replace(item.getTitle(), TC_REGEX) + ":" + Translator.get("test_case_create_module_fail"));
|
||||||
} else {
|
} else {
|
||||||
String nodePath = item.getTitle();
|
String nodePath = item.getTitle();
|
||||||
item.setPath(nodePath);
|
item.setPath(nodePath);
|
||||||
|
@ -344,8 +336,8 @@ public class XmindCaseParser {
|
||||||
//检查目录合规性
|
//检查目录合规性
|
||||||
this.validate();
|
this.validate();
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
return ex.getMessage();
|
return process.parse(ex.getMessage());
|
||||||
}
|
}
|
||||||
return process.toString();
|
return process.parse();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class XmindParser {
|
||||||
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
||||||
jsonRootBeans.add(jsonRootBean);
|
jsonRootBeans.add(jsonRootBean);
|
||||||
}
|
}
|
||||||
if (caseCount > 500) {
|
if (caseCount > 800) {
|
||||||
MSException.throwException(Translator.get("import_xmind_count_error"));
|
MSException.throwException(Translator.get("import_xmind_count_error"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package io.metersphere.xmind.utils;
|
||||||
|
|
||||||
|
import io.metersphere.excel.domain.ExcelErrData;
|
||||||
|
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public class DetailUtil implements Serializable {
|
||||||
|
|
||||||
|
private Map<String, StringBuilder> process;
|
||||||
|
|
||||||
|
public DetailUtil() {
|
||||||
|
process = new LinkedHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(String type, String msContent) {
|
||||||
|
if (process.containsKey(type)) {
|
||||||
|
process.get(type).append(msContent + ";");
|
||||||
|
} else {
|
||||||
|
process.put(type, new StringBuilder(msContent + ";"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ExcelErrData<TestCaseExcelData>> parse(String content) {
|
||||||
|
List<ExcelErrData<TestCaseExcelData>> errList = new ArrayList<>();
|
||||||
|
ExcelErrData excelErrData = new ExcelErrData(null, 1, content);
|
||||||
|
errList.add(excelErrData);
|
||||||
|
return errList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ExcelErrData<TestCaseExcelData>> parse() {
|
||||||
|
List<ExcelErrData<TestCaseExcelData>> errList = new ArrayList<>();
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
process.entrySet().parallelStream().reduce(result, (first, second) -> {
|
||||||
|
first.add(second.getKey() + ":" + second.getValue());
|
||||||
|
return first;
|
||||||
|
}, (first, second) -> {
|
||||||
|
if (first == second) {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
first.addAll(second);
|
||||||
|
return first;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (int i = 0; i < result.size(); i++) {
|
||||||
|
ExcelErrData excelErrData = new ExcelErrData(null, i, result.get(i));
|
||||||
|
errList.add(excelErrData);
|
||||||
|
}
|
||||||
|
return errList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
Subproject commit cf6b06526324326a563d933e07118fac014a63b4
|
Subproject commit ee74568be0beba46da19616f5832e83f9164c688
|
|
@ -35,7 +35,8 @@
|
||||||
"jspdf": "^2.1.1",
|
"jspdf": "^2.1.1",
|
||||||
"yan-progress": "^1.0.3",
|
"yan-progress": "^1.0.3",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"el-table-infinite-scroll": "^1.0.10"
|
"el-table-infinite-scroll": "^1.0.10",
|
||||||
|
"vue-pdf": "^4.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "^4.1.0",
|
"@vue/cli-plugin-babel": "^4.1.0",
|
||||||
|
|
|
@ -29,6 +29,10 @@
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
|
showCopyTipWithMultiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -63,7 +67,7 @@
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
variable() {
|
variable() {
|
||||||
return "${" + this.value + "}";
|
return "${" + (this.showCopyTipWithMultiple ? (this.value + "_n") : this.value) + "}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,17 @@
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
<ms-api-variable-input :is-read-only="isReadOnly" v-model="common.variable" size="small" maxlength="60"
|
<ms-api-variable-input :is-read-only="isReadOnly" v-model="common.variable" size="small" maxlength="60"
|
||||||
@change="change" show-word-limit :placeholder="$t('api_test.variable_name')"/>
|
@change="change" :show-copy-tip-with-multiple="common.multipleMatching" show-word-limit :placeholder="$t('api_test.variable_name')"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col>
|
<el-col>
|
||||||
<el-input :disabled="isReadOnly" v-model="common.expression" size="small" show-word-limit
|
<el-input :disabled="isReadOnly" v-model="common.expression" size="small" show-word-limit
|
||||||
:placeholder="expression"/>
|
:placeholder="expression"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col class="multiple_checkbox">
|
||||||
|
<el-checkbox v-model="common.multipleMatching" :disabled="isReadOnly">
|
||||||
|
{{ $t('api_test.request.extract.multiple_matching') }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-col>
|
||||||
<el-col class="extract-btn">
|
<el-col class="extract-btn">
|
||||||
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove"
|
<el-button :disabled="isReadOnly" type="danger" size="mini" icon="el-icon-delete" circle @click="remove"
|
||||||
v-if="edit"/>
|
v-if="edit"/>
|
||||||
|
@ -159,4 +164,10 @@
|
||||||
.extract-btn {
|
.extract-btn {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.multiple_checkbox {
|
||||||
|
text-align: center;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -855,6 +855,7 @@ export class ExtractCommon extends ExtractType {
|
||||||
this.value = ""; // ${variable}
|
this.value = ""; // ${variable}
|
||||||
this.expression = undefined;
|
this.expression = undefined;
|
||||||
this.description = undefined;
|
this.description = undefined;
|
||||||
|
this.multipleMatching = undefined;
|
||||||
|
|
||||||
this.set(options);
|
this.set(options);
|
||||||
}
|
}
|
||||||
|
@ -1460,6 +1461,7 @@ class JMXGenerator {
|
||||||
let props = {
|
let props = {
|
||||||
name: extractCommon.variable,
|
name: extractCommon.variable,
|
||||||
expression: extractCommon.expression,
|
expression: extractCommon.expression,
|
||||||
|
match: extractCommon.multipleMatching ? -1 : undefined
|
||||||
}
|
}
|
||||||
let testName = props.name
|
let testName = props.name
|
||||||
switch (extractCommon.type) {
|
switch (extractCommon.type) {
|
||||||
|
|
|
@ -252,6 +252,10 @@
|
||||||
<el-table-column
|
<el-table-column
|
||||||
:label="$t('commons.operating')">
|
:label="$t('commons.operating')">
|
||||||
<template v-slot:default="scope">
|
<template v-slot:default="scope">
|
||||||
|
<el-button @click="preview(scope.row)" :disabled="!scope.row.id || readOnly" type="primary"
|
||||||
|
v-if="isPreview(scope.row)"
|
||||||
|
icon="el-icon-view"
|
||||||
|
size="mini" circle/>
|
||||||
<el-button @click="handleDownload(scope.row)" :disabled="!scope.row.id || readOnly" type="primary"
|
<el-button @click="handleDownload(scope.row)" :disabled="!scope.row.id || readOnly" type="primary"
|
||||||
icon="el-icon-download"
|
icon="el-icon-download"
|
||||||
size="mini" circle/>
|
size="mini" circle/>
|
||||||
|
@ -277,6 +281,7 @@
|
||||||
|
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<test-case-file ref="testCaseFile"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
@ -289,10 +294,11 @@ import MsDialogFooter from '../../../common/components/MsDialogFooter'
|
||||||
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
|
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
|
||||||
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
import {LIST_CHANGE, TrackEvent} from "@/business/components/common/head/ListEvent";
|
||||||
import {Message} from "element-ui";
|
import {Message} from "element-ui";
|
||||||
|
import TestCaseFile from "@/business/components/track/case/components/TestCaseFile";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TestCaseEdit",
|
name: "TestCaseEdit",
|
||||||
components: {MsDialogFooter},
|
components: {MsDialogFooter, TestCaseFile},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
result: {},
|
result: {},
|
||||||
|
@ -718,7 +724,13 @@ export default {
|
||||||
/// todo: 是否需要对文件内容和大小做限制
|
/// todo: 是否需要对文件内容和大小做限制
|
||||||
return file.size > 0;
|
return file.size > 0;
|
||||||
},
|
},
|
||||||
|
preview(row) {
|
||||||
|
this.$refs.testCaseFile.open(row);
|
||||||
|
},
|
||||||
|
isPreview(row) {
|
||||||
|
const fileType = row.type;
|
||||||
|
return fileType === 'JPG' || fileType === 'JPEG' || fileType === 'PDF' || fileType === 'PNG';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
<template>
|
||||||
|
<el-dialog :visible.sync="dialogVisible" width="80%" :destroy-on-close="true" :before-close="close">
|
||||||
|
<div>
|
||||||
|
<img :src="'/test/case/file/preview/' + file.id" alt="图片显示异常" style="width: 100%;height: 100%;"
|
||||||
|
v-if="file.type === 'JPG' || file.type === 'JPEG' || file.type === 'PNG'">
|
||||||
|
<div v-if="file.type === 'PDF'">
|
||||||
|
<test-case-pdf :file-id="file.id"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import TestCasePdf from "@/business/components/track/case/components/TestCasePdf";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "TestCaseFiles",
|
||||||
|
components: {TestCasePdf},
|
||||||
|
props: {},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
file: {
|
||||||
|
id: '',
|
||||||
|
type: ''
|
||||||
|
},
|
||||||
|
dialogVisible: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
open(file) {
|
||||||
|
this.file = file;
|
||||||
|
this.dialogVisible = true;
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.file = {
|
||||||
|
id: '',
|
||||||
|
type: ''
|
||||||
|
};
|
||||||
|
this.dialogVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<pdf ref="pdf" v-for="i in numPages" :key="i" :src="loadingTask" :page="i"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import pdf from "vue-pdf";
|
||||||
|
export default {
|
||||||
|
name: "TestCasePdf",
|
||||||
|
components: {pdf},
|
||||||
|
props: {
|
||||||
|
fileId: String
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
numPages: null,
|
||||||
|
loadingTask: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.loading = true;
|
||||||
|
this.loadingTask = pdf.createLoadingTask("/test/case/file/preview/" + this.fileId);
|
||||||
|
this.loadingTask.promise.then(pdf => {
|
||||||
|
this.numPages = pdf.numPages
|
||||||
|
this.loading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
this.loading = false;
|
||||||
|
this.$error("pdf 加载失败");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -554,6 +554,7 @@ export default {
|
||||||
select_type: "Choose type",
|
select_type: "Choose type",
|
||||||
description: "Extract data from the response and store it in variables. Use the variables in subsequent requests.",
|
description: "Extract data from the response and store it in variables. Use the variables in subsequent requests.",
|
||||||
regex: "Regex",
|
regex: "Regex",
|
||||||
|
multiple_matching: "Multiple matching",
|
||||||
regex_expression: "Regular expression",
|
regex_expression: "Regular expression",
|
||||||
json_path_expression: "JSONPath expression",
|
json_path_expression: "JSONPath expression",
|
||||||
xpath_expression: "XPath expression",
|
xpath_expression: "XPath expression",
|
||||||
|
@ -748,7 +749,7 @@ export default {
|
||||||
click_upload: "Upload",
|
click_upload: "Upload",
|
||||||
upload_limit: "Only XLS/XLSX/XMIND files can be uploaded, and no more than 20M",
|
upload_limit: "Only XLS/XLSX/XMIND files can be uploaded, and no more than 20M",
|
||||||
upload_xmind_format: "Upload files can only be .xmind format",
|
upload_xmind_format: "Upload files can only be .xmind format",
|
||||||
upload_xmind: "Only xmind files can be uploaded, and no more than 500",
|
upload_xmind: "Only xmind files can be uploaded, and no more than 800",
|
||||||
upload_limit_count: "Only one file can be uploaded at a time",
|
upload_limit_count: "Only one file can be uploaded at a time",
|
||||||
upload_limit_format: "Upload files can only be XLS, XLSX format!",
|
upload_limit_format: "Upload files can only be XLS, XLSX format!",
|
||||||
upload_limit_size: "Upload file size cannot exceed 20MB!",
|
upload_limit_size: "Upload file size cannot exceed 20MB!",
|
||||||
|
|
|
@ -554,6 +554,7 @@ export default {
|
||||||
},
|
},
|
||||||
extract: {
|
extract: {
|
||||||
label: "提取",
|
label: "提取",
|
||||||
|
multiple_matching: "匹配多条",
|
||||||
select_type: "请选择类型",
|
select_type: "请选择类型",
|
||||||
description: "从响应结果中提取数据并将其存储在变量中,在后续请求中使用变量。",
|
description: "从响应结果中提取数据并将其存储在变量中,在后续请求中使用变量。",
|
||||||
regex: "正则",
|
regex: "正则",
|
||||||
|
@ -751,7 +752,7 @@ export default {
|
||||||
download_template: "下载模版",
|
download_template: "下载模版",
|
||||||
click_upload: "点击上传",
|
click_upload: "点击上传",
|
||||||
upload_limit: "只能上传xls/xlsx文件,且不超过20M",
|
upload_limit: "只能上传xls/xlsx文件,且不超过20M",
|
||||||
upload_xmind: "支持文件类型:.xmind;一次至多导入500 条用例",
|
upload_xmind: "支持文件类型:.xmind;一次至多导入800 条用例",
|
||||||
upload_xmind_format: "上传文件只能是 .xmind 格式",
|
upload_xmind_format: "上传文件只能是 .xmind 格式",
|
||||||
upload_limit_other_size: "上传文件大小不能超过",
|
upload_limit_other_size: "上传文件大小不能超过",
|
||||||
upload_limit_count: "一次只能上传一个文件",
|
upload_limit_count: "一次只能上传一个文件",
|
||||||
|
|
|
@ -554,6 +554,7 @@ export default {
|
||||||
},
|
},
|
||||||
extract: {
|
extract: {
|
||||||
label: "提取",
|
label: "提取",
|
||||||
|
multiple_matching: "匹配多條",
|
||||||
select_type: "請選擇類型",
|
select_type: "請選擇類型",
|
||||||
description: "從響應結果中提取數據並將其存儲在變量中,在後續請求中使用變量。",
|
description: "從響應結果中提取數據並將其存儲在變量中,在後續請求中使用變量。",
|
||||||
regex: "正則",
|
regex: "正則",
|
||||||
|
@ -751,7 +752,7 @@ export default {
|
||||||
download_template: "下載模版",
|
download_template: "下載模版",
|
||||||
click_upload: "點擊上傳",
|
click_upload: "點擊上傳",
|
||||||
upload_limit: "只能上傳xls/xlsx文件,且不超過20M",
|
upload_limit: "只能上傳xls/xlsx文件,且不超過20M",
|
||||||
upload_xmind: "支持文件類型:.xmind;壹次至多導入500 條用例",
|
upload_xmind: "支持文件類型:.xmind;壹次至多導入800 條用例",
|
||||||
upload_xmind_format: "上傳文件只能是 .xmind 格式",
|
upload_xmind_format: "上傳文件只能是 .xmind 格式",
|
||||||
upload_limit_other_size: "上傳文件大小不能超過",
|
upload_limit_other_size: "上傳文件大小不能超過",
|
||||||
upload_limit_count: "壹次只能上傳壹個文件",
|
upload_limit_count: "壹次只能上傳壹個文件",
|
||||||
|
|
Loading…
Reference in New Issue