Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
MeterSphere
metersphere
提交
155b340e
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,发现更多精彩内容 >>
提交
155b340e
编写于
4月 15, 2020
作者:
Q
q4speed
浏览文件
操作
浏览文件
下载
差异文件
Merge remote-tracking branch 'origin/dev' into dev
上级
f9f1809f
98379ac9
变更
41
隐藏空白更改
内联
并排
Showing
41 changed file
with
1721 addition
and
288 deletion
+1721
-288
backend/pom.xml
backend/pom.xml
+7
-4
backend/src/main/java/io/metersphere/base/domain/LoadTestReportDetail.java
...java/io/metersphere/base/domain/LoadTestReportDetail.java
+27
-0
backend/src/main/java/io/metersphere/base/domain/LoadTestReportDetailExample.java
.../metersphere/base/domain/LoadTestReportDetailExample.java
+270
-0
backend/src/main/java/io/metersphere/base/domain/ZaleniumTest.java
...rc/main/java/io/metersphere/base/domain/ZaleniumTest.java
+0
-194
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportDetailMapper.java
...o/metersphere/base/mapper/LoadTestReportDetailMapper.java
+35
-0
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportDetailMapper.xml
...io/metersphere/base/mapper/LoadTestReportDetailMapper.xml
+194
-0
backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestReportDetailMapper.java
...sphere/base/mapper/ext/ExtLoadTestReportDetailMapper.java
+7
-0
backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestReportDetailMapper.xml
...rsphere/base/mapper/ext/ExtLoadTestReportDetailMapper.xml
+9
-0
backend/src/main/java/io/metersphere/commons/constants/PerformanceTestStatus.java
.../metersphere/commons/constants/PerformanceTestStatus.java
+5
-0
backend/src/main/java/io/metersphere/commons/constants/TestCaseConstants.java
...a/io/metersphere/commons/constants/TestCaseConstants.java
+5
-0
backend/src/main/java/io/metersphere/controller/PerformanceTestController.java
.../io/metersphere/controller/PerformanceTestController.java
+1
-5
backend/src/main/java/io/metersphere/controller/TestCaseController.java
...in/java/io/metersphere/controller/TestCaseController.java
+7
-5
backend/src/main/java/io/metersphere/engine/AbstractEngine.java
...d/src/main/java/io/metersphere/engine/AbstractEngine.java
+2
-2
backend/src/main/java/io/metersphere/engine/docker/DockerTestEngine.java
...n/java/io/metersphere/engine/docker/DockerTestEngine.java
+2
-1
backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java
...o/metersphere/engine/kubernetes/KubernetesTestEngine.java
+2
-1
backend/src/main/java/io/metersphere/excel/domain/ExcelErrData.java
...c/main/java/io/metersphere/excel/domain/ExcelErrData.java
+42
-0
backend/src/main/java/io/metersphere/excel/domain/ExcelResponse.java
.../main/java/io/metersphere/excel/domain/ExcelResponse.java
+25
-0
backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java
...n/java/io/metersphere/excel/domain/TestCaseExcelData.java
+137
-0
backend/src/main/java/io/metersphere/excel/listener/EasyExcelListener.java
...java/io/metersphere/excel/listener/EasyExcelListener.java
+140
-0
backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java
...a/io/metersphere/excel/listener/TestCaseDataListener.java
+138
-0
backend/src/main/java/io/metersphere/excel/util/ExcelValidateHelper.java
...n/java/io/metersphere/excel/util/ExcelValidateHelper.java
+32
-0
backend/src/main/java/io/metersphere/exception/ExcelImportException.java
...n/java/io/metersphere/exception/ExcelImportException.java
+18
-0
backend/src/main/java/io/metersphere/report/JtlResolver.java
backend/src/main/java/io/metersphere/report/JtlResolver.java
+30
-16
backend/src/main/java/io/metersphere/service/PerformanceTestService.java
...n/java/io/metersphere/service/PerformanceTestService.java
+30
-20
backend/src/main/java/io/metersphere/service/ReportService.java
...d/src/main/java/io/metersphere/service/ReportService.java
+3
-3
backend/src/main/java/io/metersphere/service/TestCaseNodeService.java
...main/java/io/metersphere/service/TestCaseNodeService.java
+125
-2
backend/src/main/java/io/metersphere/service/TestCaseService.java
...src/main/java/io/metersphere/service/TestCaseService.java
+79
-7
backend/src/main/resources/db/migration/V2__metersphere_ddl.sql
...d/src/main/resources/db/migration/V2__metersphere_ddl.sql
+9
-0
backend/src/main/resources/i18n/en-US.json
backend/src/main/resources/i18n/en-US.json
+2
-1
backend/src/main/resources/i18n/zh-CN.json
backend/src/main/resources/i18n/zh-CN.json
+2
-1
backend/src/test/java/io/metersphere/ReportContentTests.java
backend/src/test/java/io/metersphere/ReportContentTests.java
+61
-0
frontend/src/business/components/common/head/HeaderUser.vue
frontend/src/business/components/common/head/HeaderUser.vue
+3
-3
frontend/src/business/components/performance/report/PerformanceReportView.vue
...s/components/performance/report/PerformanceReportView.vue
+8
-3
frontend/src/business/components/performance/report/components/TestOverview.vue
...components/performance/report/components/TestOverview.vue
+81
-10
frontend/src/business/components/performance/test/PerformanceTestPlan.vue
...iness/components/performance/test/PerformanceTestPlan.vue
+4
-1
frontend/src/business/components/track/case/TestCase.vue
frontend/src/business/components/track/case/TestCase.vue
+1
-0
frontend/src/business/components/track/case/components/TestCaseExport.vue
...iness/components/track/case/components/TestCaseExport.vue
+17
-0
frontend/src/business/components/track/case/components/TestCaseImport.vue
...iness/components/track/case/components/TestCaseImport.vue
+134
-0
frontend/src/business/components/track/case/components/TestCaseList.vue
...usiness/components/track/case/components/TestCaseList.vue
+16
-2
frontend/src/common/js/utils.js
frontend/src/common/js/utils.js
+9
-0
frontend/src/login/Login.vue
frontend/src/login/Login.vue
+2
-7
未找到文件。
backend/pom.xml
浏览文件 @
155b340e
...
...
@@ -36,10 +36,6 @@
<artifactId>
spring-boot-starter-tomcat
</artifactId>
<groupId>
org.springframework.boot
</groupId>
</exclusion>
<exclusion>
<artifactId>
hibernate-validator
</artifactId>
<groupId>
org.hibernate.validator
</groupId>
</exclusion>
</exclusions>
</dependency>
...
...
@@ -133,6 +129,13 @@
<version>
5.1
</version>
</dependency>
<!-- easyexcel -->
<dependency>
<groupId>
com.alibaba
</groupId>
<artifactId>
easyexcel
</artifactId>
<version>
2.1.7
</version>
</dependency>
</dependencies>
<build>
...
...
backend/src/main/java/io/metersphere/base/domain/LoadTestReportDetail.java
0 → 100644
浏览文件 @
155b340e
package
io.metersphere.base.domain
;
import
java.io.Serializable
;
public
class
LoadTestReportDetail
implements
Serializable
{
private
String
reportId
;
private
String
content
;
private
static
final
long
serialVersionUID
=
1L
;
public
String
getReportId
()
{
return
reportId
;
}
public
void
setReportId
(
String
reportId
)
{
this
.
reportId
=
reportId
==
null
?
null
:
reportId
.
trim
();
}
public
String
getContent
()
{
return
content
;
}
public
void
setContent
(
String
content
)
{
this
.
content
=
content
==
null
?
null
:
content
.
trim
();
}
}
\ No newline at end of file
backend/src/main/java/io/metersphere/base/domain/LoadTestReportDetailExample.java
0 → 100644
浏览文件 @
155b340e
package
io.metersphere.base.domain
;
import
java.util.ArrayList
;
import
java.util.List
;
public
class
LoadTestReportDetailExample
{
protected
String
orderByClause
;
protected
boolean
distinct
;
protected
List
<
Criteria
>
oredCriteria
;
public
LoadTestReportDetailExample
()
{
oredCriteria
=
new
ArrayList
<
Criteria
>();
}
public
void
setOrderByClause
(
String
orderByClause
)
{
this
.
orderByClause
=
orderByClause
;
}
public
String
getOrderByClause
()
{
return
orderByClause
;
}
public
void
setDistinct
(
boolean
distinct
)
{
this
.
distinct
=
distinct
;
}
public
boolean
isDistinct
()
{
return
distinct
;
}
public
List
<
Criteria
>
getOredCriteria
()
{
return
oredCriteria
;
}
public
void
or
(
Criteria
criteria
)
{
oredCriteria
.
add
(
criteria
);
}
public
Criteria
or
()
{
Criteria
criteria
=
createCriteriaInternal
();
oredCriteria
.
add
(
criteria
);
return
criteria
;
}
public
Criteria
createCriteria
()
{
Criteria
criteria
=
createCriteriaInternal
();
if
(
oredCriteria
.
size
()
==
0
)
{
oredCriteria
.
add
(
criteria
);
}
return
criteria
;
}
protected
Criteria
createCriteriaInternal
()
{
Criteria
criteria
=
new
Criteria
();
return
criteria
;
}
public
void
clear
()
{
oredCriteria
.
clear
();
orderByClause
=
null
;
distinct
=
false
;
}
protected
abstract
static
class
GeneratedCriteria
{
protected
List
<
Criterion
>
criteria
;
protected
GeneratedCriteria
()
{
super
();
criteria
=
new
ArrayList
<
Criterion
>();
}
public
boolean
isValid
()
{
return
criteria
.
size
()
>
0
;
}
public
List
<
Criterion
>
getAllCriteria
()
{
return
criteria
;
}
public
List
<
Criterion
>
getCriteria
()
{
return
criteria
;
}
protected
void
addCriterion
(
String
condition
)
{
if
(
condition
==
null
)
{
throw
new
RuntimeException
(
"Value for condition cannot be null"
);
}
criteria
.
add
(
new
Criterion
(
condition
));
}
protected
void
addCriterion
(
String
condition
,
Object
value
,
String
property
)
{
if
(
value
==
null
)
{
throw
new
RuntimeException
(
"Value for "
+
property
+
" cannot be null"
);
}
criteria
.
add
(
new
Criterion
(
condition
,
value
));
}
protected
void
addCriterion
(
String
condition
,
Object
value1
,
Object
value2
,
String
property
)
{
if
(
value1
==
null
||
value2
==
null
)
{
throw
new
RuntimeException
(
"Between values for "
+
property
+
" cannot be null"
);
}
criteria
.
add
(
new
Criterion
(
condition
,
value1
,
value2
));
}
public
Criteria
andReportIdIsNull
()
{
addCriterion
(
"report_id is null"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdIsNotNull
()
{
addCriterion
(
"report_id is not null"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdEqualTo
(
String
value
)
{
addCriterion
(
"report_id ="
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdNotEqualTo
(
String
value
)
{
addCriterion
(
"report_id <>"
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdGreaterThan
(
String
value
)
{
addCriterion
(
"report_id >"
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdGreaterThanOrEqualTo
(
String
value
)
{
addCriterion
(
"report_id >="
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdLessThan
(
String
value
)
{
addCriterion
(
"report_id <"
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdLessThanOrEqualTo
(
String
value
)
{
addCriterion
(
"report_id <="
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdLike
(
String
value
)
{
addCriterion
(
"report_id like"
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdNotLike
(
String
value
)
{
addCriterion
(
"report_id not like"
,
value
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdIn
(
List
<
String
>
values
)
{
addCriterion
(
"report_id in"
,
values
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdNotIn
(
List
<
String
>
values
)
{
addCriterion
(
"report_id not in"
,
values
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdBetween
(
String
value1
,
String
value2
)
{
addCriterion
(
"report_id between"
,
value1
,
value2
,
"reportId"
);
return
(
Criteria
)
this
;
}
public
Criteria
andReportIdNotBetween
(
String
value1
,
String
value2
)
{
addCriterion
(
"report_id not between"
,
value1
,
value2
,
"reportId"
);
return
(
Criteria
)
this
;
}
}
public
static
class
Criteria
extends
GeneratedCriteria
{
protected
Criteria
()
{
super
();
}
}
public
static
class
Criterion
{
private
String
condition
;
private
Object
value
;
private
Object
secondValue
;
private
boolean
noValue
;
private
boolean
singleValue
;
private
boolean
betweenValue
;
private
boolean
listValue
;
private
String
typeHandler
;
public
String
getCondition
()
{
return
condition
;
}
public
Object
getValue
()
{
return
value
;
}
public
Object
getSecondValue
()
{
return
secondValue
;
}
public
boolean
isNoValue
()
{
return
noValue
;
}
public
boolean
isSingleValue
()
{
return
singleValue
;
}
public
boolean
isBetweenValue
()
{
return
betweenValue
;
}
public
boolean
isListValue
()
{
return
listValue
;
}
public
String
getTypeHandler
()
{
return
typeHandler
;
}
protected
Criterion
(
String
condition
)
{
super
();
this
.
condition
=
condition
;
this
.
typeHandler
=
null
;
this
.
noValue
=
true
;
}
protected
Criterion
(
String
condition
,
Object
value
,
String
typeHandler
)
{
super
();
this
.
condition
=
condition
;
this
.
value
=
value
;
this
.
typeHandler
=
typeHandler
;
if
(
value
instanceof
List
<?>)
{
this
.
listValue
=
true
;
}
else
{
this
.
singleValue
=
true
;
}
}
protected
Criterion
(
String
condition
,
Object
value
)
{
this
(
condition
,
value
,
null
);
}
protected
Criterion
(
String
condition
,
Object
value
,
Object
secondValue
,
String
typeHandler
)
{
super
();
this
.
condition
=
condition
;
this
.
value
=
value
;
this
.
secondValue
=
secondValue
;
this
.
typeHandler
=
typeHandler
;
this
.
betweenValue
=
true
;
}
protected
Criterion
(
String
condition
,
Object
value
,
Object
secondValue
)
{
this
(
condition
,
value
,
secondValue
,
null
);
}
}
}
\ No newline at end of file
backend/src/main/java/io/metersphere/base/domain/ZaleniumTest.java
已删除
100644 → 0
浏览文件 @
f9f1809f
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
;
}
}
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportDetailMapper.java
0 → 100644
浏览文件 @
155b340e
package
io.metersphere.base.mapper
;
import
io.metersphere.base.domain.LoadTestReportDetail
;
import
io.metersphere.base.domain.LoadTestReportDetailExample
;
import
org.apache.ibatis.annotations.Param
;
import
java.util.List
;
public
interface
LoadTestReportDetailMapper
{
long
countByExample
(
LoadTestReportDetailExample
example
);
int
deleteByExample
(
LoadTestReportDetailExample
example
);
int
deleteByPrimaryKey
(
String
reportId
);
int
insert
(
LoadTestReportDetail
record
);
int
insertSelective
(
LoadTestReportDetail
record
);
List
<
LoadTestReportDetail
>
selectByExampleWithBLOBs
(
LoadTestReportDetailExample
example
);
List
<
LoadTestReportDetail
>
selectByExample
(
LoadTestReportDetailExample
example
);
LoadTestReportDetail
selectByPrimaryKey
(
String
reportId
);
int
updateByExampleSelective
(
@Param
(
"record"
)
LoadTestReportDetail
record
,
@Param
(
"example"
)
LoadTestReportDetailExample
example
);
int
updateByExampleWithBLOBs
(
@Param
(
"record"
)
LoadTestReportDetail
record
,
@Param
(
"example"
)
LoadTestReportDetailExample
example
);
int
updateByExample
(
@Param
(
"record"
)
LoadTestReportDetail
record
,
@Param
(
"example"
)
LoadTestReportDetailExample
example
);
int
updateByPrimaryKeySelective
(
LoadTestReportDetail
record
);
int
updateByPrimaryKeyWithBLOBs
(
LoadTestReportDetail
record
);
}
\ No newline at end of file
backend/src/main/java/io/metersphere/base/mapper/LoadTestReportDetailMapper.xml
0 → 100644
浏览文件 @
155b340e
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper
namespace=
"io.metersphere.base.mapper.LoadTestReportDetailMapper"
>
<resultMap
id=
"BaseResultMap"
type=
"io.metersphere.base.domain.LoadTestReportDetail"
>
<id
column=
"report_id"
jdbcType=
"VARCHAR"
property=
"reportId"
/>
</resultMap>
<resultMap
extends=
"BaseResultMap"
id=
"ResultMapWithBLOBs"
type=
"io.metersphere.base.domain.LoadTestReportDetail"
>
<result
column=
"content"
jdbcType=
"LONGVARCHAR"
property=
"content"
/>
</resultMap>
<sql
id=
"Example_Where_Clause"
>
<where>
<foreach
collection=
"oredCriteria"
item=
"criteria"
separator=
"or"
>
<if
test=
"criteria.valid"
>
<trim
prefix=
"("
prefixOverrides=
"and"
suffix=
")"
>
<foreach
collection=
"criteria.criteria"
item=
"criterion"
>
<choose>
<when
test=
"criterion.noValue"
>
and ${criterion.condition}
</when>
<when
test=
"criterion.singleValue"
>
and ${criterion.condition} #{criterion.value}
</when>
<when
test=
"criterion.betweenValue"
>
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when
test=
"criterion.listValue"
>
and ${criterion.condition}
<foreach
close=
")"
collection=
"criterion.value"
item=
"listItem"
open=
"("
separator=
","
>
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql
id=
"Update_By_Example_Where_Clause"
>
<where>
<foreach
collection=
"example.oredCriteria"
item=
"criteria"
separator=
"or"
>
<if
test=
"criteria.valid"
>
<trim
prefix=
"("
prefixOverrides=
"and"
suffix=
")"
>
<foreach
collection=
"criteria.criteria"
item=
"criterion"
>
<choose>
<when
test=
"criterion.noValue"
>
and ${criterion.condition}
</when>
<when
test=
"criterion.singleValue"
>
and ${criterion.condition} #{criterion.value}
</when>
<when
test=
"criterion.betweenValue"
>
and ${criterion.condition} #{criterion.value} and #{criterion.secondValue}
</when>
<when
test=
"criterion.listValue"
>
and ${criterion.condition}
<foreach
close=
")"
collection=
"criterion.value"
item=
"listItem"
open=
"("
separator=
","
>
#{listItem}
</foreach>
</when>
</choose>
</foreach>
</trim>
</if>
</foreach>
</where>
</sql>
<sql
id=
"Base_Column_List"
>
report_id
</sql>
<sql
id=
"Blob_Column_List"
>
content
</sql>
<select
id=
"selectByExampleWithBLOBs"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetailExample"
resultMap=
"ResultMapWithBLOBs"
>
select
<if
test=
"distinct"
>
distinct
</if>
<include
refid=
"Base_Column_List"
/>
,
<include
refid=
"Blob_Column_List"
/>
from load_test_report_detail
<if
test=
"_parameter != null"
>
<include
refid=
"Example_Where_Clause"
/>
</if>
<if
test=
"orderByClause != null"
>
order by ${orderByClause}
</if>
</select>
<select
id=
"selectByExample"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetailExample"
resultMap=
"BaseResultMap"
>
select
<if
test=
"distinct"
>
distinct
</if>
<include
refid=
"Base_Column_List"
/>
from load_test_report_detail
<if
test=
"_parameter != null"
>
<include
refid=
"Example_Where_Clause"
/>
</if>
<if
test=
"orderByClause != null"
>
order by ${orderByClause}
</if>
</select>
<select
id=
"selectByPrimaryKey"
parameterType=
"java.lang.String"
resultMap=
"ResultMapWithBLOBs"
>
select
<include
refid=
"Base_Column_List"
/>
,
<include
refid=
"Blob_Column_List"
/>
from load_test_report_detail
where report_id = #{reportId,jdbcType=VARCHAR}
</select>
<delete
id=
"deleteByPrimaryKey"
parameterType=
"java.lang.String"
>
delete from load_test_report_detail
where report_id = #{reportId,jdbcType=VARCHAR}
</delete>
<delete
id=
"deleteByExample"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetailExample"
>
delete from load_test_report_detail
<if
test=
"_parameter != null"
>
<include
refid=
"Example_Where_Clause"
/>
</if>
</delete>
<insert
id=
"insert"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetail"
>
insert into load_test_report_detail (report_id, content)
values (#{reportId,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARCHAR})
</insert>
<insert
id=
"insertSelective"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetail"
>
insert into load_test_report_detail
<trim
prefix=
"("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"reportId != null"
>
report_id,
</if>
<if
test=
"content != null"
>
content,
</if>
</trim>
<trim
prefix=
"values ("
suffix=
")"
suffixOverrides=
","
>
<if
test=
"reportId != null"
>
#{reportId,jdbcType=VARCHAR},
</if>
<if
test=
"content != null"
>
#{content,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<select
id=
"countByExample"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetailExample"
resultType=
"java.lang.Long"
>
select count(*) from load_test_report_detail
<if
test=
"_parameter != null"
>
<include
refid=
"Example_Where_Clause"
/>
</if>
</select>
<update
id=
"updateByExampleSelective"
parameterType=
"map"
>
update load_test_report_detail
<set>
<if
test=
"record.reportId != null"
>
report_id = #{record.reportId,jdbcType=VARCHAR},
</if>
<if
test=
"record.content != null"
>
content = #{record.content,jdbcType=LONGVARCHAR},
</if>
</set>
<if
test=
"_parameter != null"
>
<include
refid=
"Update_By_Example_Where_Clause"
/>
</if>
</update>
<update
id=
"updateByExampleWithBLOBs"
parameterType=
"map"
>
update load_test_report_detail
set report_id = #{record.reportId,jdbcType=VARCHAR},
content = #{record.content,jdbcType=LONGVARCHAR}
<if
test=
"_parameter != null"
>
<include
refid=
"Update_By_Example_Where_Clause"
/>
</if>
</update>
<update
id=
"updateByExample"
parameterType=
"map"
>
update load_test_report_detail
set report_id = #{record.reportId,jdbcType=VARCHAR}
<if
test=
"_parameter != null"
>
<include
refid=
"Update_By_Example_Where_Clause"
/>
</if>
</update>
<update
id=
"updateByPrimaryKeySelective"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetail"
>
update load_test_report_detail
<set>
<if
test=
"content != null"
>
content = #{content,jdbcType=LONGVARCHAR},
</if>
</set>
where report_id = #{reportId,jdbcType=VARCHAR}
</update>
<update
id=
"updateByPrimaryKeyWithBLOBs"
parameterType=
"io.metersphere.base.domain.LoadTestReportDetail"
>
update load_test_report_detail
set content = #{content,jdbcType=LONGVARCHAR}
where report_id = #{reportId,jdbcType=VARCHAR}
</update>
</mapper>
\ No newline at end of file
backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestReportDetailMapper.java
0 → 100644
浏览文件 @
155b340e
package
io.metersphere.base.mapper.ext
;
import
org.apache.ibatis.annotations.Param
;
public
interface
ExtLoadTestReportDetailMapper
{
int
appendLine
(
@Param
(
"reportId"
)
String
id
,
@Param
(
"line"
)
String
line
);
}
backend/src/main/java/io/metersphere/base/mapper/ext/ExtLoadTestReportDetailMapper.xml
0 → 100644
浏览文件 @
155b340e
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper
namespace=
"io.metersphere.base.mapper.ext.ExtLoadTestReportDetailMapper"
>
<update
id=
"appendLine"
>
UPDATE load_test_report_detail
SET content = concat(content, #{line})
WHERE report_id = #{reportId}
</update>
</mapper>
\ No newline at end of file
backend/src/main/java/io/metersphere/commons/constants/PerformanceTestStatus.java
0 → 100644
浏览文件 @
155b340e
package
io.metersphere.commons.constants
;
public
enum
PerformanceTestStatus
{
Saved
,
Starting
,
Running
,
Completed
,
Error
}
backend/src/main/java/io/metersphere/commons/constants/Test
Statu
s.java
→
backend/src/main/java/io/metersphere/commons/constants/Test
CaseConstant
s.java
浏览文件 @
155b340e
package
io.metersphere.commons.constants
;
public
enum
TestStatu
s
{
Starting
,
Running
,
Completed
,
Error
public
class
TestCaseConstant
s
{
public
static
final
int
MAX_NODE_DEPTH
=
5
;
}
backend/src/main/java/io/metersphere/controller/PerformanceTestController.java
浏览文件 @
155b340e
...
...
@@ -4,7 +4,6 @@ import com.github.pagehelper.Page;
import
com.github.pagehelper.PageHelper
;
import
io.metersphere.base.domain.FileMetadata
;
import
io.metersphere.commons.constants.RoleConstants
;
import
io.metersphere.commons.exception.MSException
;
import
io.metersphere.commons.utils.PageUtils
;
import
io.metersphere.commons.utils.Pager
;
import
io.metersphere.controller.request.testplan.*
;
...
...
@@ -86,10 +85,7 @@ public class PerformanceTestController {
@PostMapping
(
"/run"
)
public
void
run
(
@RequestBody
RunTestPlanRequest
request
)
{
boolean
started
=
performanceTestService
.
run
(
request
);
if
(!
started
)
{
MSException
.
throwException
(
"Start engine error, please check log."
);
}
performanceTestService
.
run
(
request
);
}
@GetMapping
(
"/file/metadata/{testId}"
)
...
...
backend/src/main/java/io/metersphere/controller/TestCaseController.java
浏览文件 @
155b340e
...
...
@@ -6,14 +6,11 @@ import io.metersphere.base.domain.*;
import
io.metersphere.commons.utils.PageUtils
;
import
io.metersphere.commons.utils.Pager
;
import
io.metersphere.controller.request.testcase.QueryTestCaseRequest
;
import
io.metersphere.controller.request.testplan.QueryTestPlanRequest
;
import
io.metersphere.dto.LoadTestDTO
;
import
io.metersphere.dto.TestCaseNodeDTO
;
import
io.metersphere.dto.TestPlanCaseDTO
;
import
io.metersphere.service.TestCaseNodeService
;
import
io.metersphere.excel.domain.ExcelResponse
;
import
io.metersphere.service.TestCaseService
;
import
io.metersphere.user.SessionUtils
;
import
org.springframework.web.bind.annotation.*
;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.annotation.Resource
;
import
java.util.List
;
...
...
@@ -74,5 +71,10 @@ public class TestCaseController {
return
testCaseService
.
deleteTestCase
(
testCaseId
);
}
@PostMapping
(
"/import/{projectId}"
)
public
ExcelResponse
testCaseImport
(
MultipartFile
file
,
@PathVariable
String
projectId
){
return
testCaseService
.
testCaseImport
(
file
,
projectId
);
}
}
backend/src/main/java/io/metersphere/engine/AbstractEngine.java
浏览文件 @
155b340e
...
...
@@ -6,8 +6,8 @@ import com.alibaba.fastjson.JSONObject;
import
io.metersphere.base.domain.LoadTestWithBLOBs
;
import
io.metersphere.base.domain.TestResource
;
import
io.metersphere.base.domain.TestResourcePool
;
import
io.metersphere.commons.constants.PerformanceTestStatus
;
import
io.metersphere.commons.constants.ResourcePoolTypeEnum
;
import
io.metersphere.commons.constants.TestStatus
;
import
io.metersphere.commons.exception.MSException
;
import
io.metersphere.commons.utils.CommonBeanFactory
;
import
io.metersphere.config.JmeterProperties
;
...
...
@@ -70,7 +70,7 @@ public abstract class AbstractEngine implements Engine {
List
<
LoadTestWithBLOBs
>
loadTests
=
performanceTestService
.
selectByTestResourcePoolId
(
loadTest
.
getTestResourcePoolId
());
// 使用当前资源池正在运行的测试占用的并发数
return
loadTests
.
stream
()
.
filter
(
t
->
TestStatus
.
Running
.
name
().
equals
(
t
.
getStatus
()))
.
filter
(
t
->
Performance
TestStatus
.
Running
.
name
().
equals
(
t
.
getStatus
()))
.
map
(
this
::
getThreadNum
)
.
reduce
(
Integer:
:
sum
)
.
orElse
(
0
);
...
...
backend/src/main/java/io/metersphere/engine/docker/DockerTestEngine.java
浏览文件 @
155b340e
...
...
@@ -12,6 +12,7 @@ import io.metersphere.engine.EngineContext;
import
io.metersphere.engine.EngineFactory
;
import
io.metersphere.engine.docker.request.BaseRequest
;
import
io.metersphere.engine.docker.request.TestRequest
;
import
io.metersphere.i18n.Translator
;
import
org.springframework.web.client.RestTemplate
;
import
java.util.List
;
...
...
@@ -41,7 +42,7 @@ public class DockerTestEngine extends AbstractEngine {
.
reduce
(
Integer:
:
sum
)
.
orElse
(
0
);
if
(
threadNum
>
totalThreadNum
-
runningSumThreadNum
)
{
MSException
.
throwException
(
"Insufficient resources"
);
MSException
.
throwException
(
Translator
.
get
(
"max_thread_insufficient"
)
);
}
List
<
Integer
>
resourceRatio
=
resourceList
.
stream
()
.
filter
(
r
->
ResourceStatusEnum
.
VALID
.
name
().
equals
(
r
.
getStatus
()))
...
...
backend/src/main/java/io/metersphere/engine/kubernetes/KubernetesTestEngine.java
浏览文件 @
155b340e
...
...
@@ -15,6 +15,7 @@ import io.metersphere.engine.kubernetes.crds.jmeter.Jmeter;
import
io.metersphere.engine.kubernetes.crds.jmeter.JmeterSpec
;
import
io.metersphere.engine.kubernetes.provider.ClientCredential
;
import
io.metersphere.engine.kubernetes.provider.KubernetesProvider
;
import
io.metersphere.i18n.Translator
;
import
org.apache.commons.collections.MapUtils
;
import
java.util.HashMap
;
...
...
@@ -43,7 +44,7 @@ public class KubernetesTestEngine extends AbstractEngine {
Integer
maxConcurrency
=
clientCredential
.
getMaxConcurrency
();
// 当前测试需要的并发数大于剩余的并发数报错
if
(
threadNum
>
maxConcurrency
-
sumThreadNum
)
{
MSException
.
throwException
(
"Insufficient resources"
);
MSException
.
throwException
(
Translator
.
get
(
"max_thread_insufficient"
)
);
}
try
{
EngineContext
context
=
EngineFactory
.
createContext
(
loadTest
,
threadNum
,
this
.
getStartTime
(),
this
.
getReportId
());
...
...
backend/src/main/java/io/metersphere/excel/domain/ExcelErrData.java
0 → 100644
浏览文件 @
155b340e
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
;
}
}
\ No newline at end of file
backend/src/main/java/io/metersphere/excel/domain/ExcelResponse.java
0 → 100644
浏览文件 @
155b340e
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
;
}
}
backend/src/main/java/io/metersphere/excel/domain/TestCaseExcelData.java
0 → 100644
浏览文件 @
155b340e
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
;
}
}
backend/src/main/java/io/metersphere/excel/listener/EasyExcelListener.java
0 → 100644
浏览文件 @
155b340e
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
;
}
}
\ No newline at end of file
backend/src/main/java/io/metersphere/excel/listener/TestCaseDataListener.java
0 → 100644
浏览文件 @
155b340e
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
;
}
}
backend/src/main/java/io/metersphere/excel/util/ExcelValidateHelper.java
0 → 100644
浏览文件 @
155b340e
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
();
}
}
\ No newline at end of file
backend/src/main/java/io/metersphere/exception/ExcelImportException.java
0 → 100644
浏览文件 @
155b340e
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
);
}
}
backend/src/main/java/io/metersphere/report/JtlResolver.java
浏览文件 @
155b340e
...
...
@@ -104,7 +104,7 @@ public class JtlResolver {
String
average
=
decimalFormat
.
format
((
float
)
oneLineElapsedTime
/
jtlSamplesSize
);
requestStatistics
.
setAverage
(
average
);
/*
*
/*
* TP90的计算
* 1,把一段时间内全部的请求的响应时间,从小到大排序,获得序列A
* 2,总的请求数量,乘以90%,获得90%对应的请求个数C
...
...
@@ -123,7 +123,7 @@ public class JtlResolver {
requestStatistics
.
setMax
(
elapsedList
.
get
(
jtlSamplesSize
-
1
)
+
""
);
requestStatistics
.
setErrors
(
decimalFormat
.
format
(
failSize
*
100.0
/
jtlSamplesSize
)
+
"%"
);
requestStatistics
.
setKo
(
failSize
);
/*
*
/*
* 所有的相同请求的bytes总和 / 1024 / 请求持续运行的时间=sum(bytes)/1024/total time
* total time = 最大时间戳 - 最小时间戳 + 最后请求的响应时间
*/
...
...
@@ -268,20 +268,34 @@ public class JtlResolver {
DecimalFormat
decimalFormat
=
new
DecimalFormat
(
"0.00"
);
List
<
Metric
>
totalLineList
=
JtlResolver
.
resolver
(
jtlString
);
// todo
List
<
Metric
>
totalLineList2
=
JtlResolver
.
resolver
(
jtlString
);
// 时间戳转时间
for
(
Metric
metric
:
totalLineList2
)
{
metric
.
setTimestamp
(
stampToDate
(
DATE_TIME_PATTERN
,
metric
.
getTimestamp
()));
}
Map
<
String
,
List
<
Metric
>>
collect2
=
Objects
.
requireNonNull
(
totalLineList2
).
stream
().
collect
(
Collectors
.
groupingBy
(
Metric:
:
getTimestamp
));
List
<
Map
.
Entry
<
String
,
List
<
Metric
>>>
entries
=
new
ArrayList
<>(
collect2
.
entrySet
());
int
maxUsers
=
0
;
for
(
Map
.
Entry
<
String
,
List
<
Metric
>>
entry
:
entries
)
{
List
<
Metric
>
metrics
=
entry
.
getValue
();
Map
<
String
,
List
<
Metric
>>
metricsMap
=
metrics
.
stream
().
collect
(
Collectors
.
groupingBy
(
Metric:
:
getThreadName
));
if
(
metricsMap
.
size
()
>
maxUsers
)
{
maxUsers
=
metricsMap
.
size
();
}
}
Map
<
String
,
List
<
Metric
>>
collect
=
totalLineList
.
stream
().
collect
(
Collectors
.
groupingBy
(
Metric:
:
getTimestamp
));
Iterator
<
Map
.
Entry
<
String
,
List
<
Metric
>>>
iterator
=
collect
.
entrySet
().
iterator
();
int
maxUsers
=
0
,
totalElapsed
=
0
;
int
totalElapsed
=
0
;
float
totalBytes
=
0
f
;
while
(
iterator
.
hasNext
())
{
Map
.
Entry
<
String
,
List
<
Metric
>>
entry
=
iterator
.
next
();
List
<
Metric
>
metricList
=
entry
.
getValue
();
if
(
metricList
.
size
()
>
maxUsers
)
{
maxUsers
=
metricList
.
size
();
}
for
(
Metric
metric
:
metricList
)
{
String
elapsed
=
metric
.
getElapsed
();
totalElapsed
+=
Integer
.
parseInt
(
elapsed
);
...
...
@@ -420,22 +434,22 @@ public class JtlResolver {
totalLineList
.
sort
(
Comparator
.
comparing
(
t0
->
Long
.
valueOf
(
t0
.
getTimestamp
())));
String
startTimeStamp
=
totalLineList
.
get
(
0
).
getTimestamp
();
String
endTimeStamp
=
totalLineList
.
get
(
totalLineList
.
size
()
-
1
).
getTimestamp
();
String
endTimeStamp
=
totalLineList
.
get
(
totalLineList
.
size
()
-
1
).
getTimestamp
();
DateTimeFormatter
dtf
=
DateTimeFormatter
.
ofPattern
(
DATE_TIME_PATTERN
);
DateTimeFormatter
dtf
=
DateTimeFormatter
.
ofPattern
(
"yyyy/MM/dd HH:mm:ss"
);
String
startTime
=
dtf
.
format
(
LocalDateTime
.
ofInstant
(
Instant
.
ofEpochMilli
(
Long
.
parseLong
(
startTimeStamp
)),
ZoneId
.
systemDefault
()));
String
endTime
=
dtf
.
format
(
LocalDateTime
.
ofInstant
(
Instant
.
ofEpochMilli
(
Long
.
parseLong
(
endTimeStamp
)),
ZoneId
.
systemDefault
()));
reportTimeInfo
.
setStartTime
(
startTime
);
reportTimeInfo
.
setEndTime
(
endTime
);
Date
startDate
=
new
Date
(
Long
.
parseLong
(
startTimeStamp
));
Date
endDate
=
new
Date
(
Long
.
parseLong
(
endTimeStamp
));
long
timestamp
=
endDate
.
getTime
()
-
startDate
.
getTime
();
reportTimeInfo
.
setDuration
(
String
.
valueOf
(
timestamp
*
1.0
/
1000
/
60
));
// todo 时间问题
long
seconds
=
Duration
.
between
(
Instant
.
ofEpochMilli
(
Long
.
parseLong
(
startTimeStamp
)),
Instant
.
ofEpochMilli
(
Long
.
parseLong
(
endTimeStamp
))).
getSeconds
();
String
duration
;
if
(
seconds
/
60
==
0
)
{
duration
=
String
.
valueOf
(
1
);
}
else
{
duration
=
String
.
valueOf
(
seconds
/
60
);
}
reportTimeInfo
.
setDuration
(
duration
);
reportTimeInfo
.
setDuration
(
String
.
valueOf
(
seconds
));
return
reportTimeInfo
;
}
...
...
backend/src/main/java/io/metersphere/service/PerformanceTestService.java
浏览文件 @
155b340e
...
...
@@ -3,9 +3,10 @@ package io.metersphere.service;
import
io.metersphere.base.domain.*
;
import
io.metersphere.base.mapper.*
;
import
io.metersphere.base.mapper.ext.ExtLoadTestMapper
;
import
io.metersphere.base.mapper.ext.ExtLoadTestReportDetailMapper
;
import
io.metersphere.base.mapper.ext.ExtLoadTestReportMapper
;
import
io.metersphere.commons.constants.FileType
;
import
io.metersphere.commons.constants.TestStatus
;
import
io.metersphere.commons.constants.
Performance
TestStatus
;
import
io.metersphere.commons.exception.MSException
;
import
io.metersphere.commons.utils.LogUtil
;
import
io.metersphere.controller.request.testplan.*
;
...
...
@@ -48,6 +49,10 @@ public class PerformanceTestService {
private
LoadTestReportMapper
loadTestReportMapper
;
@Resource
private
ExtLoadTestReportMapper
extLoadTestReportMapper
;
@Resource
private
LoadTestReportDetailMapper
loadTestReportDetailMapper
;
@Resource
private
ExtLoadTestReportDetailMapper
extLoadTestReportDetailMapper
;
public
List
<
LoadTestDTO
>
list
(
QueryTestPlanRequest
request
)
{
return
extLoadTestMapper
.
list
(
request
);
...
...
@@ -93,6 +98,7 @@ public class PerformanceTestService {
loadTest
.
setTestResourcePoolId
(
request
.
getTestResourcePoolId
());
loadTest
.
setLoadConfiguration
(
request
.
getLoadConfiguration
());
loadTest
.
setAdvancedConfiguration
(
request
.
getAdvancedConfiguration
());
loadTest
.
setStatus
(
PerformanceTestStatus
.
Saved
.
name
());
loadTestMapper
.
insert
(
loadTest
);
return
loadTest
;
}
...
...
@@ -158,19 +164,22 @@ public class PerformanceTestService {
loadTest
.
setLoadConfiguration
(
request
.
getLoadConfiguration
());
loadTest
.
setAdvancedConfiguration
(
request
.
getAdvancedConfiguration
());
loadTest
.
setTestResourcePoolId
(
request
.
getTestResourcePoolId
());
// todo 修改 load_test 的时候排除状态,这里存在修改了 Running 的测试状态的风险
// loadTest.setStatus(PerformanceTestStatus.Saved.name());
loadTestMapper
.
updateByPrimaryKeySelective
(
loadTest
);
}
return
request
.
getId
();
}
public
boolean
run
(
RunTestPlanRequest
request
)
{
@Transactional
(
noRollbackFor
=
MSException
.
class
)
// 保存失败的信息
public
void
run
(
RunTestPlanRequest
request
)
{
final
LoadTestWithBLOBs
loadTest
=
loadTestMapper
.
selectByPrimaryKey
(
request
.
getId
());
if
(
loadTest
==
null
)
{
MSException
.
throwException
(
Translator
.
get
(
"run_load_test_not_found"
)
+
request
.
getId
());
}
if
(
StringUtils
.
equalsAny
(
loadTest
.
getStatus
(),
TestStatus
.
Running
.
name
(),
TestStatus
.
Starting
.
name
()))
{
if
(
StringUtils
.
equalsAny
(
loadTest
.
getStatus
(),
PerformanceTestStatus
.
Running
.
name
(),
Performance
TestStatus
.
Starting
.
name
()))
{
MSException
.
throwException
(
Translator
.
get
(
"load_test_is_running"
));
}
...
...
@@ -181,12 +190,12 @@ public class PerformanceTestService {
MSException
.
throwException
(
String
.
format
(
"Test cannot be run,test ID:%s"
,
request
.
getId
()));
}
return
startEngine
(
loadTest
,
engine
);
startEngine
(
loadTest
,
engine
);
// todo:通过调用stop方法能够停止正在运行的engine,但是如果部署了多个backend实例,页面发送的停止请求如何定位到具体的engine
}
private
boolean
startEngine
(
LoadTestWithBLOBs
loadTest
,
Engine
engine
)
{
private
void
startEngine
(
LoadTestWithBLOBs
loadTest
,
Engine
engine
)
{
LoadTestReportWithBLOBs
testReport
=
new
LoadTestReportWithBLOBs
();
testReport
.
setId
(
engine
.
getReportId
());
testReport
.
setCreateTime
(
engine
.
getStartTime
());
...
...
@@ -194,31 +203,32 @@ public class PerformanceTestService {
testReport
.
setTestId
(
loadTest
.
getId
());
testReport
.
setName
(
loadTest
.
getName
());
// 启动测试
boolean
started
=
true
;
try
{
engine
.
start
();
//
标记running状态
loadTest
.
setStatus
(
TestStatus
.
Starting
.
name
());
//
启动正常修改状态 starting
loadTest
.
setStatus
(
Performance
TestStatus
.
Starting
.
name
());
loadTestMapper
.
updateByPrimaryKeySelective
(
loadTest
);
// 启动正常插入 report
testReport
.
setContent
(
HEADERS
);
testReport
.
setStatus
(
TestStatus
.
Starting
.
name
());
testReport
.
setStatus
(
Performance
TestStatus
.
Starting
.
name
());
loadTestReportMapper
.
insertSelective
(
testReport
);
LoadTestReportDetail
reportDetail
=
new
LoadTestReportDetail
();
reportDetail
.
setContent
(
HEADERS
);
reportDetail
.
setReportId
(
testReport
.
getId
());
loadTestReportDetailMapper
.
insertSelective
(
reportDetail
);
// append \n
extLoadTestReportMapper
.
appendLine
(
testReport
.
getId
(),
"\n"
);
}
catch
(
Exception
e
)
{
// append \n
extLoadTestReportDetailMapper
.
appendLine
(
testReport
.
getId
(),
"\n"
);
}
catch
(
MSException
e
)
{
LogUtil
.
error
(
e
);
started
=
false
;
loadTest
.
setStatus
(
TestStatus
.
Error
.
name
());
loadTest
.
setStatus
(
PerformanceTestStatus
.
Error
.
name
());
loadTest
.
setDescription
(
e
.
getMessage
());
loadTestMapper
.
updateByPrimaryKeySelective
(
loadTest
);
//
testReport
.
setStatus
(
TestStatus
.
Error
.
name
());
testReport
.
setDescription
(
e
.
getMessage
());
loadTestReportMapper
.
insertSelective
(
testReport
);
throw
e
;
}
return
started
;
}
public
List
<
LoadTestDTO
>
recentTestPlans
(
QueryTestPlanRequest
request
)
{
...
...
backend/src/main/java/io/metersphere/service/ReportService.java
浏览文件 @
155b340e
...
...
@@ -5,7 +5,7 @@ import io.metersphere.base.domain.LoadTestReportExample;
import
io.metersphere.base.domain.LoadTestReportWithBLOBs
;
import
io.metersphere.base.mapper.LoadTestReportMapper
;
import
io.metersphere.base.mapper.ext.ExtLoadTestReportMapper
;
import
io.metersphere.commons.constants.TestStatus
;
import
io.metersphere.commons.constants.
Performance
TestStatus
;
import
io.metersphere.commons.exception.MSException
;
import
io.metersphere.controller.request.ReportRequest
;
import
io.metersphere.dto.ReportDTO
;
...
...
@@ -112,9 +112,9 @@ public class ReportService {
public
void
checkReportStatus
(
String
reportId
)
{
LoadTestReportWithBLOBs
loadTestReport
=
loadTestReportMapper
.
selectByPrimaryKey
(
reportId
);
String
reportStatus
=
loadTestReport
.
getStatus
();
if
(
StringUtils
.
equals
(
TestStatus
.
Running
.
name
(),
reportStatus
))
{
if
(
StringUtils
.
equals
(
Performance
TestStatus
.
Running
.
name
(),
reportStatus
))
{
MSException
.
throwException
(
"Reporting in progress..."
);
}
else
if
(
StringUtils
.
equals
(
TestStatus
.
Error
.
name
(),
reportStatus
))
{
}
else
if
(
StringUtils
.
equals
(
Performance
TestStatus
.
Error
.
name
(),
reportStatus
))
{
MSException
.
throwException
(
"Report generation error!"
);
}
}
...
...
backend/src/main/java/io/metersphere/service/TestCaseNodeService.java
浏览文件 @
155b340e
...
...
@@ -6,8 +6,11 @@ import io.metersphere.base.mapper.TestCaseMapper;
import
io.metersphere.base.mapper.TestCaseNodeMapper
;
import
io.metersphere.base.mapper.TestPlanMapper
;
import
io.metersphere.base.mapper.TestPlanTestCaseMapper
;
import
io.metersphere.commons.constants.TestCaseConstants
;
import
io.metersphere.commons.utils.BeanUtils
;
import
io.metersphere.dto.TestCaseNodeDTO
;
import
io.metersphere.exception.ExcelImportException
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
...
...
@@ -30,8 +33,8 @@ public class TestCaseNodeService {
public
int
addNode
(
TestCaseNode
node
)
{
if
(
node
.
getLevel
()
>
5
){
throw
new
RuntimeException
(
"模块树最大深度为
5
层!"
);
if
(
node
.
getLevel
()
>
TestCaseConstants
.
MAX_NODE_DEPTH
){
throw
new
RuntimeException
(
"模块树最大深度为
"
+
TestCaseConstants
.
MAX_NODE_DEPTH
+
"
层!"
);
}
node
.
setCreateTime
(
System
.
currentTimeMillis
());
node
.
setUpdateTime
(
System
.
currentTimeMillis
());
...
...
@@ -196,4 +199,124 @@ public class TestCaseNodeService {
TestPlan
testPlan
=
testPlanMapper
.
selectByPrimaryKey
(
planId
);
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
();
}
}
backend/src/main/java/io/metersphere/service/TestCaseService.java
浏览文件 @
155b340e
package
io.metersphere.service
;
import
com.alibaba.excel.EasyExcelFactory
;
import
com.github.pagehelper.PageHelper
;
import
io.metersphere.base.domain.*
;
import
io.metersphere.base.mapper.ProjectMapper
;
import
io.metersphere.base.mapper.TestCaseMapper
;
import
io.metersphere.base.mapper.TestPlanMapper
;
import
io.metersphere.base.mapper.TestPlanTestCaseMapper
;
import
io.metersphere.base.mapper.*
;
import
io.metersphere.base.mapper.ext.ExtTestCaseMapper
;
import
io.metersphere.commons.utils.LogUtil
;
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.ibatis.session.ExecutorType
;
import
org.apache.ibatis.session.SqlSession
;
import
org.apache.ibatis.session.SqlSessionFactory
;
import
org.springframework.stereotype.Service
;
import
org.springframework.transaction.annotation.Transactional
;
import
org.springframework.web.multipart.MultipartFile
;
import
javax.annotation.Resource
;
import
java.
util.ArrayList
;
import
java.
io.IOException
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.UUID
;
import
java.util.stream.Collectors
;
import
java.util.stream.Stream
;
@Service
@Transactional
(
rollbackFor
=
Exception
.
class
)
...
...
@@ -41,6 +49,15 @@ public class TestCaseService {
@Resource
ProjectMapper
projectMapper
;
@Resource
SqlSessionFactory
sqlSessionFactory
;
@Resource
TestCaseNodeService
testCaseNodeService
;
@Resource
UserMapper
userMapper
;
public
void
addTestCase
(
TestCaseWithBLOBs
testCase
)
{
testCase
.
setId
(
UUID
.
randomUUID
().
toString
());
testCase
.
setCreateTime
(
System
.
currentTimeMillis
());
...
...
@@ -144,4 +161,59 @@ public class TestCaseService {
}
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
();
}
}
backend/src/main/resources/db/migration/V2__metersphere_ddl.sql
浏览文件 @
155b340e
...
...
@@ -62,6 +62,15 @@ CREATE TABLE IF NOT EXISTS `load_test_report` (
DEFAULT
CHARSET
=
utf8mb4
COLLATE
=
utf8mb4_bin
;
CREATE
TABLE
IF
NOT
EXISTS
`load_test_report_detail`
(
`report_id`
varchar
(
50
)
NOT
NULL
,
`content`
longtext
,
PRIMARY
KEY
(
`report_id`
)
)
ENGINE
=
InnoDB
DEFAULT
CHARSET
=
utf8mb4
COLLATE
=
utf8mb4_bin
;
CREATE
TABLE
IF
NOT
EXISTS
`organization`
(
`id`
varchar
(
50
)
NOT
NULL
COMMENT
'Organization ID'
,
`name`
varchar
(
64
)
NOT
NULL
COMMENT
'Organization name'
,
...
...
backend/src/main/resources/i18n/en-US.json
浏览文件 @
155b340e
...
...
@@ -18,5 +18,6 @@
"no_nodes_message"
:
"No node message"
,
"duplicate_node_ip"
:
"Duplicate IPs"
,
"only_one_k8s"
:
"Only one K8s can be added"
,
"organization_id_is_null"
:
"Organization ID cannot be null"
"organization_id_is_null"
:
"Organization ID cannot be null"
,
"max_thread_insufficient"
:
"The number of concurrent users exceeds"
}
\ No newline at end of file
backend/src/main/resources/i18n/zh-CN.json
浏览文件 @
155b340e
...
...
@@ -18,5 +18,6 @@
"no_nodes_message"
:
"没有节点信息"
,
"duplicate_node_ip"
:
"节点 IP 重复"
,
"only_one_k8s"
:
"只能添加一个 K8s"
,
"organization_id_is_null"
:
"组织 ID 不能为空"
"organization_id_is_null"
:
"组织 ID 不能为空"
,
"max_thread_insufficient"
:
"并发用户数超额"
}
\ No newline at end of file
backend/src/test/java/io/metersphere/ReportContentTests.java
0 → 100644
浏览文件 @
155b340e
package
io.metersphere
;
import
com.opencsv.bean.CsvToBean
;
import
com.opencsv.bean.CsvToBeanBuilder
;
import
com.opencsv.bean.HeaderColumnNameMappingStrategy
;
import
io.metersphere.base.domain.LoadTestReportDetail
;
import
io.metersphere.base.domain.LoadTestReportWithBLOBs
;
import
io.metersphere.base.mapper.LoadTestReportDetailMapper
;
import
io.metersphere.base.mapper.LoadTestReportMapper
;
import
io.metersphere.report.base.Metric
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.test.context.junit4.SpringRunner
;
import
javax.annotation.Resource
;
import
java.io.Reader
;
import
java.io.StringReader
;
@RunWith
(
SpringRunner
.
class
)
@SpringBootTest
(
webEnvironment
=
SpringBootTest
.
WebEnvironment
.
RANDOM_PORT
)
public
class
ReportContentTests
{
@Resource
private
LoadTestReportDetailMapper
loadTestReportDetailMapper
;
@Resource
private
LoadTestReportMapper
loadTestReportMapper
;
@Test
public
void
test1
()
{
String
reportId
=
"ba972086-7d74-4f58-99b0-9c014114fd99"
;
LoadTestReportDetail
loadTestReportDetail
=
loadTestReportDetailMapper
.
selectByPrimaryKey
(
reportId
);
LoadTestReportWithBLOBs
loadTestReportWithBLOBs
=
loadTestReportMapper
.
selectByPrimaryKey
(
reportId
);
HeaderColumnNameMappingStrategy
<
Metric
>
ms
=
new
HeaderColumnNameMappingStrategy
<>();
ms
.
setType
(
Metric
.
class
);
try
(
Reader
reader
=
new
StringReader
(
loadTestReportDetail
.
getContent
()))
{
CsvToBean
<
Metric
>
cb
=
new
CsvToBeanBuilder
<
Metric
>(
reader
)
.
withType
(
Metric
.
class
)
.
withSkipLines
(
0
)
.
withMappingStrategy
(
ms
)
.
withIgnoreLeadingWhiteSpace
(
true
)
.
build
();
System
.
out
.
println
(
cb
.
parse
().
size
());
}
catch
(
Exception
ex
)
{
ex
.
printStackTrace
();
}
try
(
Reader
reader
=
new
StringReader
(
loadTestReportWithBLOBs
.
getContent
()))
{
CsvToBean
<
Metric
>
cb
=
new
CsvToBeanBuilder
<
Metric
>(
reader
)
.
withType
(
Metric
.
class
)
.
withSkipLines
(
0
)
.
withMappingStrategy
(
ms
)
.
withIgnoreLeadingWhiteSpace
(
true
)
.
build
();
System
.
out
.
println
(
cb
.
parse
().
size
());
}
catch
(
Exception
ex
)
{
ex
.
printStackTrace
();
}
}
}
frontend/src/business/components/common/head/HeaderUser.vue
浏览文件 @
155b340e
...
...
@@ -51,7 +51,7 @@
TokenKey
,
WORKSPACE_ID
}
from
'
../../../../common/js/constants
'
;
import
{
hasRoles
}
from
"
../../../../common/js/utils
"
;
import
{
hasRoles
,
saveLocalStorage
}
from
"
../../../../common/js/utils
"
;
export
default
{
name
:
"
MsUser
"
,
...
...
@@ -136,7 +136,7 @@
changeOrg
(
data
)
{
let
orgId
=
data
.
id
;
this
.
$post
(
"
/user/switch/source/org/
"
+
orgId
,
{},
response
=>
{
localStorage
.
setItem
(
TokenKey
,
JSON
.
stringify
(
response
.
data
)
);
saveLocalStorage
(
response
);
this
.
$router
.
push
(
'
/
'
);
window
.
location
.
reload
();
})
...
...
@@ -147,7 +147,7 @@
return
false
;
}
this
.
$post
(
"
/user/switch/source/ws/
"
+
workspaceId
,
{},
response
=>
{
localStorage
.
setItem
(
TokenKey
,
JSON
.
stringify
(
response
.
data
)
);
saveLocalStorage
(
response
);
localStorage
.
setItem
(
"
workspace_id
"
,
workspaceId
);
this
.
$router
.
push
(
'
/
'
);
window
.
location
.
reload
();
...
...
frontend/src/business/components/performance/report/PerformanceReportView.vue
浏览文件 @
155b340e
...
...
@@ -20,7 +20,7 @@
</el-col>
<el-col
:span=
"8"
>
<span
class=
"ms-report-time-desc"
>
持续时间:
{{
minutes
}}
分钟
持续时间:
{{
minutes
}}
分钟
{{
seconds
}}
秒
</span>
<span
class=
"ms-report-time-desc"
>
开始时间:
{{
startTime
}}
...
...
@@ -79,6 +79,7 @@
startTime
:
'
0
'
,
endTime
:
'
0
'
,
minutes
:
'
0
'
,
seconds
:
'
0
'
}
},
methods
:
{
...
...
@@ -101,7 +102,9 @@
if
(
data
){
this
.
startTime
=
data
.
startTime
;
this
.
endTime
=
data
.
endTime
;
this
.
minutes
=
data
.
duration
;
let
duration
=
data
.
duration
;
this
.
minutes
=
Math
.
floor
(
duration
/
60
);
this
.
seconds
=
duration
%
60
;
}
})
}
...
...
@@ -144,7 +147,9 @@
if
(
data
){
this
.
startTime
=
data
.
startTime
;
this
.
endTime
=
data
.
endTime
;
this
.
minutes
=
data
.
duration
;
let
duration
=
data
.
duration
;
this
.
minutes
=
Math
.
floor
(
duration
/
60
);
this
.
seconds
=
duration
%
60
;
}
})
window
.
location
.
reload
();
...
...
frontend/src/business/components/performance/report/components/TestOverview.vue
浏览文件 @
155b340e
...
...
@@ -96,6 +96,10 @@
})
this
.
$get
(
"
/performance/report/content/load_chart/
"
+
this
.
id
,
res
=>
{
let
data
=
res
.
data
;
let
userList
=
data
.
filter
(
m
=>
m
.
groupName
===
"
users
"
).
map
(
m
=>
m
.
yAxis
);
let
hitsList
=
data
.
filter
(
m
=>
m
.
groupName
===
"
hits
"
).
map
(
m
=>
m
.
yAxis
);
let
userMax
=
this
.
_getChartMax
(
userList
);
let
hitsMax
=
this
.
_getChartMax
(
hitsList
);
let
loadOption
=
{
title
:
{
text
:
'
Load
'
,
...
...
@@ -105,30 +109,57 @@
color
:
'
#65A2FF
'
},
},
tooltip
:
{
show
:
true
,
trigger
:
'
axis
'
},
legend
:
{},
xAxis
:
{},
yAxis
:
[{
name
:
'
User
'
,
type
:
'
value
'
,
min
:
0
,
max
:
userMax
,
splitNumber
:
5
,
// interval: 10
/ 5
interval
:
userMax
/
5
},
{
name
:
'
Hits/s
'
,
type
:
'
value
'
,
splitNumber
:
5
,
min
:
0
,
// max: 5
,
// interval: 5
/ 5
max
:
hitsMax
,
interval
:
hitsMax
/
5
}
],
series
:
[]
};
let
setting
=
{
series
:
[
{
name
:
'
users
'
,
color
:
'
#0CA74A
'
,
},
{
name
:
'
hits
'
,
yAxisIndex
:
'
1
'
,
color
:
'
#65A2FF
'
,
},
{
name
:
'
errors
'
,
yAxisIndex
:
'
1
'
,
color
:
'
#E6113C
'
,
}
]
}
this
.
loadOption
=
this
.
generateOption
(
loadOption
,
data
);
this
.
loadOption
=
this
.
generateOption
(
loadOption
,
data
,
setting
);
})
this
.
$get
(
"
/performance/report/content/res_chart/
"
+
this
.
id
,
res
=>
{
let
data
=
res
.
data
;
let
userList
=
data
.
filter
(
m
=>
m
.
groupName
===
"
users
"
).
map
(
m
=>
m
.
yAxis
);
let
responseTimeList
=
data
.
filter
(
m
=>
m
.
groupName
===
"
responseTime
"
).
map
(
m
=>
m
.
yAxis
);
let
userMax
=
this
.
_getChartMax
(
userList
);
let
resMax
=
this
.
_getChartMax
(
responseTimeList
);
let
resOption
=
{
title
:
{
text
:
'
Response Time
'
,
...
...
@@ -138,28 +169,55 @@
color
:
'
#99743C
'
},
},
tooltip
:
{
show
:
true
,
trigger
:
'
axis
'
},
legend
:
{},
xAxis
:
{},
yAxis
:
[{
name
:
'
User
'
,
type
:
'
value
'
,
splitNumber
:
5
,
min
:
0
min
:
0
,
max
:
userMax
,
interval
:
userMax
/
5
},
{
name
:
'
Response Time
'
,
type
:
'
value
'
,
splitNumber
:
5
,
min
:
0
min
:
0
,
max
:
resMax
,
interval
:
resMax
/
5
}
],
series
:
[]
}
this
.
resOption
=
this
.
generateOption
(
resOption
,
data
);
let
setting
=
{
series
:
[
{
name
:
'
users
'
,
color
:
'
#0CA74A
'
,
},
{
name
:
"
responseTime
"
,
yAxisIndex
:
'
1
'
,
color
:
'
#99743C
'
,
}
]
}
this
.
resOption
=
this
.
generateOption
(
resOption
,
data
,
setting
);
})
},
generateOption
(
option
,
data
)
{
generateOption
(
option
,
data
,
setting
)
{
let
chartData
=
data
;
let
seriesArray
=
[];
for
(
let
set
in
setting
)
{
if
(
set
===
"
series
"
)
{
seriesArray
=
setting
[
set
];
continue
;
}
this
.
$set
(
option
,
set
,
setting
[
set
]);
}
let
legend
=
[],
series
=
{},
xAxis
=
[],
seriesData
=
[];
chartData
.
forEach
(
item
=>
{
if
(
!
xAxis
.
includes
(
item
.
xAxis
))
{
...
...
@@ -183,11 +241,24 @@
type
:
'
line
'
,
data
:
data
};
let
seriesArrayNames
=
seriesArray
.
map
(
m
=>
m
.
name
);
if
(
seriesArrayNames
.
includes
(
name
))
{
for
(
let
j
=
0
;
j
<
seriesArray
.
length
;
j
++
)
{
let
seriesObj
=
seriesArray
[
j
];
if
(
seriesObj
[
'
name
'
]
===
name
)
{
Object
.
assign
(
items
,
seriesObj
);
}
}
}
seriesData
.
push
(
items
);
}
this
.
$set
(
option
,
"
series
"
,
seriesData
);
return
option
;
},
_getChartMax
(
arr
)
{
const
max
=
Math
.
max
(...
arr
);
return
Math
.
ceil
(
max
/
4.5
)
*
5
;
}
},
watch
:
{
status
()
{
...
...
frontend/src/business/components/performance/test/PerformanceTestPlan.vue
浏览文件 @
155b340e
...
...
@@ -46,7 +46,10 @@
prop=
"status"
:label=
"$t('commons.status')"
>
<
template
v-slot:default=
"{row}"
>
<el-tag
size=
"mini"
type=
"primary"
v-if=
"row.status === 'Starting'"
>
<el-tag
size=
"mini"
type=
"info"
v-if=
"row.status === 'Saved'"
>
{{
row
.
status
}}
</el-tag>
<el-tag
size=
"mini"
type=
"primary"
v-else-if=
"row.status === 'Starting'"
>
{{
row
.
status
}}
</el-tag>
<el-tag
size=
"mini"
type=
"success"
v-else-if=
"row.status === 'Running'"
>
...
...
frontend/src/business/components/track/case/TestCase.vue
浏览文件 @
155b340e
...
...
@@ -26,6 +26,7 @@
:current-project=
"currentProject"
@
openTestCaseEditDialog=
"openTestCaseEditDialog"
@
testCaseEdit=
"openTestCaseEditDialog"
@
refresh=
"refresh"
ref=
"testCaseList"
>
</test-case-list>
</el-main>
...
...
frontend/src/business/components/track/case/components/TestCaseExport.vue
0 → 100644
浏览文件 @
155b340e
<
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
>
frontend/src/business/components/track/case/components/TestCaseImport.vue
0 → 100644
浏览文件 @
155b340e
<
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
>
frontend/src/business/components/track/case/components/TestCaseList.vue
浏览文件 @
155b340e
...
...
@@ -4,12 +4,21 @@
<el-card
v-loading=
"result.loading"
>
<template
v-slot:header
>
<div>
<el-row
type=
"flex"
justify=
"s
pace-between
"
align=
"middle"
>
<el-row
type=
"flex"
justify=
"s
tart
"
align=
"middle"
>
<el-col
:span=
"5"
>
<span
class=
"title"
>
{{
$t
(
'
test_track.test_case
'
)
}}
</span>
<ms-create-box
:tips=
"$t('test_track.create')"
:exec=
"testCaseCreate"
/>
</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"
>
<span
class=
"search"
>
<el-input
type=
"text"
size=
"small"
:placeholder=
"$t('load_test.search_by_name')"
...
...
@@ -105,10 +114,12 @@
<
script
>
import
MsCreateBox
from
'
../../../settings/CreateBox
'
;
import
TestCaseImport
from
'
../components/TestCaseImport
'
;
import
TestCaseExport
from
'
../components/TestCaseExport
'
;
export
default
{
name
:
"
TestCaseList
"
,
components
:
{
MsCreateBox
},
components
:
{
MsCreateBox
,
TestCaseImport
,
TestCaseExport
},
data
()
{
return
{
result
:
{},
...
...
@@ -195,6 +206,9 @@
type
:
'
success
'
});
});
},
refresh
()
{
this
.
$emit
(
'
refresh
'
);
}
}
}
...
...
frontend/src/common/js/utils.js
浏览文件 @
155b340e
...
...
@@ -28,3 +28,12 @@ export function checkoutCurrentWorkspace() {
// 查看当前用户是否是 lastWorkspaceId 的工作空间用户
return
user
.
userRoles
.
filter
(
ur
=>
hasRoles
(
ROLE_TEST_MANAGER
,
ROLE_TEST_USER
,
ROLE_TEST_VIEWER
)
&&
user
.
lastWorkspaceId
===
ur
.
sourceId
).
length
>
0
;
}
export
function
saveLocalStorage
(
response
)
{
// 登录信息保存 cookie
localStorage
.
setItem
(
TokenKey
,
JSON
.
stringify
(
response
.
data
));
let
rolesArray
=
response
.
data
.
roles
;
let
roles
=
rolesArray
.
map
(
r
=>
r
.
id
);
// 保存角色
localStorage
.
setItem
(
"
roles
"
,
roles
);
}
frontend/src/login/Login.vue
浏览文件 @
155b340e
...
...
@@ -41,7 +41,7 @@
</
template
>
<
script
>
import
{
TokenKey
}
from
'
../common/js/constant
s
'
;
import
{
saveLocalStorage
}
from
'
../common/js/util
s
'
;
export
default
{
...
...
@@ -105,12 +105,7 @@
this
.
$refs
[
form
].
validate
((
valid
)
=>
{
if
(
valid
)
{
this
.
$post
(
"
signin
"
,
this
.
form
,
(
response
)
=>
{
// 登录信息保存 cookie
localStorage
.
setItem
(
TokenKey
,
JSON
.
stringify
(
response
.
data
));
let
rolesArray
=
response
.
data
.
roles
;
let
roles
=
rolesArray
.
map
(
r
=>
r
.
id
);
// 保存角色
localStorage
.
setItem
(
"
roles
"
,
roles
);
saveLocalStorage
(
response
);
window
.
location
.
href
=
"
/
"
});
}
else
{
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录