Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MeterSphere
metersphere
提交
916b3811
M
metersphere
项目概览
MeterSphere
/
metersphere
上一次同步 大约 3 年
通知
25
Star
1
Fork
1
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
metersphere
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
916b3811
编写于
5月 10, 2021
作者:
S
song-tianyang
提交者:
BugKing
5月 13, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refactor: 优化功能案例导入
功能案例导入增加自定义ID的判断、增加忽略错误继续导入功能
上级
64ea3e7b
变更
12
隐藏空白更改
内联
并排
Showing
12 changed file
with
751 addition
and
22 deletion
+751
-22
backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java
...io/metersphere/api/service/ApiTestEnvironmentService.java
+46
-0
backend/src/main/java/io/metersphere/excel/handler/FunctionCaseTemplateWriteHandler.java
...phere/excel/handler/FunctionCaseTemplateWriteHandler.java
+87
-0
backend/src/main/java/io/metersphere/excel/listener/TestCaseDataIgnoreErrorListener.java
...phere/excel/listener/TestCaseDataIgnoreErrorListener.java
+338
-0
backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java
...in/java/io/metersphere/excel/utils/EasyExcelExporter.java
+16
-0
backend/src/main/java/io/metersphere/service/SystemParameterService.java
...n/java/io/metersphere/service/SystemParameterService.java
+8
-0
backend/src/main/java/io/metersphere/track/controller/TestCaseController.java
...a/io/metersphere/track/controller/TestCaseController.java
+7
-0
backend/src/main/java/io/metersphere/track/service/TestCaseService.java
...in/java/io/metersphere/track/service/TestCaseService.java
+105
-15
backend/src/main/java/io/metersphere/xmind/XmindCaseParser.java
...d/src/main/java/io/metersphere/xmind/XmindCaseParser.java
+47
-0
frontend/src/business/components/track/case/components/TestCaseImport.vue
...iness/components/track/case/components/TestCaseImport.vue
+91
-7
frontend/src/i18n/en-US.js
frontend/src/i18n/en-US.js
+2
-0
frontend/src/i18n/zh-CN.js
frontend/src/i18n/zh-CN.js
+2
-0
frontend/src/i18n/zh-TW.js
frontend/src/i18n/zh-TW.js
+2
-0
未找到文件。
backend/src/main/java/io/metersphere/api/service/ApiTestEnvironmentService.java
浏览文件 @
916b3811
...
...
@@ -7,8 +7,11 @@ import io.metersphere.base.domain.ApiTestEnvironmentExample;
import
io.metersphere.base.domain.ApiTestEnvironmentWithBLOBs
;
import
io.metersphere.base.mapper.ApiTestEnvironmentMapper
;
import
io.metersphere.commons.exception.MSException
;
import
io.metersphere.commons.utils.CommonBeanFactory
;
import
io.metersphere.controller.request.EnvironmentRequest
;
import
io.metersphere.dto.BaseSystemConfigDTO
;
import
io.metersphere.i18n.Translator
;
import
io.metersphere.service.SystemParameterService
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
...
...
@@ -90,6 +93,18 @@ public class ApiTestEnvironmentService {
* @return
*/
public
synchronized
ApiTestEnvironmentWithBLOBs
getMockEnvironmentByProjectId
(
String
projectId
,
String
protocal
,
String
baseUrl
)
{
//创建的时候检查当前站点
SystemParameterService
systemParameterService
=
CommonBeanFactory
.
getBean
(
SystemParameterService
.
class
);
BaseSystemConfigDTO
baseSystemConfigDTO
=
systemParameterService
.
getBaseInfo
();
if
(
baseSystemConfigDTO
!=
null
&&
StringUtils
.
isNotEmpty
(
baseSystemConfigDTO
.
getUrl
()))
{
baseUrl
=
baseSystemConfigDTO
.
getUrl
();
if
(
baseUrl
.
startsWith
(
"http:"
))
{
protocal
=
"http"
;
}
else
if
(
baseUrl
.
startsWith
(
"https:"
))
{
protocal
=
"https"
;
}
}
String
apiName
=
MockConfigStaticData
.
MOCK_EVN_NAME
;
ApiTestEnvironmentWithBLOBs
returnModel
=
null
;
ApiTestEnvironmentExample
example
=
new
ApiTestEnvironmentExample
();
...
...
@@ -117,6 +132,21 @@ public class ApiTestEnvironmentService {
JSONArray
conditions
=
httpObj
.
getJSONArray
(
"conditions"
);
if
(
conditions
.
isEmpty
())
{
needUpdate
=
true
;
}
else
{
for
(
int
i
=
0
;
i
<
conditions
.
size
();
i
++)
{
JSONObject
obj
=
conditions
.
getJSONObject
(
i
);
String
socket
=
url
;
if
(
socket
.
startsWith
(
"http://"
))
{
socket
=
socket
.
substring
(
7
);
}
else
if
(
socket
.
startsWith
(
"https://"
))
{
socket
=
socket
.
substring
(
8
);
}
if
(!
obj
.
containsKey
(
"socket"
)
||
!
StringUtils
.
equals
(
socket
,
String
.
valueOf
(
obj
.
get
(
"socket"
))))
{
needUpdate
=
true
;
}
else
if
(!
obj
.
containsKey
(
"protocol"
)
||
!
StringUtils
.
equals
(
protocal
,
String
.
valueOf
(
obj
.
get
(
"protocol"
))))
{
needUpdate
=
true
;
}
}
}
}
}
...
...
@@ -228,4 +258,20 @@ public class ApiTestEnvironmentService {
return
blobs
;
}
public
void
checkMockEvnInfoByBaseUrl
(
String
baseUrl
)
{
List
<
ApiTestEnvironmentWithBLOBs
>
allEvnList
=
this
.
selectByExampleWithBLOBs
(
null
);
for
(
ApiTestEnvironmentWithBLOBs
model
:
allEvnList
)
{
if
(
StringUtils
.
equals
(
model
.
getName
(),
MockConfigStaticData
.
MOCK_EVN_NAME
))
{
String
protocal
=
""
;
if
(
baseUrl
.
startsWith
(
"http:"
))
{
protocal
=
"http"
;
}
else
if
(
baseUrl
.
startsWith
(
"https:"
))
{
protocal
=
"https"
;
}
model
=
this
.
checkMockEvnIsRightful
(
model
,
protocal
,
model
.
getProjectId
(),
model
.
getName
(),
baseUrl
);
}
}
}
}
backend/src/main/java/io/metersphere/excel/handler/FunctionCaseTemplateWriteHandler.java
0 → 100644
浏览文件 @
916b3811
package
io.metersphere.excel.handler
;
import
com.alibaba.excel.write.metadata.holder.WriteSheetHolder
;
import
com.alibaba.excel.write.metadata.holder.WriteTableHolder
;
import
com.alibaba.excel.write.style.row.AbstractRowHeightStyleStrategy
;
import
io.metersphere.i18n.Translator
;
import
org.apache.poi.ss.usermodel.Comment
;
import
org.apache.poi.ss.usermodel.Drawing
;
import
org.apache.poi.ss.usermodel.Row
;
import
org.apache.poi.ss.usermodel.Sheet
;
import
org.apache.poi.xssf.usermodel.XSSFClientAnchor
;
import
org.apache.poi.xssf.usermodel.XSSFRichTextString
;
/**
* @author song.tianyang
* @Date 2021/5/7 2:17 下午
* @Description
*/
public
class
FunctionCaseTemplateWriteHandler
extends
AbstractRowHeightStyleStrategy
{
@Override
public
void
beforeRowCreate
(
WriteSheetHolder
writeSheetHolder
,
WriteTableHolder
writeTableHolder
,
Integer
rowIndex
,
Integer
relativeRowIndex
,
Boolean
isHead
)
{
}
@Override
public
void
afterRowCreate
(
WriteSheetHolder
writeSheetHolder
,
WriteTableHolder
writeTableHolder
,
Row
row
,
Integer
relativeRowIndex
,
Boolean
isHead
)
{
}
@Override
public
void
afterRowDispose
(
WriteSheetHolder
writeSheetHolder
,
WriteTableHolder
writeTableHolder
,
Row
row
,
Integer
relativeRowIndex
,
Boolean
isHead
)
{
super
.
afterRowDispose
(
writeSheetHolder
,
writeTableHolder
,
row
,
relativeRowIndex
,
isHead
);
if
(
isHead
)
{
Sheet
sheet
=
writeSheetHolder
.
getSheet
();
Drawing
<?>
drawingPatriarch
=
sheet
.
createDrawingPatriarch
();
// 在第一行 第3列创建一个批注
Comment
comment1
=
drawingPatriarch
.
createCellComment
(
new
XSSFClientAnchor
(
0
,
0
,
0
,
0
,
(
short
)
2
,
0
,
(
short
)
3
,
1
));
// 输入批注信息
comment1
.
setString
(
new
XSSFRichTextString
(
Translator
.
get
(
"do_not_modify_header_order"
)
+
","
+
Translator
.
get
(
"num_needed_modify_testcase"
)
+
","
+
Translator
.
get
(
"num_needless_create_testcase"
)));
// 在第一行 第4列创建一个批注
Comment
comment2
=
drawingPatriarch
.
createCellComment
(
new
XSSFClientAnchor
(
0
,
0
,
0
,
0
,
(
short
)
3
,
0
,
(
short
)
3
,
1
));
// 输入批注信息
comment2
.
setString
(
new
XSSFRichTextString
(
Translator
.
get
(
"module_created_automatically"
)));
// 在第一行 第5列创建一个批注
Comment
comment3
=
drawingPatriarch
.
createCellComment
(
new
XSSFClientAnchor
(
0
,
0
,
0
,
0
,
(
short
)
4
,
0
,
(
short
)
3
,
1
));
// 输入批注信息
comment3
.
setString
(
new
XSSFRichTextString
(
Translator
.
get
(
"options"
)
+
"(functional、performance、api)"
));
// 在第一行 第6列创建一个批注
Comment
comment4
=
drawingPatriarch
.
createCellComment
(
new
XSSFClientAnchor
(
0
,
0
,
0
,
0
,
(
short
)
5
,
0
,
(
short
)
3
,
1
));
// 输入批注信息
comment4
.
setString
(
new
XSSFRichTextString
(
Translator
.
get
(
"please_input_workspace_member"
)));
// 在第一行 第7列创建一个批注
Comment
comment5
=
drawingPatriarch
.
createCellComment
(
new
XSSFClientAnchor
(
0
,
0
,
0
,
0
,
(
short
)
6
,
0
,
(
short
)
3
,
1
));
// 输入批注信息
comment5
.
setString
(
new
XSSFRichTextString
(
Translator
.
get
(
"options"
)
+
"(P0、P1、P2、P3)"
));
// 在第一行 第8列创建一个批注
Comment
comment6
=
drawingPatriarch
.
createCellComment
(
new
XSSFClientAnchor
(
0
,
0
,
0
,
0
,
(
short
)
7
,
0
,
(
short
)
3
,
1
));
// 输入批注信息
comment6
.
setString
(
new
XSSFRichTextString
(
Translator
.
get
(
"tag_tip_pattern"
)));
// 将批注添加到单元格对象中
sheet
.
getRow
(
0
).
getCell
(
1
).
setCellComment
(
comment1
);
sheet
.
getRow
(
0
).
getCell
(
1
).
setCellComment
(
comment2
);
sheet
.
getRow
(
0
).
getCell
(
1
).
setCellComment
(
comment3
);
sheet
.
getRow
(
0
).
getCell
(
1
).
setCellComment
(
comment4
);
sheet
.
getRow
(
0
).
getCell
(
1
).
setCellComment
(
comment5
);
sheet
.
getRow
(
0
).
getCell
(
1
).
setCellComment
(
comment6
);
}
}
@Override
protected
void
setHeadColumnHeight
(
Row
row
,
int
relativeRowIndex
)
{
}
@Override
protected
void
setContentColumnHeight
(
Row
row
,
int
relativeRowIndex
)
{
}
}
backend/src/main/java/io/metersphere/excel/listener/TestCaseDataIgnoreErrorListener.java
0 → 100644
浏览文件 @
916b3811
package
io.metersphere.excel.listener
;
import
com.alibaba.excel.context.AnalysisContext
;
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.CommonBeanFactory
;
import
io.metersphere.commons.utils.LogUtil
;
import
io.metersphere.excel.domain.ExcelErrData
;
import
io.metersphere.excel.domain.TestCaseExcelData
;
import
io.metersphere.excel.utils.ExcelValidateHelper
;
import
io.metersphere.i18n.Translator
;
import
io.metersphere.track.service.TestCaseService
;
import
org.apache.commons.lang3.StringUtils
;
import
java.util.*
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.stream.Collectors
;
import
java.util.stream.Stream
;
public
class
TestCaseDataIgnoreErrorListener
extends
EasyExcelListener
<
TestCaseExcelData
>
{
private
TestCaseService
testCaseService
;
private
String
projectId
;
protected
List
<
TestCaseExcelData
>
updateList
=
new
ArrayList
<>();
//存储待更新用例的集合
protected
boolean
isUpdated
=
false
;
//判断是否更新过用例,将会传给前端
Set
<
String
>
testCaseNames
;
Set
<
String
>
userIds
;
public
boolean
isUpdated
()
{
return
isUpdated
;
}
public
TestCaseDataIgnoreErrorListener
(
Class
clazz
,
String
projectId
,
Set
<
String
>
testCaseNames
,
Set
<
String
>
userIds
)
{
this
.
clazz
=
clazz
;
this
.
testCaseService
=
(
TestCaseService
)
CommonBeanFactory
.
getBean
(
"testCaseService"
);
this
.
projectId
=
projectId
;
this
.
testCaseNames
=
testCaseNames
;
this
.
userIds
=
userIds
;
}
@Override
public
String
validate
(
TestCaseExcelData
data
,
String
errMsg
)
{
String
nodePath
=
data
.
getNodePath
();
StringBuilder
stringBuilder
=
new
StringBuilder
(
errMsg
);
//校验”所属模块"
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
&&
StringUtils
.
equals
(
nodes
[
i
].
trim
(),
""
))
{
stringBuilder
.
append
(
Translator
.
get
(
"module_not_null"
)
+
"; "
);
break
;
}
}
//增加字数校验,每一层不能超过100字
for
(
int
i
=
0
;
i
<
nodes
.
length
;
i
++)
{
String
nodeStr
=
nodes
[
i
];
if
(
StringUtils
.
isNotEmpty
(
nodeStr
))
{
if
(
nodeStr
.
trim
().
length
()
>
100
)
{
stringBuilder
.
append
(
Translator
.
get
(
"module"
)
+
Translator
.
get
(
"test_track.length_less_than"
)
+
"100:"
+
nodeStr
);
break
;
}
}
}
}
//校验维护人
if
(!
userIds
.
contains
(
data
.
getMaintainer
()))
{
stringBuilder
.
append
(
Translator
.
get
(
"user_not_exists"
)
+
":"
+
data
.
getMaintainer
()
+
"; "
);
}
/*
校验Excel中是否有ID
有的话校验ID是否已在当前项目中存在,存在则更新用例,
不存在则继续校验看是否重复,不重复则新建用例。
*/
if
(
null
!=
data
.
getNum
())
{
//当前读取的数据有ID
if
(
null
!=
testCaseService
.
checkIdExist
(
data
.
getNum
(),
projectId
))
{
//该ID在当前项目中存在
//如果前面所经过的校验都没报错
if
(
StringUtils
.
isEmpty
(
stringBuilder
))
{
updateList
.
add
(
data
);
//将当前数据存入更新列表
stringBuilder
.
append
(
"update_testcase"
);
//该信息用于在invoke方法中判断是否该更新用例
}
return
stringBuilder
.
toString
();
}
else
{
/*
该ID在当前数据库中不存在,应当继续校验用例是否重复,
在下面的校验过程中,num的值会被用于判断是否重复,所以应当先设置为null
*/
data
.
setNum
(
null
);
}
}
/*
校验用例
*/
if
(
testCaseNames
.
contains
(
data
.
getName
()))
{
TestCaseWithBLOBs
testCase
=
new
TestCaseWithBLOBs
();
BeanUtils
.
copyBean
(
testCase
,
data
);
testCase
.
setProjectId
(
projectId
);
String
steps
=
getSteps
(
data
);
testCase
.
setSteps
(
steps
);
boolean
dbExist
=
testCaseService
.
exist
(
testCase
);
boolean
excelExist
=
false
;
if
(
dbExist
)
{
// db exist
stringBuilder
.
append
(
Translator
.
get
(
"test_case_already_exists"
)
+
":"
+
data
.
getName
()
+
"; "
);
}
else
{
// @Data 重写了 equals 和 hashCode 方法
excelExist
=
excelDataList
.
contains
(
data
);
}
if
(
excelExist
)
{
// excel exist
stringBuilder
.
append
(
Translator
.
get
(
"test_case_already_exists_excel"
)
+
":"
+
data
.
getName
()
+
"; "
);
}
else
{
excelDataList
.
add
(
data
);
}
}
else
{
testCaseNames
.
add
(
data
.
getName
());
excelDataList
.
add
(
data
);
}
return
stringBuilder
.
toString
();
}
@Override
public
void
saveData
()
{
if
(!(
list
.
size
()
==
0
))
{
Collections
.
reverse
(
list
);
//因为saveImportData里面是先分配最大的ID,这个ID应该先发给list中最后的数据,所以要reverse
List
<
TestCaseWithBLOBs
>
result
=
list
.
stream
()
.
map
(
item
->
this
.
convert2TestCase
(
item
))
.
collect
(
Collectors
.
toList
());
testCaseService
.
saveImportData
(
result
,
projectId
);
this
.
isUpdated
=
true
;
}
if
(!(
updateList
.
size
()
==
0
))
{
List
<
TestCaseWithBLOBs
>
result2
=
updateList
.
stream
()
.
map
(
item
->
this
.
convert2TestCaseForUpdate
(
item
))
.
collect
(
Collectors
.
toList
());
testCaseService
.
updateImportDataCarryId
(
result2
,
projectId
);
this
.
isUpdated
=
true
;
updateList
.
clear
();
}
}
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
());
testCase
.
setCustomNum
(
data
.
getCustomNum
());
String
nodePath
=
data
.
getNodePath
();
if
(!
nodePath
.
startsWith
(
"/"
))
{
nodePath
=
"/"
+
nodePath
;
}
if
(
nodePath
.
endsWith
(
"/"
))
{
nodePath
=
nodePath
.
substring
(
0
,
nodePath
.
length
()
-
1
);
}
testCase
.
setNodePath
(
nodePath
);
//将标签设置为前端可解析的格式
String
modifiedTags
=
modifyTagPattern
(
data
);
testCase
.
setTags
(
modifiedTags
);
if
(
StringUtils
.
isNotBlank
(
data
.
getStepModel
())
&&
StringUtils
.
equals
(
data
.
getStepModel
(),
TestCaseConstants
.
StepModel
.
TEXT
.
name
()))
{
testCase
.
setStepDescription
(
data
.
getStepDesc
());
testCase
.
setExpectedResult
(
data
.
getStepResult
());
}
else
{
String
steps
=
getSteps
(
data
);
testCase
.
setSteps
(
steps
);
}
return
testCase
;
}
/**
* 将Excel中的数据对象转换为用于更新操作的用例数据对象,
*
* @param data
* @return
*/
private
TestCaseWithBLOBs
convert2TestCaseForUpdate
(
TestCaseExcelData
data
)
{
TestCaseWithBLOBs
testCase
=
new
TestCaseWithBLOBs
();
BeanUtils
.
copyBean
(
testCase
,
data
);
testCase
.
setProjectId
(
this
.
projectId
);
testCase
.
setUpdateTime
(
System
.
currentTimeMillis
());
//调整nodePath格式
String
nodePath
=
data
.
getNodePath
();
if
(!
nodePath
.
startsWith
(
"/"
))
{
nodePath
=
"/"
+
nodePath
;
}
if
(
nodePath
.
endsWith
(
"/"
))
{
nodePath
=
nodePath
.
substring
(
0
,
nodePath
.
length
()
-
1
);
}
testCase
.
setNodePath
(
nodePath
);
String
steps
=
getSteps
(
data
);
testCase
.
setSteps
(
steps
);
//将标签设置为前端可解析的格式
String
modifiedTags
=
modifyTagPattern
(
data
);
testCase
.
setTags
(
modifiedTags
);
return
testCase
;
}
/**
* 调整tags格式,便于前端进行解析。
* 例如对于:标签1,标签2。将调整为:["标签1","标签2"]。
*/
public
String
modifyTagPattern
(
TestCaseExcelData
data
)
{
String
tags
=
data
.
getTags
();
try
{
if
(
StringUtils
.
isNotBlank
(
tags
))
{
JSONArray
.
parse
(
tags
);
return
tags
;
}
return
"[]"
;
}
catch
(
Exception
e
)
{
if
(
tags
!=
null
)
{
Stream
<
String
>
stringStream
=
Arrays
.
stream
(
tags
.
split
(
"[,;,;]"
));
//当标签值以中英文的逗号和分号分隔时才能正确解析
List
<
String
>
tagList
=
stringStream
.
map
(
tag
->
tag
=
"\""
+
tag
+
"\""
)
.
collect
(
Collectors
.
toList
());
String
modifiedTags
=
StringUtils
.
join
(
tagList
,
","
);
modifiedTags
=
"["
+
modifiedTags
+
"]"
;
return
modifiedTags
;
}
else
{
return
"[]"
;
}
}
}
public
String
getSteps
(
TestCaseExcelData
data
)
{
JSONArray
jsonArray
=
new
JSONArray
();
String
[]
stepDesc
=
new
String
[
1
];
String
[]
stepRes
=
new
String
[
1
];
if
(
data
.
getStepDesc
()
!=
null
)
{
stepDesc
=
data
.
getStepDesc
().
split
(
"\r\n|\n"
);
}
else
{
stepDesc
[
0
]
=
""
;
}
if
(
data
.
getStepResult
()
!=
null
)
{
stepRes
=
data
.
getStepResult
().
split
(
"\r\n|\n"
);
}
else
{
stepRes
[
0
]
=
""
;
}
String
pattern
=
"(^\\d+)(\\.)?"
;
int
index
=
stepDesc
.
length
>
stepRes
.
length
?
stepDesc
.
length
:
stepRes
.
length
;
for
(
int
i
=
0
;
i
<
index
;
i
++)
{
// 保持插入顺序,判断用例是否有相同的steps
JSONObject
step
=
new
JSONObject
(
true
);
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
);
}
return
jsonArray
.
toJSONString
();
}
@Override
public
void
invoke
(
TestCaseExcelData
testCaseExcelData
,
AnalysisContext
analysisContext
)
{
String
errMsg
;
Integer
rowIndex
=
analysisContext
.
readRowHolder
().
getRowIndex
();
String
updateMsg
=
"update_testcase"
;
try
{
//根据excel数据实体中的javax.validation + 正则表达式来校验excel数据
errMsg
=
ExcelValidateHelper
.
validateEntity
(
testCaseExcelData
);
//自定义校验规则
errMsg
=
validate
(
testCaseExcelData
,
errMsg
);
}
catch
(
NoSuchFieldException
e
)
{
errMsg
=
Translator
.
get
(
"parse_data_error"
);
LogUtil
.
error
(
e
.
getMessage
(),
e
);
}
if
(!
StringUtils
.
isEmpty
(
errMsg
))
{
//如果errMsg只有"update testcase",说明用例待更新
if
(!
errMsg
.
equals
(
updateMsg
))
{
ExcelErrData
excelErrData
=
new
ExcelErrData
(
testCaseExcelData
,
rowIndex
,
Translator
.
get
(
"number"
)
+
" "
+
rowIndex
+
" "
+
Translator
.
get
(
"row"
)
+
Translator
.
get
(
"error"
)
+
":"
+
errMsg
);
errList
.
add
(
excelErrData
);
}
}
else
{
list
.
add
(
testCaseExcelData
);
}
if
(
list
.
size
()
>
BATCH_COUNT
)
{
saveData
();
list
.
clear
();
}
}
}
backend/src/main/java/io/metersphere/excel/utils/EasyExcelExporter.java
浏览文件 @
916b3811
package
io.metersphere.excel.utils
;
import
com.alibaba.excel.EasyExcel
;
import
com.alibaba.excel.write.handler.WriteHandler
;
import
com.alibaba.excel.write.metadata.style.WriteCellStyle
;
import
com.alibaba.excel.write.style.HorizontalCellStyleStrategy
;
import
io.metersphere.commons.utils.LogUtil
;
...
...
@@ -38,4 +39,19 @@ public class EasyExcelExporter {
}
}
public
void
exportByCustomWriteHandler
(
HttpServletResponse
response
,
List
data
,
String
fileName
,
String
sheetName
,
WriteHandler
writeHandler
)
{
response
.
setContentType
(
"application/vnd.ms-excel"
);
response
.
setCharacterEncoding
(
"utf-8"
);
try
{
response
.
setHeader
(
"Content-disposition"
,
"attachment;filename="
+
URLEncoder
.
encode
(
fileName
,
"UTF-8"
)
+
".xlsx"
);
EasyExcel
.
write
(
response
.
getOutputStream
(),
this
.
clazz
).
registerWriteHandler
(
writeHandler
).
sheet
(
sheetName
).
doWrite
(
data
);
}
catch
(
UnsupportedEncodingException
e
)
{
LogUtil
.
error
(
e
.
getMessage
(),
e
);
throw
new
ExcelException
(
"Utf-8 encoding is not supported"
);
}
catch
(
IOException
e
)
{
LogUtil
.
error
(
e
.
getMessage
(),
e
);
throw
new
ExcelException
(
"IO exception"
);
}
}
}
backend/src/main/java/io/metersphere/service/SystemParameterService.java
浏览文件 @
916b3811
package
io.metersphere.service
;
import
io.metersphere.api.service.ApiTestEnvironmentService
;
import
io.metersphere.base.domain.*
;
import
io.metersphere.base.mapper.SystemHeaderMapper
;
import
io.metersphere.base.mapper.SystemParameterMapper
;
...
...
@@ -42,6 +43,8 @@ public class SystemParameterService {
private
ExtSystemParameterMapper
extSystemParameterMapper
;
@Resource
private
SystemHeaderMapper
systemHeaderMapper
;
@Resource
private
ApiTestEnvironmentService
apiTestEnvironmentService
;
public
String
searchEmail
()
{
return
extSystemParameterMapper
.
email
();
...
...
@@ -237,6 +240,7 @@ public class SystemParameterService {
public
void
saveBaseInfo
(
List
<
SystemParameter
>
parameters
)
{
SystemParameterExample
example
=
new
SystemParameterExample
();
parameters
.
forEach
(
param
->
{
// 去掉路径最后的 /
param
.
setParamValue
(
StringUtils
.
removeEnd
(
param
.
getParamValue
(),
"/"
));
...
...
@@ -247,6 +251,10 @@ public class SystemParameterService {
systemParameterMapper
.
insert
(
param
);
}
example
.
clear
();
if
(
StringUtils
.
equals
(
param
.
getParamKey
(),
"base.url"
))
{
apiTestEnvironmentService
.
checkMockEvnInfoByBaseUrl
(
param
.
getParamValue
());
}
});
}
...
...
backend/src/main/java/io/metersphere/track/controller/TestCaseController.java
浏览文件 @
916b3811
...
...
@@ -154,6 +154,13 @@ public class TestCaseController {
return
testCaseService
.
testCaseImport
(
file
,
projectId
,
userId
);
}
@PostMapping
(
"/importIgnoreError/{projectId}/{userId}"
)
@RequiresRoles
(
value
=
{
RoleConstants
.
TEST_USER
,
RoleConstants
.
TEST_MANAGER
},
logical
=
Logical
.
OR
)
public
ExcelResponse
testCaseImportIgnoreError
(
MultipartFile
file
,
@PathVariable
String
projectId
,
@PathVariable
String
userId
)
{
checkPermissionService
.
checkProjectOwner
(
projectId
);
return
testCaseService
.
testCaseImportIgnoreError
(
file
,
projectId
,
userId
);
}
@GetMapping
(
"/export/template"
)
@RequiresRoles
(
value
=
{
RoleConstants
.
TEST_USER
,
RoleConstants
.
TEST_MANAGER
},
logical
=
Logical
.
OR
)
public
void
testCaseTemplateExport
(
HttpServletResponse
response
)
{
...
...
backend/src/main/java/io/metersphere/track/service/TestCaseService.java
浏览文件 @
916b3811
...
...
@@ -19,6 +19,8 @@ import io.metersphere.excel.domain.ExcelErrData;
import
io.metersphere.excel.domain.ExcelResponse
;
import
io.metersphere.excel.domain.TestCaseExcelData
;
import
io.metersphere.excel.domain.TestCaseExcelDataFactory
;
import
io.metersphere.excel.handler.FunctionCaseTemplateWriteHandler
;
import
io.metersphere.excel.listener.TestCaseDataIgnoreErrorListener
;
import
io.metersphere.excel.listener.TestCaseDataListener
;
import
io.metersphere.excel.utils.EasyExcelExporter
;
import
io.metersphere.i18n.Translator
;
...
...
@@ -97,7 +99,8 @@ public class TestCaseService {
TestCaseFileMapper
testCaseFileMapper
;
@Resource
TestCaseTestMapper
testCaseTestMapper
;
private
void
setNode
(
TestCaseWithBLOBs
testCase
){
private
void
setNode
(
TestCaseWithBLOBs
testCase
)
{
if
(
StringUtils
.
isEmpty
(
testCase
.
getNodeId
())
||
"default-module"
.
equals
(
testCase
.
getNodeId
()))
{
TestCaseNodeExample
example
=
new
TestCaseNodeExample
();
example
.
createCriteria
().
andProjectIdEqualTo
(
testCase
.
getProjectId
()).
andNameEqualTo
(
"默认模块"
);
...
...
@@ -224,7 +227,7 @@ public class TestCaseService {
String
remark
=
tc
.
getRemark
();
String
prerequisite
=
tc
.
getPrerequisite
();
if
(
StringUtils
.
equals
(
steps
,
caseSteps
)
&&
StringUtils
.
equals
(
remark
,
caseRemark
)
&&
StringUtils
.
equals
(
prerequisite
,
casePrerequisite
))
{
//MSException.throwException(Translator.get("test_case_already_exists"));
//MSException.throwException(Translator.get("test_case_already_exists"));
return
tc
;
}
}
...
...
@@ -487,7 +490,7 @@ public class TestCaseService {
testcase
.
setCustomNum
(
String
.
valueOf
(
number
));
}
testcase
.
setReviewStatus
(
TestCaseReviewStatus
.
Prepare
.
name
());
mapper
.
insert
(
testcase
);
mapper
.
insert
(
testcase
);
});
}
sqlSession
.
flushStatements
();
...
...
@@ -516,6 +519,7 @@ public class TestCaseService {
/**
* 把Excel中带ID的数据更新到数据库
* feat(测试跟踪):通过Excel导入导出时有ID字段,可通过Excel导入来更新用例。 (#1727)
*
* @param testCases
* @param projectId
*/
...
...
@@ -555,8 +559,9 @@ public class TestCaseService {
public
void
testCaseTemplateExport
(
HttpServletResponse
response
)
{
try
{
EasyExcelExporter
easyExcelExporter
=
new
EasyExcelExporter
(
new
TestCaseExcelDataFactory
().
getExcelDataByLocal
());
easyExcelExporter
.
export
(
response
,
generateExportTemplate
(),
Translator
.
get
(
"test_case_import_template_name"
),
Translator
.
get
(
"test_case_import_template_sheet"
));
FunctionCaseTemplateWriteHandler
handler
=
new
FunctionCaseTemplateWriteHandler
();
easyExcelExporter
.
exportByCustomWriteHandler
(
response
,
generateExportTemplate
(),
Translator
.
get
(
"test_case_import_template_name"
),
Translator
.
get
(
"test_case_import_template_sheet"
),
handler
);
}
catch
(
Exception
e
)
{
MSException
.
throwException
(
e
);
}
...
...
@@ -619,16 +624,15 @@ public class TestCaseService {
}
list
.
add
(
new
TestCaseExcelData
());
TestCaseExcelData
explain
=
new
TestCaseExcelData
();
explain
.
setName
(
Translator
.
get
(
"do_not_modify_header_order"
)
+
","
+
Translator
.
get
(
"num_needed_modify_testcase"
)
+
","
+
Translator
.
get
(
"num_needless_create_testcase"
));
explain
.
setNodePath
(
Translator
.
get
(
"module_created_automatically"
));
explain
.
setType
(
Translator
.
get
(
"options"
)
+
"(functional、performance、api)"
);
explain
.
setTags
(
Translator
.
get
(
"tag_tip_pattern"
));
// explain.setMethod(Translator.get("options") + "(manual、auto)");
explain
.
setPriority
(
Translator
.
get
(
"options"
)
+
"(P0、P1、P2、P3)"
);
explain
.
setMaintainer
(
Translator
.
get
(
"please_input_workspace_member"
));
list
.
add
(
explain
);
// TestCaseExcelData explain = new TestCaseExcelData();
// explain.setName(Translator.get("do_not_modify_header_order") + "," + Translator.get("num_needed_modify_testcase") + "," + Translator.get("num_needless_create_testcase"));
// explain.setNodePath(Translator.get("module_created_automatically"));
// explain.setType(Translator.get("options") + "(functional、performance、api)");
// explain.setTags(Translator.get("tag_tip_pattern"));
//// explain.setMethod(Translator.get("options") + "(manual、auto)");
// explain.setPriority(Translator.get("options") + "(P0、P1、P2、P3)");
// explain.setMaintainer(Translator.get("please_input_workspace_member"));
// list.add(explain);
return
list
;
}
...
...
@@ -1020,9 +1024,95 @@ public class TestCaseService {
/**
* 更新项目下用例的CustomNum值
*
* @param projectId 项目ID
*/
public
void
updateTestCaseCustomNumByProjectId
(
String
projectId
)
{
extTestCaseMapper
.
updateTestCaseCustomNumByProjectId
(
projectId
);
}
public
ExcelResponse
testCaseImportIgnoreError
(
MultipartFile
multipartFile
,
String
projectId
,
String
userId
)
{
ExcelResponse
excelResponse
=
new
ExcelResponse
();
boolean
isUpdated
=
false
;
//判断是否更新了用例
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
());
List
<
ExcelErrData
<
TestCaseExcelData
>>
errList
=
null
;
if
(
multipartFile
==
null
)
{
MSException
.
throwException
(
Translator
.
get
(
"upload_fail"
));
}
if
(
multipartFile
.
getOriginalFilename
().
endsWith
(
".xmind"
))
{
try
{
XmindCaseParser
xmindParser
=
new
XmindCaseParser
(
this
,
userId
,
projectId
,
testCaseNames
);
errList
=
xmindParser
.
parse
(
multipartFile
);
if
(
CollectionUtils
.
isEmpty
(
xmindParser
.
getNodePaths
())
&&
CollectionUtils
.
isEmpty
(
xmindParser
.
getTestCase
())
&&
CollectionUtils
.
isEmpty
(
xmindParser
.
getUpdateTestCase
()))
{
if
(
errList
==
null
)
{
errList
=
new
ArrayList
<>();
}
ExcelErrData
excelErrData
=
new
ExcelErrData
(
null
,
1
,
Translator
.
get
(
"upload_fail"
)
+
":"
+
Translator
.
get
(
"upload_content_is_null"
));
errList
.
add
(
excelErrData
);
excelResponse
.
setErrList
(
errList
);
}
List
<
TestCaseWithBLOBs
>
continueCaseList
=
xmindParser
.
getContinueValidatedCase
();
if
(
CollectionUtils
.
isNotEmpty
(
continueCaseList
)
||
CollectionUtils
.
isNotEmpty
(
xmindParser
.
getUpdateTestCase
()))
{
if
(
CollectionUtils
.
isNotEmpty
(
xmindParser
.
getUpdateTestCase
()))
{
continueCaseList
.
removeAll
(
xmindParser
.
getUpdateTestCase
());
this
.
updateImportData
(
xmindParser
.
getUpdateTestCase
(),
projectId
);
}
List
<
String
>
nodePathList
=
xmindParser
.
getValidatedNodePath
();
if
(
CollectionUtils
.
isNotEmpty
(
nodePathList
))
{
testCaseNodeService
.
createNodes
(
nodePathList
,
projectId
);
}
if
(
CollectionUtils
.
isNotEmpty
(
continueCaseList
))
{
Collections
.
reverse
(
continueCaseList
);
this
.
saveImportData
(
continueCaseList
,
projectId
);
}
}
xmindParser
.
clear
();
}
catch
(
Exception
e
)
{
LogUtil
.
error
(
e
.
getMessage
(),
e
);
MSException
.
throwException
(
e
.
getMessage
());
}
}
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
());
try
{
//根据本地语言环境选择用哪种数据对象进行存放读取的数据
Class
clazz
=
new
TestCaseExcelDataFactory
().
getExcelDataByLocal
();
TestCaseDataIgnoreErrorListener
easyExcelListener
=
new
TestCaseDataIgnoreErrorListener
(
clazz
,
projectId
,
testCaseNames
,
userIds
);
//读取excel数据
EasyExcelFactory
.
read
(
multipartFile
.
getInputStream
(),
clazz
,
easyExcelListener
).
sheet
().
doRead
();
errList
=
easyExcelListener
.
getErrList
();
isUpdated
=
easyExcelListener
.
isUpdated
();
}
catch
(
Exception
e
)
{
LogUtil
.
error
(
e
.
getMessage
(),
e
);
MSException
.
throwException
(
e
.
getMessage
());
}
}
//如果包含错误信息就导出错误信息
if
(!
errList
.
isEmpty
())
{
excelResponse
.
setSuccess
(
false
);
excelResponse
.
setErrList
(
errList
);
excelResponse
.
setIsUpdated
(
isUpdated
);
}
else
{
excelResponse
.
setSuccess
(
true
);
}
return
excelResponse
;
}
}
backend/src/main/java/io/metersphere/xmind/XmindCaseParser.java
浏览文件 @
916b3811
...
...
@@ -59,6 +59,10 @@ public class XmindCaseParser {
*/
private
List
<
String
>
nodePaths
;
private
List
<
TestCaseWithBLOBs
>
continueValidatedCase
;
private
List
<
String
>
errorPath
;
public
XmindCaseParser
(
TestCaseService
testCaseService
,
String
userId
,
String
projectId
,
Set
<
String
>
testCaseNames
)
{
this
.
testCaseService
=
testCaseService
;
this
.
maintainer
=
userId
;
...
...
@@ -69,11 +73,14 @@ public class XmindCaseParser {
compartDatas
=
new
ArrayList
<>();
process
=
new
DetailUtil
();
nodePaths
=
new
ArrayList
<>();
continueValidatedCase
=
new
ArrayList
<>();
errorPath
=
new
ArrayList
<>();
}
private
static
final
String
TC_REGEX
=
"(?:tc:|tc:|tc)"
;
private
static
final
String
PC_REGEX
=
"(?:pc:|pc:|pc)"
;
private
static
final
String
RC_REGEX
=
"(?:rc:|rc:|rc)"
;
private
static
final
String
ID_REGEX
=
"(?:id:|id:|id)"
;
private
static
final
String
TAG_REGEX
=
"(?:tag:|tag:|tag)"
;
public
void
clear
()
{
...
...
@@ -126,6 +133,7 @@ public class XmindCaseParser {
* 验证用例的合规性
*/
private
boolean
validate
(
TestCaseWithBLOBs
data
)
{
boolean
validatePass
=
true
;
String
nodePath
=
data
.
getNodePath
();
if
(!
nodePath
.
startsWith
(
"/"
))
{
nodePath
=
"/"
+
nodePath
;
...
...
@@ -137,27 +145,41 @@ public class XmindCaseParser {
if
(
data
.
getName
().
length
()
>
200
)
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"test_case"
)
+
Translator
.
get
(
"test_track.length_less_than"
)
+
"200"
,
nodePath
+
data
.
getName
());
}
if
(!
StringUtils
.
isEmpty
(
nodePath
))
{
String
[]
nodes
=
nodePath
.
split
(
"/"
);
if
(
nodes
.
length
>
TestCaseConstants
.
MAX_NODE_DEPTH
+
1
)
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"test_case_node_level_tip"
)
+
TestCaseConstants
.
MAX_NODE_DEPTH
+
Translator
.
get
(
"test_case_node_level"
),
nodePath
);
if
(!
errorPath
.
contains
(
nodePath
))
{
errorPath
.
add
(
nodePath
);
}
}
for
(
int
i
=
0
;
i
<
nodes
.
length
;
i
++)
{
if
(
i
!=
0
&&
StringUtils
.
equals
(
nodes
[
i
].
trim
(),
""
))
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"test_case"
)
+
Translator
.
get
(
"module_not_null"
),
nodePath
+
data
.
getName
());
if
(!
errorPath
.
contains
(
nodePath
))
{
errorPath
.
add
(
nodePath
);
}
break
;
}
else
if
(
nodes
[
i
].
trim
().
length
()
>
100
)
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"module"
)
+
Translator
.
get
(
"test_track.length_less_than"
)
+
"100 "
,
nodes
[
i
].
trim
());
if
(!
errorPath
.
contains
(
nodePath
))
{
errorPath
.
add
(
nodePath
);
}
break
;
}
}
}
if
(
StringUtils
.
equals
(
data
.
getType
(),
TestCaseConstants
.
Type
.
Functional
.
getValue
())
&&
StringUtils
.
equals
(
data
.
getMethod
(),
TestCaseConstants
.
Method
.
Auto
.
getValue
()))
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"functional_method_tip"
),
nodePath
+
data
.
getName
());
}
...
...
@@ -176,9 +198,11 @@ public class XmindCaseParser {
// 用例等级和用例性质处理
if
(!
priorityList
.
contains
(
data
.
getPriority
()))
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"test_case_priority"
)
+
Translator
.
get
(
"incorrect_format"
),
nodePath
+
data
.
getName
());
}
if
(
data
.
getType
()
==
null
)
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"test_case_type"
)
+
Translator
.
get
(
"incorrect_format"
),
nodePath
+
data
.
getName
());
}
...
...
@@ -186,9 +210,13 @@ public class XmindCaseParser {
TestCaseExcelData
compartData
=
new
TestCaseExcelData
();
BeanUtils
.
copyBean
(
compartData
,
data
);
if
(
compartDatas
.
contains
(
compartData
))
{
validatePass
=
false
;
process
.
add
(
Translator
.
get
(
"test_case_already_exists_excel"
),
nodePath
+
"/"
+
compartData
.
getName
());
}
compartDatas
.
add
(
compartData
);
if
(
validatePass
)
{
this
.
continueValidatedCase
.
add
(
data
);
}
return
true
;
}
...
...
@@ -305,6 +333,7 @@ public class XmindCaseParser {
List
<
Attached
>
steps
=
new
LinkedList
<>();
StringBuilder
rc
=
new
StringBuilder
();
List
<
String
>
tags
=
new
LinkedList
<>();
StringBuilder
customId
=
new
StringBuilder
();
if
(
attacheds
!=
null
&&
!
attacheds
.
isEmpty
())
{
attacheds
.
forEach
(
item
->
{
if
(
isAvailable
(
item
.
getTitle
(),
PC_REGEX
))
{
...
...
@@ -314,12 +343,15 @@ public class XmindCaseParser {
rc
.
append
(
"\n"
);
}
else
if
(
isAvailable
(
item
.
getTitle
(),
TAG_REGEX
))
{
tags
.
add
(
replace
(
item
.
getTitle
(),
TAG_REGEX
));
}
else
if
(
isAvailable
(
item
.
getTitle
(),
ID_REGEX
))
{
customId
.
append
(
replace
(
item
.
getTitle
(),
ID_REGEX
));
}
else
{
steps
.
add
(
item
);
}
});
}
testCase
.
setRemark
(
rc
.
toString
());
testCase
.
setCustomNum
(
customId
.
toString
());
testCase
.
setTags
(
JSON
.
toJSONString
(
tags
));
testCase
.
setSteps
(
this
.
getSteps
(
steps
));
// 校验合规性
...
...
@@ -364,8 +396,23 @@ public class XmindCaseParser {
//检查目录合规性
this
.
validate
();
}
catch
(
Exception
ex
)
{
ex
.
printStackTrace
();
return
process
.
parse
(
ex
.
getMessage
());
}
return
process
.
parse
();
}
public
List
<
TestCaseWithBLOBs
>
getContinueValidatedCase
()
{
return
this
.
continueValidatedCase
;
}
public
List
<
String
>
getValidatedNodePath
()
{
List
<
String
>
returnPathList
=
new
ArrayList
<>(
nodePaths
);
if
(
CollectionUtils
.
isNotEmpty
(
returnPathList
))
{
if
(
CollectionUtils
.
isNotEmpty
(
errorPath
))
{
returnPathList
.
removeAll
(
errorPath
);
}
}
return
returnPathList
;
}
}
frontend/src/business/components/track/case/components/TestCaseImport.vue
浏览文件 @
916b3811
...
...
@@ -2,13 +2,13 @@
<el-dialog
class=
"testcase-import"
:title=
"$t('test_track.case.import.case_import')"
:visible.sync=
"dialogVisible"
@
close=
"close"
>
<el-tabs
v-model=
"activeName"
simple
>
<el-tabs
v-model=
"activeName"
@
tab-click=
"clickTabs"
simple
>
<el-tab-pane
:label=
"$t('test_track.case.import.excel_title')"
name=
"excelImport"
>
<el-row>
<el-link
type=
"primary"
class=
"download-template"
@
click=
"downloadTemplate"
>
{{
$t
(
'
test_track.case.import.download_template
'
)
}}
>
{{
$t
(
'
test_track.case.import.download_template
'
)
}}
</el-link>
</el-row>
<el-row>
...
...
@@ -39,10 +39,20 @@
<el-row>
<ul>
<li
v-for=
"errFile in errList"
:key=
"errFile.rowNum"
>
{{
errFile.errMsg
}}
{{
errFile.errMsg
}}
</li>
</ul>
</el-row>
<el-row
style=
"text-align: right"
v-if=
"showExcelImportContinueBtn"
>
<div
style=
"margin-right: 20px;margin-bottom: 10px;"
>
<el-checkbox
v-model=
"uploadIgnoreError"
>
{{ $t('test_track.case.import.ignore_error') }}
</el-checkbox>
</div>
<el-button
type=
"primary"
@
click=
"uploadContinue(false)"
>
{{ $t('test_track.case.import.continue_upload') }}
</el-button>
<el-button
@
click=
"close"
>
{{ $t('commons.cancel') }}
</el-button>
</el-row>
</el-tab-pane>
<!-- Xmind 导入 -->
<el-tab-pane
:label=
"$t('test_track.case.import.xmind_title')"
name=
"xmindImport"
style=
"border: 0px"
>
...
...
@@ -98,10 +108,18 @@
<el-row>
<ul>
<li
v-for=
"errFile in xmindErrList"
:key=
"errFile.rowNum"
>
{{
errFile.errMsg
}}
{{
errFile.errMsg
}}
</li>
</ul>
</el-row>
<el-row
style=
"text-align: right"
v-if=
"showXmindImportContinueBtn"
>
<div
style=
"margin-right: 20px;margin-bottom: 10px;"
>
<el-checkbox
v-model=
"uploadXmindIgnoreError"
>
{{ $t('test_track.case.import.ignore_error') }}
</el-checkbox>
</div>
<el-button
type=
"primary"
@
click=
"uploadContinue(true)"
>
{{ $t('test_track.case.import.continue_upload') }}
</el-button>
<el-button
@
click=
"close"
>
{{ $t('commons.cancel') }}
</el-button>
</el-row>
</el-tab-pane>
</el-tabs>
...
...
@@ -124,16 +142,34 @@
activeName
:
'
excelImport
'
,
dialogVisible
:
false
,
fileList
:
[],
lastXmindFile
:
null
,
lastExcelFile
:
null
,
errList
:
[],
xmindErrList
:
[],
isLoading
:
false
,
isUpdated
:
false
isUpdated
:
false
,
clickTabsName
:
""
,
showExcelImportContinueBtn
:
false
,
showXmindImportContinueBtn
:
false
,
uploadIgnoreError
:
false
,
uploadXmindIgnoreError
:
false
,
}
},
created
()
{
this
.
showExcelImportContinueBtn
=
false
;
this
.
showXmindImportContinueBtn
=
false
;
},
activated
()
{
this
.
showExcelImportContinueBtn
=
false
;
this
.
showXmindImportContinueBtn
=
false
;
},
methods
:
{
handleExceed
(
files
,
fileList
)
{
this
.
$warning
(
this
.
$t
(
'
test_track.case.import.upload_limit_count
'
));
},
clickTabs
(
tab
,
event
)
{
this
.
clickTabsName
=
tab
.
name
;
},
uploadValidate
(
file
)
{
let
suffix
=
file
.
name
.
substring
(
file
.
name
.
lastIndexOf
(
'
.
'
)
+
1
);
if
(
suffix
!=
'
xls
'
&&
suffix
!=
'
xlsx
'
)
{
...
...
@@ -179,11 +215,13 @@
removeGoBackListener
(
this
.
close
);
this
.
dialogVisible
=
false
;
this
.
fileList
=
[];
this
.
showExcelImportContinueBtn
=
false
;
this
.
showXmindImportContinueBtn
=
false
;
this
.
errList
=
[];
this
.
xmindErrList
=
[];
//通过excel导入更新过数据的话就刷新页面
if
(
this
.
isUpdated
===
true
){
if
(
this
.
isUpdated
===
true
)
{
this
.
$emit
(
"
refreshAll
"
);
this
.
isUpdated
=
false
;
}
...
...
@@ -202,8 +240,10 @@
});
},
upload
(
file
)
{
this
.
isLoading
=
fals
e
;
this
.
lastExcelFile
=
file
.
fil
e
;
this
.
fileList
.
push
(
file
.
file
);
this
.
isLoading
=
false
;
let
user
=
JSON
.
parse
(
localStorage
.
getItem
(
TokenKey
));
this
.
result
=
this
.
$fileUpload
(
'
/test/case/import/
'
+
this
.
projectId
+
'
/
'
+
user
.
id
,
file
.
file
,
null
,
{},
response
=>
{
...
...
@@ -212,18 +252,57 @@
this
.
$success
(
this
.
$t
(
'
test_track.case.import.success
'
));
this
.
dialogVisible
=
false
;
this
.
$emit
(
"
refreshAll
"
);
this
.
lastXmindFile
=
null
;
this
.
lastExcelFile
=
null
;
this
.
showExcelImportContinueBtn
=
false
;
this
.
showXmindImportContinueBtn
=
false
;
}
else
{
this
.
errList
=
res
.
errList
;
this
.
isUpdated
=
res
.
isUpdated
;
this
.
showExcelImportContinueBtn
=
true
;
}
this
.
fileList
=
[];
},
erro
=>
{
this
.
fileList
=
[];
this
.
lastXmindFile
=
null
;
this
.
lastExcelFile
=
null
;
});
},
uploadContinue
(
isImportXmind
)
{
this
.
isLoading
=
false
;
let
user
=
JSON
.
parse
(
localStorage
.
getItem
(
TokenKey
));
let
file
=
null
;
if
(
isImportXmind
)
{
this
.
uploadXmindIgnoreError
=
true
;
file
=
this
.
lastXmindFile
;
}
else
{
this
.
uploadIgnoreError
=
true
;
file
=
this
.
lastExcelFile
;
}
this
.
result
=
this
.
$fileUpload
(
'
/test/case/importIgnoreError/
'
+
this
.
projectId
+
'
/
'
+
user
.
id
,
file
,
null
,
{},
response
=>
{
let
res
=
response
.
data
;
this
.
$success
(
this
.
$t
(
'
test_track.case.import.success
'
));
this
.
dialogVisible
=
false
;
this
.
$emit
(
"
refreshAll
"
);
this
.
fileList
=
[];
this
.
lastXmindFile
=
null
;
this
.
lastExcelFile
=
null
;
this
.
showExcelImportContinueBtn
=
false
;
this
.
showXmindImportContinueBtn
=
false
;
this
.
uploadIgnoreError
=
false
;
this
.
uploadXmindIgnoreError
=
false
;
},
erro
=>
{
this
.
fileList
=
[];
this
.
lastXmindFile
=
null
;
this
.
lastExcelFile
=
null
;
this
.
uploadIgnoreError
=
false
;
this
.
uploadXmindIgnoreError
=
false
;
});
},
uploadXmind
(
file
)
{
this
.
isLoading
=
false
;
this
.
fileList
.
push
(
file
.
file
);
this
.
lastXmindFile
=
file
.
file
;
let
user
=
JSON
.
parse
(
localStorage
.
getItem
(
TokenKey
));
this
.
result
=
this
.
$fileUpload
(
'
/test/case/import/
'
+
this
.
projectId
+
'
/
'
+
user
.
id
,
file
.
file
,
null
,
{},
response
=>
{
...
...
@@ -232,8 +311,13 @@
this
.
$success
(
this
.
$t
(
'
test_track.case.import.success
'
));
this
.
dialogVisible
=
false
;
this
.
$emit
(
"
refreshAll
"
);
this
.
lastXmindFile
=
null
;
this
.
lastExcelFile
=
null
;
this
.
showExcelImportContinueBtn
=
false
;
this
.
showXmindImportContinueBtn
=
false
;
}
else
{
this
.
xmindErrList
=
res
.
errList
;
this
.
showXmindImportContinueBtn
=
true
;
}
this
.
fileList
=
[];
},
erro
=>
{
...
...
frontend/src/i18n/en-US.js
浏览文件 @
916b3811
...
...
@@ -1342,6 +1342,8 @@ export default {
xmind_title
:
"
Xmind
"
,
import_desc
:
"
Import instructions
"
,
import_file
:
"
upload files
"
,
ignore_error
:
"
Ignore errors
"
,
continue_upload
:
"
Upload continue
"
,
},
export
:
{
export
:
"
Export cases
"
...
...
frontend/src/i18n/zh-CN.js
浏览文件 @
916b3811
...
...
@@ -1347,6 +1347,8 @@ export default {
xmind_title
:
"
思维导图
"
,
import_desc
:
"
导入说明
"
,
import_file
:
"
上传文件
"
,
ignore_error
:
"
忽略错误
"
,
continue_upload
:
"
继续上传
"
,
},
export
:
{
export
:
"
导出用例
"
...
...
frontend/src/i18n/zh-TW.js
浏览文件 @
916b3811
...
...
@@ -1347,6 +1347,8 @@ export default {
xmind_title
:
"
思維導圖
"
,
import_desc
:
"
導入說明
"
,
import_file
:
"
上傳文件
"
,
ignore_error
:
"
忽略錯誤
"
,
continue_upload
:
"
繼續上傳
"
,
},
export
:
{
export
:
"
導出用例
"
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录