feat(测试跟踪): 支持思维导图导入用例
This commit is contained in:
parent
d71969b2fa
commit
76b09b4751
|
@ -297,6 +297,31 @@
|
|||
<version>0.15.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>1.20</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dom4j</groupId>
|
||||
<artifactId>dom4j</artifactId>
|
||||
<version>2.1.3</version>
|
||||
</dependency>
|
||||
<!--xpath不加这个依赖会报错-->
|
||||
<dependency>
|
||||
<groupId>jaxen</groupId>
|
||||
<artifactId>jaxen</artifactId>
|
||||
<version>1.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20171018</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -2,9 +2,10 @@ package io.metersphere.base.mapper;
|
|||
|
||||
import io.metersphere.base.domain.TestCaseNode;
|
||||
import io.metersphere.base.domain.TestCaseNodeExample;
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface TestCaseNodeMapper {
|
||||
long countByExample(TestCaseNodeExample example);
|
||||
|
||||
|
@ -14,6 +15,9 @@ public interface TestCaseNodeMapper {
|
|||
|
||||
int insert(TestCaseNode record);
|
||||
|
||||
int insertBatch(@Param("records") List<TestCaseNode> records);
|
||||
|
||||
|
||||
int insertSelective(TestCaseNode record);
|
||||
|
||||
List<TestCaseNode> selectByExample(TestCaseNodeExample example);
|
||||
|
|
|
@ -101,14 +101,28 @@
|
|||
<include refid="Example_Where_Clause" />
|
||||
</if>
|
||||
</delete>
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.TestCaseNode">
|
||||
|
||||
<insert id="insertBatch" parameterType="io.metersphere.base.domain.TestCaseNode">
|
||||
insert into test_case_node (id, project_id, name,
|
||||
parent_id, level, create_time,
|
||||
update_time)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{parentId,jdbcType=VARCHAR}, #{level,jdbcType=INTEGER}, #{createTime,jdbcType=BIGINT},
|
||||
values
|
||||
<foreach collection="records" item="emp" separator=",">
|
||||
(#{emp.id,jdbcType=VARCHAR}, #{emp.projectId,jdbcType=VARCHAR}, #{emp.name,jdbcType=VARCHAR},
|
||||
#{emp.parentId,jdbcType=VARCHAR}, #{emp.level,jdbcType=INTEGER}, #{emp.createTime,jdbcType=BIGINT},
|
||||
#{emp.updateTime,jdbcType=BIGINT})
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<insert id="insert" parameterType="io.metersphere.base.domain.TestCaseNode">
|
||||
insert into test_case_node (id, project_id, name,
|
||||
parent_id, level, create_time,
|
||||
update_time)
|
||||
values (#{id,jdbcType=VARCHAR}, #{projectId,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
|
||||
#{parentId,jdbcType=VARCHAR}, #{level,jdbcType=INTEGER}, #{createTime,jdbcType=BIGINT},
|
||||
#{updateTime,jdbcType=BIGINT})
|
||||
</insert>
|
||||
|
||||
<insert id="insertSelective" parameterType="io.metersphere.base.domain.TestCaseNode">
|
||||
insert into test_case_node
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
|
|
|
@ -99,10 +99,10 @@ public class TestCaseController {
|
|||
return testCaseService.deleteTestCase(testCaseId);
|
||||
}
|
||||
|
||||
@PostMapping("/import/{projectId}")
|
||||
@PostMapping("/import/{projectId}/{userId}")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId) throws NoSuchFieldException {
|
||||
return testCaseService.testCaseImport(file, projectId);
|
||||
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId,@PathVariable String userId) throws NoSuchFieldException {
|
||||
return testCaseService.testCaseImport(file, projectId,userId);
|
||||
}
|
||||
|
||||
@GetMapping("/export/template")
|
||||
|
@ -110,6 +110,11 @@ public class TestCaseController {
|
|||
public void testCaseTemplateExport(HttpServletResponse response) {
|
||||
testCaseService.testCaseTemplateExport(response);
|
||||
}
|
||||
@GetMapping("/export/xmindTemplate")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public void xmindTemplate(HttpServletResponse response) {
|
||||
testCaseService.testCaseXmindTemplateExport(response);
|
||||
}
|
||||
|
||||
@PostMapping("/export/testcase")
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
|
|
|
@ -27,6 +27,7 @@ import io.metersphere.i18n.Translator;
|
|||
import io.metersphere.track.dto.TestCaseDTO;
|
||||
import io.metersphere.track.request.testcase.QueryTestCaseRequest;
|
||||
import io.metersphere.track.request.testcase.TestCaseBatchRequest;
|
||||
import io.metersphere.xmind.XmindToTestCaseParser;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
import org.apache.ibatis.session.SqlSession;
|
||||
|
@ -38,6 +39,8 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.*;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -67,9 +70,6 @@ public class TestCaseService {
|
|||
@Resource
|
||||
TestCaseNodeService testCaseNodeService;
|
||||
|
||||
@Resource
|
||||
UserMapper userMapper;
|
||||
|
||||
@Resource
|
||||
UserRoleMapper userRoleMapper;
|
||||
|
||||
|
@ -236,10 +236,10 @@ public class TestCaseService {
|
|||
return projectMapper.selectByPrimaryKey(testCaseWithBLOBs.getProjectId());
|
||||
}
|
||||
|
||||
public ExcelResponse testCaseImport(MultipartFile file, String projectId) {
|
||||
|
||||
public ExcelResponse testCaseImport(MultipartFile multipartFile, String projectId, String userId) {
|
||||
|
||||
ExcelResponse excelResponse = new ExcelResponse();
|
||||
|
||||
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
|
||||
queryTestCaseRequest.setProjectId(projectId);
|
||||
|
@ -247,25 +247,44 @@ public class TestCaseService {
|
|||
Set<String> testCaseNames = testCases.stream()
|
||||
.map(TestCase::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
UserRoleExample userRoleExample = new UserRoleExample();
|
||||
userRoleExample.createCriteria()
|
||||
.andRoleIdIn(Arrays.asList(RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER))
|
||||
.andSourceIdEqualTo(currentWorkspaceId);
|
||||
|
||||
Set<String> userIds = userRoleMapper.selectByExample(userRoleExample).stream().map(UserRole::getUserId).collect(Collectors.toSet());
|
||||
|
||||
EasyExcelListener easyExcelListener = null;
|
||||
List<ExcelErrData<TestCaseExcelData>> errList = null;
|
||||
try {
|
||||
easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds);
|
||||
EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
|
||||
errList = easyExcelListener.getErrList();
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
} finally {
|
||||
easyExcelListener.close();
|
||||
|
||||
if (multipartFile.getOriginalFilename().endsWith(".xmind")) {
|
||||
try {
|
||||
errList = new ArrayList<>();
|
||||
String processLog = new XmindToTestCaseParser(this, userId, projectId, testCaseNames).importXmind(multipartFile);
|
||||
if (!StringUtils.isEmpty(processLog)) {
|
||||
excelResponse.setSuccess(false);
|
||||
ExcelErrData excelErrData = new ExcelErrData(null, 1, processLog);
|
||||
errList.add(excelErrData);
|
||||
excelResponse.setErrList(errList);
|
||||
} else {
|
||||
excelResponse.setSuccess(true);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} else {
|
||||
UserRoleExample userRoleExample = new UserRoleExample();
|
||||
userRoleExample.createCriteria()
|
||||
.andRoleIdIn(Arrays.asList(RoleConstants.TEST_MANAGER, RoleConstants.TEST_USER))
|
||||
.andSourceIdEqualTo(currentWorkspaceId);
|
||||
|
||||
Set<String> userIds = userRoleMapper.selectByExample(userRoleExample).stream().map(UserRole::getUserId).collect(Collectors.toSet());
|
||||
|
||||
EasyExcelListener easyExcelListener = null;
|
||||
try {
|
||||
easyExcelListener = new TestCaseDataListener(this, projectId, testCaseNames, userIds);
|
||||
EasyExcelFactory.read(multipartFile.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
|
||||
errList = easyExcelListener.getErrList();
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
MSException.throwException(e.getMessage());
|
||||
} finally {
|
||||
easyExcelListener.close();
|
||||
}
|
||||
|
||||
}
|
||||
//如果包含错误信息就导出错误信息
|
||||
if (!errList.isEmpty()) {
|
||||
|
@ -309,6 +328,35 @@ public class TestCaseService {
|
|||
}
|
||||
}
|
||||
|
||||
public static void download(HttpServletResponse res) throws IOException {
|
||||
// 发送给客户端的数据
|
||||
OutputStream outputStream = res.getOutputStream();
|
||||
byte[] buff = new byte[1024];
|
||||
// 读取filename
|
||||
String filePath = ClassLoader.getSystemResource("template/testcase.xmind").getPath();
|
||||
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(filePath)));) {
|
||||
int i = bis.read(buff);
|
||||
while (i != -1) {
|
||||
outputStream.write(buff, 0, buff.length);
|
||||
outputStream.flush();
|
||||
i = bis.read(buff);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
LogUtil.error(ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void testCaseXmindTemplateExport(HttpServletResponse response) {
|
||||
try {
|
||||
response.setContentType("application/vnd.ms-excel");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("思维导图用例模版", "UTF-8") + ".xmind");
|
||||
download(response);
|
||||
} catch (Exception ex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private List<TestCaseExcelData> generateExportTemplate() {
|
||||
List<TestCaseExcelData> list = new ArrayList<>();
|
||||
StringBuilder path = new StringBuilder("");
|
||||
|
@ -406,18 +454,18 @@ public class TestCaseService {
|
|||
} else if (t.getMethod().equals("auto") && t.getType().equals("api")) {
|
||||
data.setStepDesc("");
|
||||
data.setStepResult("");
|
||||
if(t.getTestId().equals("other")){
|
||||
if (t.getTestId().equals("other")) {
|
||||
data.setRemark(t.getOtherTestName());
|
||||
}else{
|
||||
} else {
|
||||
data.setRemark(t.getApiName());
|
||||
}
|
||||
|
||||
} else if (t.getMethod().equals("auto") && t.getType().equals("performance")) {
|
||||
data.setStepDesc("");
|
||||
data.setStepResult("");
|
||||
if(t.getTestId().equals("other")){
|
||||
if (t.getTestId().equals("other")) {
|
||||
data.setRemark(t.getOtherTestName());
|
||||
}else{
|
||||
} else {
|
||||
data.setRemark(t.getPerformName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
package io.metersphere.xmind;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||
import io.metersphere.commons.constants.TestCaseConstants;
|
||||
import io.metersphere.commons.utils.BeanUtils;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.track.service.TestCaseService;
|
||||
import io.metersphere.xmind.parser.XmindParser;
|
||||
import io.metersphere.xmind.parser.domain.Attached;
|
||||
import io.metersphere.xmind.parser.domain.JsonRootBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 数据转换
|
||||
*/
|
||||
public class XmindToTestCaseParser {
|
||||
|
||||
private TestCaseService testCaseService;
|
||||
private String maintainer;
|
||||
private String projectId;
|
||||
private StringBuffer process; // 过程校验记录
|
||||
private Set<String> testCaseNames;
|
||||
|
||||
public XmindToTestCaseParser(TestCaseService testCaseService, String userId, String projectId, Set<String> testCaseNames) {
|
||||
this.testCaseService = testCaseService;
|
||||
this.maintainer = userId;
|
||||
this.projectId = projectId;
|
||||
this.testCaseNames = testCaseNames;
|
||||
testCaseWithBLOBs = new LinkedList<>();
|
||||
xmindDataList = new ArrayList<>();
|
||||
process = new StringBuffer();
|
||||
}
|
||||
|
||||
// 案例详情
|
||||
private List<TestCaseWithBLOBs> testCaseWithBLOBs;
|
||||
// 用于重复对比
|
||||
protected List<TestCaseExcelData> xmindDataList;
|
||||
|
||||
// 递归处理案例数据
|
||||
private void makeXmind(StringBuffer processBuffer, String nodeId, int level, String nodePath, List<Attached> attacheds) {
|
||||
for (Attached item : attacheds) {
|
||||
if (!StringUtils.isEmpty(item.getTitle()) && item.getTitle().startsWith("tc")) { // 用例
|
||||
this.newTestCase(item.getTitle(), nodePath, item.getChildren() != null ? item.getChildren().getAttached() : null);
|
||||
} else {
|
||||
nodePath = nodePath + "/" + item.getTitle();
|
||||
if (item.getChildren() != null && !item.getChildren().getAttached().isEmpty())
|
||||
makeXmind(processBuffer, nodeId, level + 1, nodePath, item.getChildren().getAttached());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取步骤数据
|
||||
public String getSteps(List<Attached> attacheds) {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
for (int i = 0; i < attacheds.size(); i++) {
|
||||
// 保持插入顺序,判断用例是否有相同的steps
|
||||
JSONObject step = new JSONObject(true);
|
||||
step.put("num", i + 1);
|
||||
step.put("desc", attacheds.get(i).getTitle());
|
||||
if (attacheds.get(i).getChildren() != null && !attacheds.get(i).getChildren().getAttached().isEmpty()) {
|
||||
step.put("result", attacheds.get(i).getChildren().getAttached().get(0).getTitle());
|
||||
}
|
||||
jsonArray.add(step);
|
||||
}
|
||||
return jsonArray.toJSONString();
|
||||
}
|
||||
|
||||
private void newTestCase(String title, String nodePath, List<Attached> attacheds) {
|
||||
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
|
||||
testCase.setProjectId(projectId);
|
||||
testCase.setMaintainer(maintainer);
|
||||
testCase.setPriority("P0");
|
||||
testCase.setMethod("manual");
|
||||
testCase.setType("functional");
|
||||
|
||||
String tc = title.replace(":", ":");
|
||||
String tcArr[] = tc.split(":");
|
||||
if (tcArr.length != 2) {
|
||||
process.append(Translator.get("test_case_name") + "【 " + title + " 】" + Translator.get("incorrect_format"));
|
||||
}
|
||||
// 用例名称
|
||||
testCase.setName(tcArr[1].replace("tc:|tc:", ""));
|
||||
|
||||
if (!nodePath.startsWith("/")) {
|
||||
nodePath = "/" + nodePath;
|
||||
}
|
||||
if (nodePath.endsWith("/")) {
|
||||
nodePath = nodePath.substring(0, nodePath.length() - 1);
|
||||
}
|
||||
testCase.setNodePath(nodePath);
|
||||
|
||||
// 用例等级和用例性质处理
|
||||
if (tcArr[0].indexOf("-") != -1) {
|
||||
String otArr[] = tcArr[0].split("-");
|
||||
for (int i = 0; i < otArr.length; i++) {
|
||||
if (otArr[i].startsWith("P") || otArr[i].startsWith("p")) {
|
||||
testCase.setPriority(otArr[i]);
|
||||
} else if (otArr[i].endsWith("功能测试")) {
|
||||
testCase.setType("functional");
|
||||
} else if (otArr[i].endsWith("性能测试")) {
|
||||
testCase.setType("performance");
|
||||
} else if (otArr[i].endsWith("接口测试")) {
|
||||
testCase.setType("api");
|
||||
}
|
||||
}
|
||||
}
|
||||
// 测试步骤处理
|
||||
if (attacheds != null && !attacheds.isEmpty()) {
|
||||
List<Attached> steps = new LinkedList<>();
|
||||
attacheds.forEach(item -> {
|
||||
if (item.getTitle().startsWith("pc")) {
|
||||
testCase.setPrerequisite(item.getTitle().replaceAll("(?:pc:|pc:)", ""));
|
||||
} else if (item.getTitle().startsWith("rc")) {
|
||||
testCase.setRemark(item.getTitle().replaceAll("(?:rc:|rc:)", ""));
|
||||
} else {
|
||||
steps.add(item);
|
||||
}
|
||||
});
|
||||
if (!steps.isEmpty()) {
|
||||
testCase.setSteps(this.getSteps(steps));
|
||||
}
|
||||
}
|
||||
TestCaseExcelData compartData = new TestCaseExcelData();
|
||||
BeanUtils.copyBean(compartData, testCase);
|
||||
if (xmindDataList.contains(compartData)) {
|
||||
process.append(Translator.get("test_case_already_exists_excel") + ":" + testCase.getName() + "; ");
|
||||
} else if (validate(testCase)) {
|
||||
testCase.setId(UUID.randomUUID().toString());
|
||||
testCase.setCreateTime(System.currentTimeMillis());
|
||||
testCase.setUpdateTime(System.currentTimeMillis());
|
||||
testCaseWithBLOBs.add(testCase);
|
||||
}
|
||||
xmindDataList.add(compartData);
|
||||
}
|
||||
|
||||
//获取流文件
|
||||
private static void inputStreamToFile(InputStream ins, File file) {
|
||||
try (OutputStream os = new FileOutputStream(file);) {
|
||||
int bytesRead = 0;
|
||||
byte[] buffer = new byte[8192];
|
||||
while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
|
||||
os.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MultipartFile 转 File
|
||||
*
|
||||
* @param file
|
||||
* @throws Exception
|
||||
*/
|
||||
private File multipartFileToFile(MultipartFile file) throws Exception {
|
||||
if (file != null && file.getSize() > 0) {
|
||||
try (InputStream ins = file.getInputStream();) {
|
||||
File toFile = new File(file.getOriginalFilename());
|
||||
inputStreamToFile(ins, toFile);
|
||||
return toFile;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public boolean validate(TestCaseWithBLOBs data) {
|
||||
String nodePath = data.getNodePath();
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
if (nodePath != null) {
|
||||
String[] nodes = nodePath.split("/");
|
||||
if (nodes.length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||
stringBuilder.append(Translator.get("test_case_node_level_tip") +
|
||||
TestCaseConstants.MAX_NODE_DEPTH + Translator.get("test_case_node_level") + "; ");
|
||||
}
|
||||
for (int i = 0; i < nodes.length; i++) {
|
||||
if (i != 0 && org.apache.commons.lang3.StringUtils.equals(nodes[i].trim(), "")) {
|
||||
stringBuilder.append(Translator.get("module_not_null") + "; ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (org.apache.commons.lang3.StringUtils.equals(data.getType(), TestCaseConstants.Type.Functional.getValue()) && org.apache.commons.lang3.StringUtils.equals(data.getMethod(), TestCaseConstants.Method.Auto.getValue())) {
|
||||
stringBuilder.append(Translator.get("functional_method_tip") + "; ");
|
||||
}
|
||||
|
||||
if (testCaseNames.contains(data.getName())) {
|
||||
boolean dbExist = testCaseService.exist(data);
|
||||
boolean excelExist = false;
|
||||
|
||||
if (dbExist) {
|
||||
// db exist
|
||||
stringBuilder.append(Translator.get("test_case_already_exists_excel") + ":" + data.getName() + "; ");
|
||||
}
|
||||
|
||||
} else {
|
||||
testCaseNames.add(data.getName());
|
||||
}
|
||||
if (!StringUtils.isEmpty(stringBuilder.toString())) {
|
||||
process.append(stringBuilder.toString());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public String importXmind(MultipartFile multipartFile) {
|
||||
StringBuffer processBuffer = new StringBuffer();
|
||||
try {
|
||||
File file = multipartFileToFile(multipartFile);
|
||||
JsonRootBean root = XmindParser.parseObject(file);
|
||||
if (root != null && root.getRootTopic() != null && root.getRootTopic().getChildren() != null) {
|
||||
// 判断是模块还是用例
|
||||
root.getRootTopic().getChildren().getAttached().forEach(item -> {
|
||||
if (!StringUtils.isEmpty(item.getTitle()) && item.getTitle().startsWith("tc")) { // 用例
|
||||
this.newTestCase(item.getTitle(), "", item.getChildren() != null ? item.getChildren().getAttached() : null);
|
||||
} else {
|
||||
item.setPath(item.getTitle());
|
||||
if (item.getChildren() != null && !item.getChildren().getAttached().isEmpty())
|
||||
makeXmind(processBuffer, null, 1, item.getPath(), item.getChildren().getAttached());
|
||||
}
|
||||
});
|
||||
}
|
||||
if (StringUtils.isEmpty(process.toString()) && !testCaseWithBLOBs.isEmpty()) {
|
||||
testCaseService.saveImportData(testCaseWithBLOBs, projectId);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
processBuffer.append(Translator.get("incorrect_format"));
|
||||
LogUtil.error(ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
testCaseWithBLOBs.clear();
|
||||
}
|
||||
return process.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package io.metersphere.xmind.parser;
|
||||
|
||||
import org.dom4j.*;
|
||||
import org.json.JSONObject;
|
||||
import org.json.XML;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class XmindLegacy {
|
||||
|
||||
/**
|
||||
* 返回content.xml和comments.xml合并后的json
|
||||
*
|
||||
* @param xmlContent
|
||||
* @param xmlComments
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws DocumentException
|
||||
*/
|
||||
public static String getContent(String xmlContent, String xmlComments) throws IOException, DocumentException {
|
||||
// 删除content.xml里面不能识别的字符串
|
||||
xmlContent = xmlContent.replace("xmlns=\"urn:xmind:xmap:xmlns:content:2.0\"", "");
|
||||
xmlContent = xmlContent.replace("xmlns:fo=\"http://www.w3.org/1999/XSL/Format\"", "");
|
||||
// 删除<topic>节点
|
||||
xmlContent = xmlContent.replace("<topics type=\"attached\">", "");
|
||||
xmlContent = xmlContent.replace("</topics>", "");
|
||||
|
||||
// 去除title中svg:width属性
|
||||
xmlContent = xmlContent.replaceAll("<title svg:width=\"[0-9]*\">", "<title>");
|
||||
|
||||
Document document = DocumentHelper.parseText(xmlContent);// 读取XML文件,获得document对象
|
||||
Element root = document.getRootElement();
|
||||
List<Node> topics = root.selectNodes("//topic");
|
||||
|
||||
if (xmlComments != null) {
|
||||
// 删除comments.xml里面不能识别的字符串
|
||||
xmlComments = xmlComments.replace("xmlns=\"urn:xmind:xmap:xmlns:comments:2.0\"", "");
|
||||
|
||||
// 添加评论到content中
|
||||
Document commentDocument = DocumentHelper.parseText(xmlComments);
|
||||
List<Node> commentsList = commentDocument.selectNodes("//comment");
|
||||
|
||||
for (Node topic : topics) {
|
||||
for (Node commentNode : commentsList) {
|
||||
Element commentElement = (Element) commentNode;
|
||||
Element topicElement = (Element) topic;
|
||||
if (topicElement.attribute("id").getValue()
|
||||
.equals(commentElement.attribute("object-id").getValue())) {
|
||||
Element comment = topicElement.addElement("comments");
|
||||
comment.addAttribute("creationTime", commentElement.attribute("time").getValue());
|
||||
comment.addAttribute("author", commentElement.attribute("author").getValue());
|
||||
comment.addAttribute("content", commentElement.element("content").getText());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 第一个topic转换为json中的rootTopic
|
||||
Node rootTopic = root.selectSingleNode("/xmap-content/sheet/topic");
|
||||
rootTopic.setName("rootTopic");
|
||||
|
||||
// 将xml中topic节点转换为attached节点
|
||||
List<Node> topicList = rootTopic.selectNodes("//topic");
|
||||
|
||||
for (Node node : topicList) {
|
||||
node.setName("attached");
|
||||
}
|
||||
// 选取第一个sheet
|
||||
Element sheet = root.elements("sheet").get(0);
|
||||
String res = sheet.asXML();
|
||||
// 将xml转为json
|
||||
JSONObject xmlJSONObj = XML.toJSONObject(res);
|
||||
JSONObject jsonObject = xmlJSONObj.getJSONObject("sheet");
|
||||
// 设置缩进
|
||||
return jsonObject.toString(4);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package io.metersphere.xmind.parser;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import io.metersphere.xmind.parser.domain.JsonRootBean;
|
||||
import org.apache.commons.compress.archivers.ArchiveException;
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @Description 解析主体
|
||||
*/
|
||||
public class XmindParser {
|
||||
public static final String xmindZenJson = "content.json";
|
||||
public static final String xmindLegacyContent = "content.xml";
|
||||
public static final String xmindLegacyComments = "comments.xml";
|
||||
|
||||
/**
|
||||
* 解析脑图文件,返回content整合后的内容
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ArchiveException
|
||||
* @throws DocumentException
|
||||
*/
|
||||
public static String parseJson(File file) throws IOException, ArchiveException, DocumentException {
|
||||
String res = ZipUtils.extract(file);
|
||||
|
||||
String content = null;
|
||||
if (isXmindZen(res, file)) {
|
||||
content = getXmindZenContent(file, res);
|
||||
} else {
|
||||
content = getXmindLegacyContent(file, res);
|
||||
}
|
||||
|
||||
// 删除生成的文件夹
|
||||
File dir = new File(res);
|
||||
boolean flag = deleteDir(dir);
|
||||
if (flag) {
|
||||
// do something
|
||||
}
|
||||
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
||||
return (JSON.toJSONString(jsonRootBean, false));
|
||||
}
|
||||
|
||||
public static JsonRootBean parseObject(File file) throws DocumentException, ArchiveException, IOException {
|
||||
String content = parseJson(file);
|
||||
JsonRootBean jsonRootBean = JSON.parseObject(content, JsonRootBean.class);
|
||||
return jsonRootBean;
|
||||
}
|
||||
|
||||
public static boolean deleteDir(File dir) {
|
||||
if (dir.isDirectory()) {
|
||||
String[] children = dir.list();
|
||||
// 递归删除目录中的子目录下
|
||||
for (int i = 0; i < children.length; i++) {
|
||||
boolean success = deleteDir(new File(dir, children[i]));
|
||||
if (!success) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 目录此时为空,可以删除
|
||||
return dir.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static String getXmindZenContent(File file, String extractFileDir)
|
||||
throws IOException, ArchiveException {
|
||||
List<String> keys = new ArrayList<>();
|
||||
keys.add(xmindZenJson);
|
||||
Map<String, String> map = ZipUtils.getContents(keys, file, extractFileDir);
|
||||
String content = map.get(xmindZenJson);
|
||||
content = XmindZen.getContent(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public static String getXmindLegacyContent(File file, String extractFileDir)
|
||||
throws IOException, ArchiveException, DocumentException {
|
||||
List<String> keys = new ArrayList<>();
|
||||
keys.add(xmindLegacyContent);
|
||||
keys.add(xmindLegacyComments);
|
||||
Map<String, String> map = ZipUtils.getContents(keys, file, extractFileDir);
|
||||
|
||||
String contentXml = map.get(xmindLegacyContent);
|
||||
String commentsXml = map.get(xmindLegacyComments);
|
||||
String xmlContent = XmindLegacy.getContent(contentXml, commentsXml);
|
||||
|
||||
return xmlContent;
|
||||
}
|
||||
|
||||
private static boolean isXmindZen(String res, File file) throws IOException, ArchiveException {
|
||||
// 解压
|
||||
File parent = new File(res);
|
||||
if (parent.isDirectory()) {
|
||||
String[] files = parent.list(new ZipUtils.FileFilter());
|
||||
for (int i = 0; i < Objects.requireNonNull(files).length; i++) {
|
||||
if (files[i].equals(xmindZenJson)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package io.metersphere.xmind.parser;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.dom4j.DocumentException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class XmindZen {
|
||||
|
||||
/**
|
||||
* @param jsonContent
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws DocumentException
|
||||
*/
|
||||
public static String getContent(String jsonContent) {
|
||||
JSONObject jsonObject = JSONArray.parseArray(jsonContent).getJSONObject(0);
|
||||
JSONObject rootTopic = jsonObject.getJSONObject("rootTopic");
|
||||
transferNotes(rootTopic);
|
||||
JSONObject children = rootTopic.getJSONObject("children");
|
||||
recursionChildren(children);
|
||||
return jsonObject.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归转换children
|
||||
*
|
||||
* @param children
|
||||
*/
|
||||
private static void recursionChildren(JSONObject children) {
|
||||
if (children == null) {
|
||||
return;
|
||||
}
|
||||
JSONArray attachedArray = children.getJSONArray("attached");
|
||||
if (attachedArray == null) {
|
||||
return;
|
||||
}
|
||||
for (Object attached : attachedArray) {
|
||||
JSONObject attachedObject = (JSONObject) attached;
|
||||
transferNotes(attachedObject);
|
||||
JSONObject childrenObject = attachedObject.getJSONObject("children");
|
||||
if (childrenObject == null) {
|
||||
continue;
|
||||
}
|
||||
recursionChildren(childrenObject);
|
||||
}
|
||||
}
|
||||
|
||||
private static void transferNotes(JSONObject object) {
|
||||
JSONObject notes = object.getJSONObject("notes");
|
||||
if (notes == null) {
|
||||
return;
|
||||
}
|
||||
JSONObject plain = notes.getJSONObject("plain");
|
||||
if (plain != null) {
|
||||
String content = plain.getString("content");
|
||||
notes.remove("plain");
|
||||
notes.put("content", content);
|
||||
} else {
|
||||
notes.put("content", null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package io.metersphere.xmind.parser;
|
||||
|
||||
import org.apache.commons.compress.archivers.ArchiveException;
|
||||
import org.apache.commons.compress.archivers.examples.Expander;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @Description zip解压工具
|
||||
*/
|
||||
public class ZipUtils {
|
||||
|
||||
private static final String currentPath = System.getProperty("user.dir");
|
||||
|
||||
/**
|
||||
* 找到压缩文件中匹配的子文件,返回的为 getContents("comments.xml, unzip
|
||||
*
|
||||
* @param subFileNames
|
||||
* @param file
|
||||
*/
|
||||
public static Map<String, String> getContents(List<String> subFileNames, File file, String extractFileDir)
|
||||
throws IOException, ArchiveException {
|
||||
String destFilePath = extractFileDir;
|
||||
Map<String, String> map = new HashMap<>();
|
||||
File destFile = new File(destFilePath);
|
||||
if (destFile.isDirectory()) {
|
||||
String[] res = destFile.list(new FileFilter());
|
||||
for (int i = 0; i < Objects.requireNonNull(res).length; i++) {
|
||||
if (subFileNames.contains(res[i])) {
|
||||
String s = destFilePath + File.separator + res[i];
|
||||
String content = getFileContent(s);
|
||||
map.put(res[i], content);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回解压后的文件夹名字
|
||||
*
|
||||
* @return
|
||||
* @throws IOException
|
||||
* @throws ArchiveException
|
||||
*/
|
||||
public static String extract(File file) throws IOException, ArchiveException {
|
||||
Expander expander = new Expander();
|
||||
String destFileName = currentPath + File.separator + "XMind" + System.currentTimeMillis(); // 目标文件夹名字
|
||||
expander.expand(file, new File(destFileName));
|
||||
return destFileName;
|
||||
}
|
||||
|
||||
// 这是一个内部类过滤器,策略模式
|
||||
static class FileFilter implements FilenameFilter {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
// String的 endsWith(String str)方法 筛选出以str结尾的字符串
|
||||
if (name.endsWith(".xml") || name.endsWith(".json")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getFileContent(String fileName) throws IOException {
|
||||
File file;
|
||||
try {
|
||||
file = new File(fileName);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("找不到该文件");
|
||||
}
|
||||
FileReader fileReader = new FileReader(file);
|
||||
BufferedReader bufferedReder = new BufferedReader(fileReader);
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
while (bufferedReder.ready()) {
|
||||
stringBuffer.append(bufferedReder.readLine());
|
||||
}
|
||||
// 打开的文件需关闭,在unix下可以删除,否则在windows下不能删除(file.delete())
|
||||
bufferedReder.close();
|
||||
fileReader.close();
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
package io.metersphere.xmind.parser.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Attached {
|
||||
|
||||
private String id;
|
||||
private String title;
|
||||
private Notes notes;
|
||||
private String path;
|
||||
private List<Comments> comments;
|
||||
private Children children;
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class Children {
|
||||
|
||||
private List<Attached> attached;
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Comments {
|
||||
|
||||
private long creationTime;
|
||||
private String author;
|
||||
private String content;
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class JsonRootBean {
|
||||
|
||||
private String id;
|
||||
private String title;
|
||||
private RootTopic rootTopic;
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Notes {
|
||||
|
||||
private String content;
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package io.metersphere.xmind.parser.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class RootTopic {
|
||||
|
||||
private String id;
|
||||
private String title;
|
||||
private Notes notes;
|
||||
private List<Comments> comments;
|
||||
private Children children;
|
||||
|
||||
}
|
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
|
@ -1,123 +1,235 @@
|
|||
<template>
|
||||
<el-dialog class="testcase-import" :title="$t('test_track.case.import.case_import')" :visible.sync="dialogVisible"
|
||||
@close="close">
|
||||
<el-dialog class="testcase-import" :title="$t('test_track.case.import.case_import')" :visible.sync="dialogVisible"
|
||||
@close="close">
|
||||
|
||||
<el-row>
|
||||
<el-link type="primary" class="download-template"
|
||||
@click="downloadTemplate"
|
||||
>{{$t('test_track.case.import.download_template')}}</el-link></el-row>
|
||||
<el-row>
|
||||
<el-upload
|
||||
v-loading="result.loading"
|
||||
:element-loading-text="$t('test_track.case.import.importing')"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
class="upload-demo"
|
||||
multiple
|
||||
:limit="1"
|
||||
action=""
|
||||
:on-exceed="handleExceed"
|
||||
:beforeUpload="uploadValidate"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:http-request="upload"
|
||||
:file-list="fileList">
|
||||
<template v-slot:trigger>
|
||||
<el-button size="mini" type="success" plain>{{$t('test_track.case.import.click_upload')}}</el-button>
|
||||
</template>
|
||||
<template v-slot:tip>
|
||||
<div class="el-upload__tip">{{$t('test_track.case.import.upload_limit')}}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-row>
|
||||
<el-tabs v-model="activeName" simple>
|
||||
<el-tab-pane :label="$t('test_track.case.import.excel_title')" name="excelImport">
|
||||
|
||||
<el-row>
|
||||
<ul>
|
||||
<li v-for="errFile in errList" :key="errFile.rowNum">
|
||||
{{errFile.errMsg}}
|
||||
</li>
|
||||
</ul>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-link type="primary" class="download-template"
|
||||
@click="downloadTemplate"
|
||||
>{{$t('test_track.case.import.download_template')}}
|
||||
</el-link>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-upload
|
||||
v-loading="result.loading"
|
||||
:element-loading-text="$t('test_track.case.import.importing')"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
class="upload-demo"
|
||||
multiple
|
||||
:limit="1"
|
||||
action=""
|
||||
:on-exceed="handleExceed"
|
||||
:beforeUpload="uploadValidate"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:http-request="upload"
|
||||
:file-list="fileList">
|
||||
<template v-slot:trigger>
|
||||
<el-button size="mini" type="success" plain>{{$t('test_track.case.import.click_upload')}}</el-button>
|
||||
</template>
|
||||
<template v-slot:tip>
|
||||
<div class="el-upload__tip">{{$t('test_track.case.import.upload_limit')}}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-row>
|
||||
|
||||
</el-dialog>
|
||||
|
||||
<el-row>
|
||||
<ul>
|
||||
<li v-for="errFile in errList" :key="errFile.rowNum">
|
||||
{{errFile.errMsg}}
|
||||
</li>
|
||||
</ul>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
<!-- Xmind 导入 -->
|
||||
<el-tab-pane :label="$t('test_track.case.import.xmind_title')" name="xmindImport" style="border: 0px">
|
||||
<el-row class="import-row">
|
||||
<div class="el-step__icon is-text" style="background-color: #C9E6F8;border-color: #C9E6F8;margin-right: 10px">
|
||||
<div class="el-step__icon-inner">1</div>
|
||||
</div>
|
||||
<label class="ms-license-label">{{$t('test_track.case.import.import_desc')}}</label>
|
||||
</el-row>
|
||||
<el-row class="import-row">
|
||||
<el-card :body-style="{ padding: '0px' }">
|
||||
<img src="../../../../../assets/xmind.jpg" width="700px" height="250px"
|
||||
class="image">
|
||||
</el-card>
|
||||
|
||||
</el-row>
|
||||
<el-row class="import-row">
|
||||
<div class="el-step__icon is-text"
|
||||
style="background-color: #C9E6F8;border-color: #C9E6F8;margin-right: 10px ">
|
||||
<div class="el-step__icon-inner">2</div>
|
||||
</div>
|
||||
<label class="ms-license-label">{{$t('test_track.case.import.import_file')}}</label>
|
||||
</el-row>
|
||||
<el-row class="import-row">
|
||||
<el-link type="primary" class="download-template"
|
||||
@click="downloadXmindTemplate"
|
||||
>{{$t('test_track.case.import.download_template')}}
|
||||
</el-link>
|
||||
</el-row>
|
||||
<el-row class="import-row">
|
||||
<el-upload
|
||||
v-loading="result.loading"
|
||||
:element-loading-text="$t('test_track.case.import.importing')"
|
||||
element-loading-spinner="el-icon-loading"
|
||||
class="upload-demo"
|
||||
multiple
|
||||
:limit="1"
|
||||
action=""
|
||||
:on-exceed="handleExceed"
|
||||
:beforeUpload="uploadValidateXmind"
|
||||
:on-error="handleError"
|
||||
:show-file-list="false"
|
||||
:http-request="uploadXmind"
|
||||
:file-list="fileList">
|
||||
<template v-slot:trigger>
|
||||
<el-button size="mini" type="success" plain>{{$t('test_track.case.import.click_upload')}}</el-button>
|
||||
</template>
|
||||
<template v-slot:tip>
|
||||
<div class="el-upload__tip">{{$t('test_track.case.import.upload_xmind')}}</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<ul>
|
||||
<li v-for="errFile in xmindErrList" :key="errFile.rowNum">
|
||||
{{errFile.errMsg}}
|
||||
</li>
|
||||
</ul>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ElUploadList from "element-ui/packages/upload/src/upload-list";
|
||||
import MsTableButton from '../../../../components/common/components/MsTableButton';
|
||||
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
|
||||
import ElUploadList from "element-ui/packages/upload/src/upload-list";
|
||||
import MsTableButton from '../../../../components/common/components/MsTableButton';
|
||||
import {listenGoBack, removeGoBackListener} from "../../../../../common/js/utils";
|
||||
import {TokenKey, WORKSPACE_ID} from '../../../../../common/js/constants';
|
||||
|
||||
export default {
|
||||
name: "TestCaseImport",
|
||||
components: {ElUploadList, MsTableButton},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
dialogVisible: false,
|
||||
fileList: [],
|
||||
errList: [],
|
||||
isLoading: false
|
||||
}
|
||||
export default {
|
||||
name: "TestCaseImport",
|
||||
components: {ElUploadList, MsTableButton},
|
||||
data() {
|
||||
return {
|
||||
result: {},
|
||||
activeName: 'excelImport',
|
||||
dialogVisible: false,
|
||||
fileList: [],
|
||||
errList: [],
|
||||
xmindErrList: [],
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
props: {
|
||||
projectId: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleExceed(files, fileList) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
props: {
|
||||
projectId: {
|
||||
type: String
|
||||
uploadValidate(file) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (suffix != 'xls' && suffix != 'xlsx') {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_format'));
|
||||
return false;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleExceed(files, fileList) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_count'));
|
||||
},
|
||||
uploadValidate(file) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (suffix != 'xls' && suffix != 'xlsx' && suffix != 'xmind') {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_format'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
||||
return false;
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
||||
return false;
|
||||
}
|
||||
this.isLoading = true;
|
||||
this.errList = [];
|
||||
this.xmindErrList = [];
|
||||
return true;
|
||||
},
|
||||
uploadValidateXmind(file) {
|
||||
let suffix = file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||
if (suffix != 'xmind') {
|
||||
this.$warning(this.$t('test_track.case.import.upload_xmind_format'));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file.size / 1024 / 1024 > 20) {
|
||||
this.$warning(this.$t('test_track.case.import.upload_limit_size'));
|
||||
return false;
|
||||
}
|
||||
this.isLoading = true;
|
||||
this.errList = [];
|
||||
this.xmindErrList = [];
|
||||
return true;
|
||||
},
|
||||
handleError(err, file, fileList) {
|
||||
this.isLoading = false;
|
||||
this.$error(err.message);
|
||||
},
|
||||
open() {
|
||||
listenGoBack(this.close);
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
close() {
|
||||
removeGoBackListener(this.close);
|
||||
this.dialogVisible = false;
|
||||
this.fileList = [];
|
||||
this.errList = [];
|
||||
this.xmindErrList = [];
|
||||
},
|
||||
downloadTemplate() {
|
||||
this.$fileDownload('/test/case/export/template');
|
||||
},
|
||||
downloadXmindTemplate() {
|
||||
this.$fileDownload('/test/case/export/xmindTemplate');
|
||||
},
|
||||
upload(file) {
|
||||
this.isLoading = false;
|
||||
this.fileList.push(file.file);
|
||||
let user = JSON.parse(localStorage.getItem(TokenKey));
|
||||
|
||||
this.result = this.$fileUpload('/test/case/import/' + this.projectId + '/' + user.id, file.file, null, {}, response => {
|
||||
let res = response.data;
|
||||
if (res.success) {
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.dialogVisible = false;
|
||||
this.$emit("refresh");
|
||||
} else {
|
||||
this.errList = res.errList;
|
||||
}
|
||||
this.isLoading = true;
|
||||
this.errList = [];
|
||||
return true;
|
||||
},
|
||||
handleError(err, file, fileList) {
|
||||
this.isLoading = false;
|
||||
this.$error(err.message);
|
||||
},
|
||||
open() {
|
||||
listenGoBack(this.close);
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
close() {
|
||||
removeGoBackListener(this.close);
|
||||
this.dialogVisible = false;
|
||||
this.fileList = [];
|
||||
this.errList = [];
|
||||
},
|
||||
downloadTemplate() {
|
||||
this.$fileDownload('/test/case/export/template');
|
||||
},
|
||||
upload(file) {
|
||||
this.isLoading = false;
|
||||
this.fileList.push(file.file);
|
||||
this.result = this.$fileUpload('/test/case/import/' + this.projectId, file.file, null, {}, response => {
|
||||
let res = response.data;
|
||||
if (res.success) {
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.dialogVisible = false;
|
||||
this.$emit("refresh");
|
||||
} else {
|
||||
this.errList = res.errList;
|
||||
}
|
||||
this.fileList = [];
|
||||
}, erro => {
|
||||
this.fileList = [];
|
||||
});
|
||||
}
|
||||
}, erro => {
|
||||
this.fileList = [];
|
||||
});
|
||||
},
|
||||
uploadXmind(file) {
|
||||
this.isLoading = false;
|
||||
this.fileList.push(file.file);
|
||||
let user = JSON.parse(localStorage.getItem(TokenKey));
|
||||
|
||||
this.result = this.$fileUpload('/test/case/import/' + this.projectId + '/' + user.id, file.file, null, {}, response => {
|
||||
let res = response.data;
|
||||
if (res.success) {
|
||||
this.$success(this.$t('test_track.case.import.success'));
|
||||
this.dialogVisible = false;
|
||||
this.$emit("refresh");
|
||||
} else {
|
||||
this.xmindErrList = res.errList;
|
||||
}
|
||||
this.fileList = [];
|
||||
}, erro => {
|
||||
this.fileList = [];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -130,8 +242,12 @@
|
|||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.import-row {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.testcase-import >>> .el-dialog {
|
||||
width: 400px;
|
||||
width: 700px;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -653,13 +653,19 @@ export default {
|
|||
case_import: "Import test case",
|
||||
download_template: "Download template",
|
||||
click_upload: "Upload",
|
||||
upload_limit: "Only XLS/XLSX 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: "Only xmind files can be uploaded, and no more than 500",
|
||||
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_size: "Upload file size cannot exceed 20MB!",
|
||||
upload_limit_other_size: "Upload file size cannot exceed",
|
||||
success: "Import success!",
|
||||
importing: "Importing...",
|
||||
excel_title: "Excel ",
|
||||
xmind_title: "Xmind",
|
||||
import_desc: "Import instructions",
|
||||
import_file: "upload files",
|
||||
},
|
||||
export: {
|
||||
export: "Export cases"
|
||||
|
|
|
@ -657,12 +657,18 @@ export default {
|
|||
download_template: "下载模版",
|
||||
click_upload: "点击上传",
|
||||
upload_limit: "只能上传xls/xlsx文件,且不超过20M",
|
||||
upload_xmind: "支持文件类型:.xmind;一次至多导入500 条用例",
|
||||
upload_xmind_format: "上传文件只能是 .xmind 格式",
|
||||
upload_limit_other_size: "上传文件大小不能超过",
|
||||
upload_limit_count: "一次只能上传一个文件",
|
||||
upload_limit_format: "上传文件只能是 xls、xlsx格式!",
|
||||
upload_limit_size: "上传文件大小不能超过 20MB!",
|
||||
success: "导入成功!",
|
||||
importing: "导入中...",
|
||||
excel_title: "表格文件",
|
||||
xmind_title: "思维导图",
|
||||
import_desc: "导入说明",
|
||||
import_file: "上传文件",
|
||||
},
|
||||
export: {
|
||||
export: "导出用例"
|
||||
|
|
|
@ -651,13 +651,20 @@ export default {
|
|||
case_import: "導入測試用例",
|
||||
download_template: "下載模版",
|
||||
click_upload: "點擊上傳",
|
||||
upload_limit: "只能上傳xls/xlsx文件,且不超過20M",
|
||||
upload_limit: "只能上傳xls/xlsx/xmind文件,且不超過20M",
|
||||
upload_xmind: "支持文件類型:.xmind;壹次至多導入500 條用例",
|
||||
upload_xmind_format: "上傳文件只能是 .xmind 格式",
|
||||
upload_limit_count: "一次只能上傳一個文件",
|
||||
upload_limit_format: "上傳文件只能是 xls、xlsx格式!",
|
||||
upload_limit_size: "上傳文件大小不能超過 20MB!",
|
||||
upload_limit_other_size: "上傳文件大小不能超過",
|
||||
success: "導入成功!",
|
||||
importing: "導入中...",
|
||||
excel_title: "表格文件",
|
||||
xmind_title: "思維導圖",
|
||||
import_desc: "導入說明",
|
||||
import_file: "上傳文件",
|
||||
|
||||
},
|
||||
export: {
|
||||
export: "導出用例"
|
||||
|
|
Loading…
Reference in New Issue