测试用例导入
This commit is contained in:
parent
41a9fcfcf2
commit
276961d97b
|
@ -36,10 +36,6 @@
|
||||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
<exclusion>
|
|
||||||
<artifactId>hibernate-validator</artifactId>
|
|
||||||
<groupId>org.hibernate.validator</groupId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
@ -133,6 +129,13 @@
|
||||||
<version>5.1</version>
|
<version>5.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- easyexcel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>easyexcel</artifactId>
|
||||||
|
<version>2.1.7</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -1,194 +0,0 @@
|
||||||
package io.metersphere.base.domain;
|
|
||||||
|
|
||||||
public class ZaleniumTest {
|
|
||||||
|
|
||||||
private String seleniumSessionId;
|
|
||||||
private String testName;
|
|
||||||
private String timestamp;
|
|
||||||
private String addedToDashboardTime;
|
|
||||||
private String browser;
|
|
||||||
private String browserVersion;
|
|
||||||
private String proxyName;
|
|
||||||
private String platform;
|
|
||||||
private String fileName;
|
|
||||||
private String fileExtension;
|
|
||||||
private String videoFolderPath;
|
|
||||||
private String logsFolderPath;
|
|
||||||
private String testNameNoExtension;
|
|
||||||
private String screenDimension;
|
|
||||||
private String timeZone;
|
|
||||||
private String build;
|
|
||||||
private String testFileNameTemplate;
|
|
||||||
private String browserDriverLogFileName;
|
|
||||||
private String retentionDate;
|
|
||||||
private String testStatus;
|
|
||||||
private boolean videoRecorded;
|
|
||||||
|
|
||||||
public String getSeleniumSessionId() {
|
|
||||||
return seleniumSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSeleniumSessionId(String seleniumSessionId) {
|
|
||||||
this.seleniumSessionId = seleniumSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTestName() {
|
|
||||||
return testName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTestName(String testName) {
|
|
||||||
this.testName = testName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTimestamp() {
|
|
||||||
return timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTimestamp(String timestamp) {
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAddedToDashboardTime() {
|
|
||||||
return addedToDashboardTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAddedToDashboardTime(String addedToDashboardTime) {
|
|
||||||
this.addedToDashboardTime = addedToDashboardTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBrowser() {
|
|
||||||
return browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBrowser(String browser) {
|
|
||||||
this.browser = browser;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBrowserVersion() {
|
|
||||||
return browserVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBrowserVersion(String browserVersion) {
|
|
||||||
this.browserVersion = browserVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getProxyName() {
|
|
||||||
return proxyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setProxyName(String proxyName) {
|
|
||||||
this.proxyName = proxyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPlatform() {
|
|
||||||
return platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPlatform(String platform) {
|
|
||||||
this.platform = platform;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFileName() {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFileName(String fileName) {
|
|
||||||
this.fileName = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFileExtension() {
|
|
||||||
return fileExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFileExtension(String fileExtension) {
|
|
||||||
this.fileExtension = fileExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getVideoFolderPath() {
|
|
||||||
return videoFolderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoFolderPath(String videoFolderPath) {
|
|
||||||
this.videoFolderPath = videoFolderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLogsFolderPath() {
|
|
||||||
return logsFolderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLogsFolderPath(String logsFolderPath) {
|
|
||||||
this.logsFolderPath = logsFolderPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTestNameNoExtension() {
|
|
||||||
return testNameNoExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTestNameNoExtension(String testNameNoExtension) {
|
|
||||||
this.testNameNoExtension = testNameNoExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getScreenDimension() {
|
|
||||||
return screenDimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setScreenDimension(String screenDimension) {
|
|
||||||
this.screenDimension = screenDimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTimeZone() {
|
|
||||||
return timeZone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTimeZone(String timeZone) {
|
|
||||||
this.timeZone = timeZone;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBuild() {
|
|
||||||
return build;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBuild(String build) {
|
|
||||||
this.build = build;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTestFileNameTemplate() {
|
|
||||||
return testFileNameTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTestFileNameTemplate(String testFileNameTemplate) {
|
|
||||||
this.testFileNameTemplate = testFileNameTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBrowserDriverLogFileName() {
|
|
||||||
return browserDriverLogFileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBrowserDriverLogFileName(String browserDriverLogFileName) {
|
|
||||||
this.browserDriverLogFileName = browserDriverLogFileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRetentionDate() {
|
|
||||||
return retentionDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRetentionDate(String retentionDate) {
|
|
||||||
this.retentionDate = retentionDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTestStatus() {
|
|
||||||
return testStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTestStatus(String testStatus) {
|
|
||||||
this.testStatus = testStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVideoRecorded() {
|
|
||||||
return videoRecorded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVideoRecorded(boolean videoRecorded) {
|
|
||||||
this.videoRecorded = videoRecorded;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package io.metersphere.commons.constants;
|
||||||
|
|
||||||
|
public class TestCaseConstants {
|
||||||
|
public static final int MAX_NODE_DEPTH = 5;
|
||||||
|
}
|
|
@ -6,14 +6,11 @@ import io.metersphere.base.domain.*;
|
||||||
import io.metersphere.commons.utils.PageUtils;
|
import io.metersphere.commons.utils.PageUtils;
|
||||||
import io.metersphere.commons.utils.Pager;
|
import io.metersphere.commons.utils.Pager;
|
||||||
import io.metersphere.controller.request.testcase.QueryTestCaseRequest;
|
import io.metersphere.controller.request.testcase.QueryTestCaseRequest;
|
||||||
import io.metersphere.controller.request.testplan.QueryTestPlanRequest;
|
import io.metersphere.excel.domain.ExcelResponse;
|
||||||
import io.metersphere.dto.LoadTestDTO;
|
|
||||||
import io.metersphere.dto.TestCaseNodeDTO;
|
|
||||||
import io.metersphere.dto.TestPlanCaseDTO;
|
|
||||||
import io.metersphere.service.TestCaseNodeService;
|
|
||||||
import io.metersphere.service.TestCaseService;
|
import io.metersphere.service.TestCaseService;
|
||||||
import io.metersphere.user.SessionUtils;
|
import io.metersphere.user.SessionUtils;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -74,5 +71,10 @@ public class TestCaseController {
|
||||||
return testCaseService.deleteTestCase(testCaseId);
|
return testCaseService.deleteTestCase(testCaseId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/import/{projectId}")
|
||||||
|
public ExcelResponse testCaseImport(MultipartFile file, @PathVariable String projectId){
|
||||||
|
return testCaseService.testCaseImport(file, projectId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package io.metersphere.excel.domain;
|
||||||
|
|
||||||
|
public class ExcelErrData<T> {
|
||||||
|
|
||||||
|
private T t;
|
||||||
|
|
||||||
|
private Integer rowNum;
|
||||||
|
|
||||||
|
private String errMsg;
|
||||||
|
|
||||||
|
public ExcelErrData(){}
|
||||||
|
|
||||||
|
public ExcelErrData(T t, Integer rowNum,String errMsg){
|
||||||
|
this.t = t;
|
||||||
|
this.rowNum = rowNum;
|
||||||
|
this.errMsg = errMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getT() {
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setT(T t) {
|
||||||
|
this.t = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrMsg() {
|
||||||
|
return errMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrMsg(String errMsg) {
|
||||||
|
this.errMsg = errMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getRowNum() {
|
||||||
|
return rowNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRowNum(Integer rowNum) {
|
||||||
|
this.rowNum = rowNum;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package io.metersphere.excel.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ExcelResponse<T> {
|
||||||
|
|
||||||
|
private Boolean success;
|
||||||
|
private List<ExcelErrData<T>> errList;
|
||||||
|
|
||||||
|
public Boolean getSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(Boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ExcelErrData<T>> getErrList() {
|
||||||
|
return errList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrList(List<ExcelErrData<T>> errList) {
|
||||||
|
this.errList = errList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,137 @@
|
||||||
|
package io.metersphere.excel.domain;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
|
public class TestCaseExcelData {
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Length(max=1000)
|
||||||
|
@ExcelProperty("所属模块")
|
||||||
|
@Pattern(regexp = "^(?!.*//).*$", message = "格式不正确")
|
||||||
|
private String nodePath;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@Length(max=50)
|
||||||
|
@ExcelProperty("用例名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ExcelProperty("用例类型")
|
||||||
|
@Pattern(regexp = "(^functional$)|(^performance$)|(^api$)", message = "必须为functional、performance、api")
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ExcelProperty("维护人")
|
||||||
|
private String maintainer;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ExcelProperty("优先级")
|
||||||
|
@Pattern(regexp = "(^P0$)|(^P1$)|(^P2$)|(^P3$)", message = "必须为P0、P1、P2、P3")
|
||||||
|
private String priority;
|
||||||
|
|
||||||
|
@NotBlank
|
||||||
|
@ExcelProperty("测试方式")
|
||||||
|
@Pattern(regexp = "(^manual$)|(^auto$)", message = "必须为manual、auto")
|
||||||
|
private String method;
|
||||||
|
|
||||||
|
@ExcelProperty("前置条件")
|
||||||
|
@Length(min=0, max=1000)
|
||||||
|
private String prerequisite;
|
||||||
|
|
||||||
|
@ExcelProperty("备注")
|
||||||
|
@Length(max=1000)
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
@ExcelProperty("步骤描述")
|
||||||
|
@Length(max=1000)
|
||||||
|
private String stepDesc;
|
||||||
|
|
||||||
|
@ExcelProperty("预期结果")
|
||||||
|
@Length(max=1000)
|
||||||
|
private String stepResult;
|
||||||
|
|
||||||
|
public String getNodePath() {
|
||||||
|
return nodePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNodePath(String nodePath) {
|
||||||
|
this.nodePath = nodePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(String type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMaintainer() {
|
||||||
|
return maintainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaintainer(String maintainer) {
|
||||||
|
this.maintainer = maintainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPriority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPriority(String priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethod(String method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrerequisite() {
|
||||||
|
return prerequisite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrerequisite(String prerequisite) {
|
||||||
|
this.prerequisite = prerequisite;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRemark() {
|
||||||
|
return remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRemark(String remark) {
|
||||||
|
this.remark = remark;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStepDesc() {
|
||||||
|
return stepDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStepDesc(String stepDesc) {
|
||||||
|
this.stepDesc = stepDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getStepResult() {
|
||||||
|
return stepResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStepResult(String stepResult) {
|
||||||
|
this.stepResult = stepResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
package io.metersphere.excel.listener;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import com.alibaba.excel.context.AnalysisContext;
|
||||||
|
import com.alibaba.excel.event.AnalysisEventListener;
|
||||||
|
import com.alibaba.excel.exception.ExcelAnalysisException;
|
||||||
|
import com.alibaba.excel.util.StringUtils;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
|
import io.metersphere.excel.util.ExcelValidateHelper;
|
||||||
|
import io.metersphere.excel.domain.ExcelErrData;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class EasyExcelListener <T> extends AnalysisEventListener<T> {
|
||||||
|
|
||||||
|
protected List<ExcelErrData<T>> errList = new ArrayList<>();
|
||||||
|
|
||||||
|
protected List<T> list = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每隔2000条存储数据库,然后清理list ,方便内存回收
|
||||||
|
*/
|
||||||
|
protected static final int BATCH_COUNT = 2000;
|
||||||
|
|
||||||
|
protected Class<T> clazz;
|
||||||
|
|
||||||
|
|
||||||
|
public EasyExcelListener(Class<T> clazz){
|
||||||
|
this.clazz = clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这个每一条数据解析都会来调用
|
||||||
|
*
|
||||||
|
* @param t
|
||||||
|
* @param analysisContext
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void invoke(T t, AnalysisContext analysisContext) {
|
||||||
|
String errMsg;
|
||||||
|
Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
|
||||||
|
try {
|
||||||
|
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
|
||||||
|
errMsg = ExcelValidateHelper.validateEntity(t);
|
||||||
|
//自定义校验规则
|
||||||
|
errMsg = validate(t, errMsg);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
errMsg = "解析数据出错";
|
||||||
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!StringUtils.isEmpty(errMsg)) {
|
||||||
|
ExcelErrData excelErrData = new ExcelErrData(t, rowIndex, "第" + rowIndex + "行出错:" + errMsg);
|
||||||
|
errList.add(excelErrData);
|
||||||
|
} else {
|
||||||
|
list.add(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.size() > BATCH_COUNT) {
|
||||||
|
saveData();
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可重写该方法
|
||||||
|
* 自定义校验规则
|
||||||
|
* @param data
|
||||||
|
* @param errMsg
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String validate(T data, String errMsg) {
|
||||||
|
return errMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义数据保存操作
|
||||||
|
*/
|
||||||
|
public abstract void saveData();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
|
||||||
|
saveData();
|
||||||
|
list.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验excel头部
|
||||||
|
* @param headMap 传入excel的头部(第一行数据)数据的index,name
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
|
||||||
|
super.invokeHeadMap(headMap, context);
|
||||||
|
if (clazz != null){
|
||||||
|
try {
|
||||||
|
Set<String> fieldNameSet = getFieldNameSet(clazz);
|
||||||
|
Collection<String> values = headMap.values();
|
||||||
|
for (String key : fieldNameSet) {
|
||||||
|
if (!values.contains(key)){
|
||||||
|
throw new ExcelAnalysisException("缺少头部信息:" + key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description: 获取注解里ExcelProperty的value
|
||||||
|
*/
|
||||||
|
public Set<String> getFieldNameSet(Class clazz) throws NoSuchFieldException {
|
||||||
|
Set<String> result = new HashSet<>();
|
||||||
|
Field field;
|
||||||
|
Field[] fields = clazz.getDeclaredFields();
|
||||||
|
for (int i = 0; i < fields.length ; i++) {
|
||||||
|
field = clazz.getDeclaredField(fields[i].getName());
|
||||||
|
field.setAccessible(true);
|
||||||
|
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
||||||
|
if(excelProperty != null){
|
||||||
|
StringBuilder value = new StringBuilder();
|
||||||
|
for (String v : excelProperty.value()) {
|
||||||
|
value.append(v);
|
||||||
|
}
|
||||||
|
result.add(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public List<ExcelErrData<T>> getErrList() {
|
||||||
|
return errList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
package io.metersphere.excel.listener;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONArray;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||||
|
import io.metersphere.base.domain.TestCaseWithBLOBs;
|
||||||
|
import io.metersphere.commons.constants.TestCaseConstants;
|
||||||
|
import io.metersphere.commons.utils.BeanUtils;
|
||||||
|
import io.metersphere.service.TestCaseService;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class TestCaseDataListener extends EasyExcelListener<TestCaseExcelData> {
|
||||||
|
|
||||||
|
private TestCaseService testCaseService;
|
||||||
|
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
Set<String> testCaseNames;
|
||||||
|
|
||||||
|
Set<String> userNames;
|
||||||
|
|
||||||
|
public TestCaseDataListener(TestCaseService testCaseService, String projectId,
|
||||||
|
Set<String> testCaseNames, Set<String> userNames, Class<TestCaseExcelData> clazz) {
|
||||||
|
super(clazz);
|
||||||
|
this.testCaseService = testCaseService;
|
||||||
|
this.projectId = projectId;
|
||||||
|
this.testCaseNames = testCaseNames;
|
||||||
|
this.userNames = userNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String validate(TestCaseExcelData data, String errMsg) {
|
||||||
|
String nodePath = data.getNodePath();
|
||||||
|
StringBuilder stringBuilder = new StringBuilder(errMsg);
|
||||||
|
if ( nodePath.split("/").length > TestCaseConstants.MAX_NODE_DEPTH + 1) {
|
||||||
|
stringBuilder.append("节点最多为" + TestCaseConstants.MAX_NODE_DEPTH + "层;");
|
||||||
|
}
|
||||||
|
if (!userNames.contains(data.getMaintainer())) {
|
||||||
|
stringBuilder.append("该工作空间下无该用户:" + data.getMaintainer() + ";");
|
||||||
|
}
|
||||||
|
if (testCaseNames.contains(data.getName())) {
|
||||||
|
stringBuilder.append("该项目下已存在该测试用例:" + data.getName() + ";");
|
||||||
|
}
|
||||||
|
return stringBuilder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveData() {
|
||||||
|
|
||||||
|
//无错误数据才插入数据
|
||||||
|
if (!errList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<TestCaseWithBLOBs> result = list.stream()
|
||||||
|
.map(item -> this.convert2TestCase(item))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
testCaseService.saveImportData(result, projectId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private TestCaseWithBLOBs convert2TestCase(TestCaseExcelData data) {
|
||||||
|
TestCaseWithBLOBs testCase = new TestCaseWithBLOBs();
|
||||||
|
BeanUtils.copyBean(testCase, data);
|
||||||
|
testCase.setId(UUID.randomUUID().toString());
|
||||||
|
testCase.setProjectId(this.projectId);
|
||||||
|
testCase.setCreateTime(System.currentTimeMillis());
|
||||||
|
testCase.setUpdateTime(System.currentTimeMillis());
|
||||||
|
String nodePath = data.getNodePath();
|
||||||
|
|
||||||
|
if (!nodePath.startsWith("/")) {
|
||||||
|
nodePath = "/" + nodePath;
|
||||||
|
}
|
||||||
|
if (nodePath.endsWith("/")) {
|
||||||
|
nodePath = nodePath.substring(0, nodePath.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
testCase.setNodePath(nodePath);
|
||||||
|
|
||||||
|
|
||||||
|
JSONArray jsonArray = new JSONArray();
|
||||||
|
|
||||||
|
String[] stepDesc = new String[0];
|
||||||
|
String[] stepRes = new String[0];
|
||||||
|
|
||||||
|
if (data.getStepDesc() != null) {
|
||||||
|
stepDesc = data.getStepDesc().split("\n");
|
||||||
|
}
|
||||||
|
if (data.getStepResult() != null) {
|
||||||
|
stepRes = data.getStepResult().split("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
String pattern = "(^\\d+)(\\.)?";
|
||||||
|
int index = stepDesc.length > stepRes.length ? stepDesc.length : stepRes.length;
|
||||||
|
|
||||||
|
for (int i = 0; i < index; i++){
|
||||||
|
|
||||||
|
JSONObject step = new JSONObject();
|
||||||
|
step.put("num", i + 1);
|
||||||
|
|
||||||
|
Pattern descPattern = Pattern.compile(pattern);
|
||||||
|
Pattern resPattern = Pattern.compile(pattern);
|
||||||
|
|
||||||
|
if (i < stepDesc.length) {
|
||||||
|
Matcher descMatcher = descPattern.matcher(stepDesc[i]);
|
||||||
|
if (descMatcher.find()) {
|
||||||
|
step.put("desc", descMatcher.replaceAll(""));
|
||||||
|
} else {
|
||||||
|
step.put("desc", stepDesc[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < stepRes.length) {
|
||||||
|
Matcher resMatcher = resPattern.matcher(stepRes[i]);
|
||||||
|
if (resMatcher.find()) {
|
||||||
|
step.put("result", resMatcher.replaceAll(""));
|
||||||
|
} else {
|
||||||
|
step.put("result", stepRes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonArray.add(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
testCase.setSteps(jsonArray.toJSONString());
|
||||||
|
|
||||||
|
return testCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package io.metersphere.excel.util;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
|
||||||
|
import javax.validation.ConstraintViolation;
|
||||||
|
import javax.validation.Validation;
|
||||||
|
import javax.validation.Validator;
|
||||||
|
import javax.validation.groups.Default;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
|
||||||
|
public class ExcelValidateHelper {
|
||||||
|
|
||||||
|
private ExcelValidateHelper(){}
|
||||||
|
|
||||||
|
private static Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||||
|
|
||||||
|
public static <T> String validateEntity(T obj) throws NoSuchFieldException {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
Set<ConstraintViolation<T>> set = validator.validate(obj, Default.class);
|
||||||
|
if (set != null && !set.isEmpty()) {
|
||||||
|
for (ConstraintViolation<T> cv : set) {
|
||||||
|
Field declaredField = obj.getClass().getDeclaredField(cv.getPropertyPath().toString());
|
||||||
|
ExcelProperty annotation = declaredField.getAnnotation(ExcelProperty.class);
|
||||||
|
//拼接错误信息,包含当前出错数据的标题名字+错误信息
|
||||||
|
result.append(annotation.value()[0]+cv.getMessage()).append(";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package io.metersphere.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author jianxing.chen
|
||||||
|
*/
|
||||||
|
public class ExcelImportException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public ExcelImportException(String message, Exception e){
|
||||||
|
super(message, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExcelImportException(String message){
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -6,8 +6,11 @@ import io.metersphere.base.mapper.TestCaseMapper;
|
||||||
import io.metersphere.base.mapper.TestCaseNodeMapper;
|
import io.metersphere.base.mapper.TestCaseNodeMapper;
|
||||||
import io.metersphere.base.mapper.TestPlanMapper;
|
import io.metersphere.base.mapper.TestPlanMapper;
|
||||||
import io.metersphere.base.mapper.TestPlanTestCaseMapper;
|
import io.metersphere.base.mapper.TestPlanTestCaseMapper;
|
||||||
|
import io.metersphere.commons.constants.TestCaseConstants;
|
||||||
import io.metersphere.commons.utils.BeanUtils;
|
import io.metersphere.commons.utils.BeanUtils;
|
||||||
import io.metersphere.dto.TestCaseNodeDTO;
|
import io.metersphere.dto.TestCaseNodeDTO;
|
||||||
|
import io.metersphere.exception.ExcelImportException;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -30,8 +33,8 @@ public class TestCaseNodeService {
|
||||||
|
|
||||||
public int addNode(TestCaseNode node) {
|
public int addNode(TestCaseNode node) {
|
||||||
|
|
||||||
if(node.getLevel() > 5){
|
if(node.getLevel() > TestCaseConstants.MAX_NODE_DEPTH){
|
||||||
throw new RuntimeException("模块树最大深度为5层!");
|
throw new RuntimeException("模块树最大深度为" + TestCaseConstants.MAX_NODE_DEPTH + "层!");
|
||||||
}
|
}
|
||||||
node.setCreateTime(System.currentTimeMillis());
|
node.setCreateTime(System.currentTimeMillis());
|
||||||
node.setUpdateTime(System.currentTimeMillis());
|
node.setUpdateTime(System.currentTimeMillis());
|
||||||
|
@ -196,4 +199,124 @@ public class TestCaseNodeService {
|
||||||
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId);
|
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(planId);
|
||||||
return getNodeTreeByProjectId(testPlan.getProjectId());
|
return getNodeTreeByProjectId(testPlan.getProjectId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Integer> createNodeByTestCases(List<TestCaseWithBLOBs> testCases, String projectId) {
|
||||||
|
|
||||||
|
List<TestCaseNodeDTO> nodeTrees = getNodeTreeByProjectId(projectId);
|
||||||
|
|
||||||
|
Map<String, Integer> pathMap = new HashMap<>();
|
||||||
|
|
||||||
|
List<String> nodePaths = testCases.stream()
|
||||||
|
.map(TestCase::getNodePath)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
nodePaths.forEach(path -> {
|
||||||
|
|
||||||
|
if (path == null) {
|
||||||
|
throw new ExcelImportException("所属模块不能为空!");
|
||||||
|
}
|
||||||
|
List<String> nodeNameList = new ArrayList<>(Arrays.asList(path.split("/")));
|
||||||
|
Iterator<String> pathIterator = nodeNameList.iterator();
|
||||||
|
|
||||||
|
Boolean hasNode = false;
|
||||||
|
String rootNodeName = null;
|
||||||
|
|
||||||
|
if (nodeNameList.size() <= 1) {
|
||||||
|
throw new ExcelImportException("创建模块失败:" + path);
|
||||||
|
} else {
|
||||||
|
pathIterator.next();
|
||||||
|
pathIterator.remove();
|
||||||
|
|
||||||
|
rootNodeName = pathIterator.next().trim();
|
||||||
|
for (TestCaseNodeDTO nodeTree : nodeTrees) {
|
||||||
|
if (StringUtils.equals(rootNodeName, nodeTree.getName())) {
|
||||||
|
hasNode = true;
|
||||||
|
createNodeByPathIterator(pathIterator, "/" + rootNodeName, nodeTree,
|
||||||
|
pathMap, projectId, 2);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasNode) {
|
||||||
|
createNodeByPath(pathIterator, rootNodeName, null, projectId, 1, "", pathMap);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return pathMap;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据目标节点路径,创建相关节点
|
||||||
|
* @param pathIterator 遍历子路径
|
||||||
|
* @param path 当前路径
|
||||||
|
* @param treeNode 当前节点
|
||||||
|
* @param pathMap 记录节点路径对应的nodeId
|
||||||
|
*/
|
||||||
|
private void createNodeByPathIterator(Iterator<String> pathIterator, String path, TestCaseNodeDTO treeNode,
|
||||||
|
Map<String, Integer> pathMap, String projectId, Integer level) {
|
||||||
|
|
||||||
|
List<TestCaseNodeDTO> children = treeNode.getChildren();
|
||||||
|
|
||||||
|
if (children == null || children.isEmpty() || !pathIterator.hasNext()) {
|
||||||
|
pathMap.put(path , treeNode.getId());
|
||||||
|
if (pathIterator.hasNext()) {
|
||||||
|
createNodeByPath(pathIterator, pathIterator.next().trim(), treeNode, projectId, level, path, pathMap);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String nodeName = pathIterator.next().trim();
|
||||||
|
|
||||||
|
Boolean hasNode = false;
|
||||||
|
|
||||||
|
for (TestCaseNodeDTO child : children) {
|
||||||
|
if (StringUtils.equals(nodeName, child.getName())) {
|
||||||
|
hasNode = true;
|
||||||
|
createNodeByPathIterator(pathIterator, path + "/" + child.getName(),
|
||||||
|
child, pathMap, projectId, level + 1);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//若子节点中不包含该目标节点,则在该节点下创建
|
||||||
|
if (!hasNode) {
|
||||||
|
createNodeByPath(pathIterator, nodeName, treeNode, projectId, level, path, pathMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param pathIterator 迭代器,遍历子节点
|
||||||
|
* @param nodeName 当前节点
|
||||||
|
* @param pNode 父节点
|
||||||
|
*/
|
||||||
|
private void createNodeByPath(Iterator<String> pathIterator, String nodeName,
|
||||||
|
TestCaseNodeDTO pNode, String projectId, Integer level,
|
||||||
|
String rootPath, Map<String, Integer> pathMap) {
|
||||||
|
|
||||||
|
StringBuilder path = new StringBuilder(rootPath);
|
||||||
|
|
||||||
|
Integer pid = insertTestCaseNode(nodeName, pNode == null ? null : pNode.getId(), projectId, level);
|
||||||
|
path.append("/" + nodeName);
|
||||||
|
pathMap.put(path.toString(), pid);
|
||||||
|
while (pathIterator.hasNext()) {
|
||||||
|
String nextNodeName = pathIterator.next();
|
||||||
|
path.append("/" + nextNodeName);
|
||||||
|
pid = insertTestCaseNode(nextNodeName, pid, projectId, ++level);
|
||||||
|
pathMap.put(path.toString(), pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer insertTestCaseNode(String nodName, Integer pId, String projectId, Integer level) {
|
||||||
|
TestCaseNode testCaseNode = new TestCaseNode();
|
||||||
|
testCaseNode.setName(nodName.trim());
|
||||||
|
testCaseNode.setpId(pId);
|
||||||
|
testCaseNode.setProjectId(projectId);
|
||||||
|
testCaseNode.setCreateTime(System.currentTimeMillis());
|
||||||
|
testCaseNode.setUpdateTime(System.currentTimeMillis());
|
||||||
|
testCaseNode.setLevel(level);
|
||||||
|
testCaseNodeMapper.insert(testCaseNode);
|
||||||
|
return testCaseNode.getId();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,34 @@
|
||||||
package io.metersphere.service;
|
package io.metersphere.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.alibaba.excel.EasyExcelFactory;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
import io.metersphere.base.domain.*;
|
import io.metersphere.base.domain.*;
|
||||||
import io.metersphere.base.mapper.ProjectMapper;
|
import io.metersphere.base.mapper.*;
|
||||||
import io.metersphere.base.mapper.TestCaseMapper;
|
|
||||||
import io.metersphere.base.mapper.TestPlanMapper;
|
|
||||||
import io.metersphere.base.mapper.TestPlanTestCaseMapper;
|
|
||||||
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
import io.metersphere.base.mapper.ext.ExtTestCaseMapper;
|
||||||
|
import io.metersphere.commons.utils.LogUtil;
|
||||||
import io.metersphere.controller.request.testcase.QueryTestCaseRequest;
|
import io.metersphere.controller.request.testcase.QueryTestCaseRequest;
|
||||||
import io.metersphere.dto.TestPlanCaseDTO;
|
import io.metersphere.excel.domain.ExcelErrData;
|
||||||
|
import io.metersphere.excel.domain.ExcelResponse;
|
||||||
|
import io.metersphere.excel.domain.TestCaseExcelData;
|
||||||
|
import io.metersphere.excel.listener.EasyExcelListener;
|
||||||
|
import io.metersphere.excel.listener.TestCaseDataListener;
|
||||||
|
import io.metersphere.user.SessionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.ibatis.session.ExecutorType;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
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.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.ArrayList;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
@ -41,6 +49,15 @@ public class TestCaseService {
|
||||||
@Resource
|
@Resource
|
||||||
ProjectMapper projectMapper;
|
ProjectMapper projectMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
SqlSessionFactory sqlSessionFactory;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
TestCaseNodeService testCaseNodeService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
UserMapper userMapper;
|
||||||
|
|
||||||
public void addTestCase(TestCaseWithBLOBs testCase) {
|
public void addTestCase(TestCaseWithBLOBs testCase) {
|
||||||
testCase.setId(UUID.randomUUID().toString());
|
testCase.setId(UUID.randomUUID().toString());
|
||||||
testCase.setCreateTime(System.currentTimeMillis());
|
testCase.setCreateTime(System.currentTimeMillis());
|
||||||
|
@ -144,4 +161,59 @@ public class TestCaseService {
|
||||||
}
|
}
|
||||||
return projectMapper.selectByPrimaryKey(testCaseWithBLOBs.getProjectId());
|
return projectMapper.selectByPrimaryKey(testCaseWithBLOBs.getProjectId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExcelResponse testCaseImport(MultipartFile file, String projectId) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
ExcelResponse excelResponse = new ExcelResponse();
|
||||||
|
|
||||||
|
String currentWorkspaceId = SessionUtils.getCurrentWorkspaceId();
|
||||||
|
QueryTestCaseRequest queryTestCaseRequest = new QueryTestCaseRequest();
|
||||||
|
queryTestCaseRequest.setProjectId(projectId);
|
||||||
|
List<TestCase> testCases = extTestCaseMapper.getTestCaseNames(queryTestCaseRequest);
|
||||||
|
Set<String> testCaseNames = testCases.stream()
|
||||||
|
.map(TestCase::getName)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
UserExample userExample = new UserExample();
|
||||||
|
userExample.createCriteria().andLastWorkspaceIdEqualTo(currentWorkspaceId);
|
||||||
|
List<User> users = userMapper.selectByExample(userExample);
|
||||||
|
Set<String> userNames = users.stream().map(User::getName).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
EasyExcelListener easyExcelListener = new TestCaseDataListener(this, projectId,
|
||||||
|
testCaseNames, userNames, TestCaseExcelData.class);
|
||||||
|
EasyExcelFactory.read(file.getInputStream(), TestCaseExcelData.class, easyExcelListener).sheet().doRead();
|
||||||
|
|
||||||
|
List<ExcelErrData<TestCaseExcelData>> errList = easyExcelListener.getErrList();
|
||||||
|
//如果包含错误信息就导出错误信息
|
||||||
|
if (!errList.isEmpty()) {
|
||||||
|
excelResponse.setSuccess(false);
|
||||||
|
excelResponse.setErrList(errList);
|
||||||
|
} else {
|
||||||
|
excelResponse.setSuccess(true);
|
||||||
|
}
|
||||||
|
return excelResponse;
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
LogUtil.error(e.getMessage(), e);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveImportData(List<TestCaseWithBLOBs> testCases, String projectId) {
|
||||||
|
|
||||||
|
Map<String, Integer> nodePathMap = testCaseNodeService.createNodeByTestCases(testCases, projectId);
|
||||||
|
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||||
|
TestCaseMapper mapper = sqlSession.getMapper(TestCaseMapper.class);
|
||||||
|
if (!testCases.isEmpty()) {
|
||||||
|
testCases.forEach(testcase -> {
|
||||||
|
testcase.setNodeId(nodePathMap.get(testcase.getNodePath()));
|
||||||
|
mapper.insert(testcase);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sqlSession.flushStatements();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
:current-project="currentProject"
|
:current-project="currentProject"
|
||||||
@openTestCaseEditDialog="openTestCaseEditDialog"
|
@openTestCaseEditDialog="openTestCaseEditDialog"
|
||||||
@testCaseEdit="openTestCaseEditDialog"
|
@testCaseEdit="openTestCaseEditDialog"
|
||||||
|
@refresh="refresh"
|
||||||
ref="testCaseList">
|
ref="testCaseList">
|
||||||
</test-case-list>
|
</test-case-list>
|
||||||
</el-main>
|
</el-main>
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-tooltip class="item" effect="dark" content="导出用例" placement="right">
|
||||||
|
<el-button type="info" icon="el-icon-download" size="mini" circle></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "TestCaseImport"
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -0,0 +1,134 @@
|
||||||
|
<template>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<el-tooltip class="item" effect="dark" content="导入用例" placement="right">
|
||||||
|
<el-button type="info" icon="el-icon-upload2" size="mini" circle
|
||||||
|
@click="dialogVisible = true"></el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<el-dialog width="30%" title="导入测试用例" :visible.sync="dialogVisible"
|
||||||
|
@close="init">
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
<el-link type="primary" class="download-template">下载模版</el-link>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
<el-upload
|
||||||
|
class="upload-demo"
|
||||||
|
:action="'/test/case/import/' + projectId"
|
||||||
|
:on-preview="handlePreview"
|
||||||
|
multiple
|
||||||
|
:limit="1"
|
||||||
|
:on-exceed="handleExceed"
|
||||||
|
:beforeUpload="UploadValidate"
|
||||||
|
:on-success="handleSuccess"
|
||||||
|
:on-error="handleError"
|
||||||
|
:file-list="fileList">
|
||||||
|
<template v-slot:trigger>
|
||||||
|
<el-button size="mini" type="success" plain>点击上传</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-slot:tip>
|
||||||
|
<div class="el-upload__tip">只能上传xls/xlsx文件,且不超过20M</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row>
|
||||||
|
<ul>
|
||||||
|
<li v-for="errFile in errList" :key="errFile.rowNum">
|
||||||
|
{{errFile.errMsg}}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import ElUploadList from "element-ui/packages/upload/src/upload-list";
|
||||||
|
export default {
|
||||||
|
name: "TestCaseImport",
|
||||||
|
components: {ElUploadList},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogVisible: false,
|
||||||
|
fileList: [],
|
||||||
|
errList: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
projectId: {
|
||||||
|
type: String
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handlePreview(file) {
|
||||||
|
console.log("init");
|
||||||
|
this.init();
|
||||||
|
},
|
||||||
|
handleExceed(files, fileList) {
|
||||||
|
this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件`);
|
||||||
|
},
|
||||||
|
UploadValidate(file) {
|
||||||
|
var suffix =file.name.substring(file.name.lastIndexOf('.') + 1);
|
||||||
|
if (suffix != 'xls' && suffix != 'xlsx') {
|
||||||
|
this.$message({
|
||||||
|
message: '上传文件只能是 xls、xlsx格式!',
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.size / 1024 / 1024 > 20) {
|
||||||
|
this.$message({
|
||||||
|
message: '上传文件大小不能超过 20MB!',
|
||||||
|
type: 'warning'
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
handleSuccess(response) {
|
||||||
|
let res = response.data;
|
||||||
|
if (res.success) {
|
||||||
|
this.$message.success("导入成功!");
|
||||||
|
this.dialogVisible = false;
|
||||||
|
this.$emit("refresh");
|
||||||
|
} else {
|
||||||
|
this.errList = res.errList;
|
||||||
|
}
|
||||||
|
this.fileList = [];
|
||||||
|
},
|
||||||
|
handleError(err, file, fileList) {
|
||||||
|
this.$message.error(err.message);
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
this.fileList = [];
|
||||||
|
this.errList = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.download-template {
|
||||||
|
padding-top: 0px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
|
@ -4,12 +4,21 @@
|
||||||
<el-card v-loading="result.loading">
|
<el-card v-loading="result.loading">
|
||||||
<template v-slot:header>
|
<template v-slot:header>
|
||||||
<div>
|
<div>
|
||||||
<el-row type="flex" justify="space-between" align="middle">
|
<el-row type="flex" justify="start" align="middle">
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<span class="title">{{$t('test_track.test_case')}}</span>
|
<span class="title">{{$t('test_track.test_case')}}</span>
|
||||||
<ms-create-box :tips="$t('test_track.create')" :exec="testCaseCreate"/>
|
<ms-create-box :tips="$t('test_track.create')" :exec="testCaseCreate"/>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="1" :offset="12">
|
||||||
|
<test-case-import :projectId="currentProject == null? null : currentProject.id"
|
||||||
|
@refresh="refresh"/>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="1">
|
||||||
|
<test-case-export/>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="5">
|
<el-col :span="5">
|
||||||
<span class="search">
|
<span class="search">
|
||||||
<el-input type="text" size="small" :placeholder="$t('load_test.search_by_name')"
|
<el-input type="text" size="small" :placeholder="$t('load_test.search_by_name')"
|
||||||
|
@ -105,10 +114,12 @@
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import MsCreateBox from '../../../settings/CreateBox';
|
import MsCreateBox from '../../../settings/CreateBox';
|
||||||
|
import TestCaseImport from '../components/TestCaseImport';
|
||||||
|
import TestCaseExport from '../components/TestCaseExport';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "TestCaseList",
|
name: "TestCaseList",
|
||||||
components: {MsCreateBox},
|
components: {MsCreateBox, TestCaseImport, TestCaseExport},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
result: {},
|
result: {},
|
||||||
|
@ -195,6 +206,9 @@
|
||||||
type: 'success'
|
type: 'success'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
refresh() {
|
||||||
|
this.$emit('refresh');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue