Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Five-菜鸟级
echarts
提交
5826ca55
E
echarts
项目概览
Five-菜鸟级
/
echarts
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
1
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
echarts
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
1
Issue
1
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
5826ca55
编写于
5月 05, 2021
作者:
Y
Yi Shen
提交者:
GitHub
5月 05, 2021
浏览文件
操作
浏览文件
下载
差异文件
Merge pull request #14862 from apache/enhance-visual-regression-test
Enhance visual regression test
上级
7f595180
3a147bc8
变更
11
展开全部
隐藏空白更改
内联
并排
Showing
11 changed file
with
868 addition
and
327 deletion
+868
-327
test/force2.html
test/force2.html
+1
-1
test/parallel-aqi.html
test/parallel-aqi.html
+1
-14
test/parallel-nutrients.html
test/parallel-nutrients.html
+1
-11
test/runTest/cli.js
test/runTest/cli.js
+11
-8
test/runTest/client/client.css
test/runTest/client/client.css
+122
-31
test/runTest/client/client.js
test/runTest/client/client.js
+226
-64
test/runTest/client/index.html
test/runTest/client/index.html
+194
-122
test/runTest/genReport.js
test/runTest/genReport.js
+18
-32
test/runTest/server.js
test/runTest/server.js
+107
-24
test/runTest/store.js
test/runTest/store.js
+176
-15
test/runTest/util.js
test/runTest/util.js
+11
-5
未找到文件。
test/force2.html
浏览文件 @
5826ca55
...
...
@@ -45,7 +45,7 @@ under the License.
var
gexf
=
dataTool
.
gexf
;
var
chart
=
echarts
.
init
(
document
.
getElementById
(
'
main
'
);
var
chart
=
echarts
.
init
(
document
.
getElementById
(
'
main
'
)
)
;
$
.
get
(
'
./data/les-miserables.gexf
'
,
function
(
xml
)
{
var
graph
=
gexf
.
parse
(
xml
);
...
...
test/parallel-aqi.html
浏览文件 @
5826ca55
...
...
@@ -115,20 +115,7 @@ under the License.
padding
:
10
,
backgroundColor
:
'
#222
'
,
borderColor
:
'
#777
'
,
borderWidth
:
1
,
formatter
:
function
(
obj
)
{
var
value
=
obj
[
0
].
value
;
return
'
<div style="border-bottom: 1px solid rgba(255,255,255,.3); font-size: 18px;padding-bottom: 7px;margin-bottom: 7px">
'
+
obj
[
0
].
seriesName
+
'
'
+
value
[
0
]
+
'
日期:
'
+
value
[
7
]
+
'
</div>
'
+
schema
[
1
].
text
+
'
:
'
+
value
[
1
]
+
'
<br>
'
+
schema
[
2
].
text
+
'
:
'
+
value
[
2
]
+
'
<br>
'
+
schema
[
3
].
text
+
'
:
'
+
value
[
3
]
+
'
<br>
'
+
schema
[
4
].
text
+
'
:
'
+
value
[
4
]
+
'
<br>
'
+
schema
[
5
].
text
+
'
:
'
+
value
[
5
]
+
'
<br>
'
+
schema
[
6
].
text
+
'
:
'
+
value
[
6
]
+
'
<br>
'
;
}
borderWidth
:
1
},
visualMap
:
{
show
:
true
,
...
...
test/parallel-nutrients.html
浏览文件 @
5826ca55
...
...
@@ -157,17 +157,7 @@ under the License.
padding
:
10
,
backgroundColor
:
'
#222
'
,
borderColor
:
'
#777
'
,
borderWidth
:
1
,
formatter
:
function
(
obj
)
{
var
value
=
obj
[
0
].
value
;
return
'
<div style="border-bottom: 1px solid rgba(255,255,255,.3); font-size: 18px;padding-bottom: 7px;margin-bottom: 7px">
'
+
schema
[
1
].
name
+
'
:
'
+
value
[
1
]
+
'
<br>
'
+
schema
[
2
].
name
+
'
:
'
+
value
[
2
]
+
'
<br>
'
+
schema
[
3
].
name
+
'
:
'
+
value
[
3
]
+
'
<br>
'
+
schema
[
4
].
name
+
'
:
'
+
value
[
4
]
+
'
<br>
'
+
schema
[
5
].
name
+
'
:
'
+
value
[
5
]
+
'
<br>
'
+
schema
[
6
].
name
+
'
:
'
+
value
[
6
]
+
'
<br>
'
;
}
borderWidth
:
1
},
title
:
[
{
...
...
test/runTest/cli.js
浏览文件 @
5826ca55
...
...
@@ -38,7 +38,8 @@ program
.
option
(
'
--expected <expected>
'
,
'
Expected version
'
)
.
option
(
'
--actual <actual>
'
,
'
Actual version
'
)
.
option
(
'
--renderer <renderer>
'
,
'
svg/canvas renderer
'
)
.
option
(
'
--no-save
'
,
'
Don
\'
t save result
'
);
.
option
(
'
--no-save
'
,
'
Don
\'
t save result
'
)
.
option
(
'
--dir <dir>
'
,
'
Out dir
'
);
program
.
parse
(
process
.
argv
);
...
...
@@ -46,13 +47,14 @@ program.speed = +program.speed || 1;
program
.
actual
=
program
.
actual
||
'
local
'
;
program
.
expected
=
program
.
expected
||
'
4.2.1
'
;
program
.
renderer
=
(
program
.
renderer
||
'
canvas
'
).
toLowerCase
();
program
.
dir
=
program
.
dir
||
(
__dirname
+
'
/tmp
'
);
if
(
!
program
.
tests
)
{
throw
new
Error
(
'
Tests are required
'
);
}
function
getScreenshotDir
()
{
return
'
tmp/__screenshot__
'
;
return
`
${
program
.
dir
}
/__screenshot__`
;
}
function
sortScreenshots
(
list
)
{
...
...
@@ -98,9 +100,6 @@ async function convertToWebP(filePath, lossless) {
async
function
takeScreenshot
(
page
,
fullPage
,
fileUrl
,
desc
,
isExpected
,
minor
)
{
let
screenshotName
=
testNameFromFile
(
fileUrl
);
if
(
program
.
renderer
===
'
svg
'
)
{
screenshotName
+=
'
-_svg_render_
'
;
}
if
(
desc
)
{
screenshotName
+=
'
-
'
+
slugify
(
desc
,
{
replacement
:
'
-
'
,
lower
:
true
});
}
...
...
@@ -108,8 +107,8 @@ async function takeScreenshot(page, fullPage, fileUrl, desc, isExpected, minor)
screenshotName
+=
'
-
'
+
minor
;
}
let
screenshotPrefix
=
isExpected
?
'
expected
'
:
'
actual
'
;
fse
.
ensureDirSync
(
path
.
join
(
__dirname
,
getScreenshotDir
()
));
let
screenshotPath
=
path
.
join
(
__dirname
,
`
${
getScreenshotDir
()}
/
${
screenshotName
}
-
${
screenshotPrefix
}
.png`
);
fse
.
ensureDirSync
(
getScreenshotDir
(
));
let
screenshotPath
=
path
.
join
(
getScreenshotDir
(),
`
${
screenshotName
}
-
${
screenshotPrefix
}
.png`
);
await
page
.
screenshot
({
path
:
screenshotPath
,
fullPage
...
...
@@ -277,7 +276,7 @@ async function runTest(browser, testOpt, runtimeCode, expectedVersion, actualVer
actual
.
rawScreenshotPath
);
const
diffPath
=
`
${
path
.
resolve
(
__dirname
,
getScreenshotDir
()
)}
/
${
shot
.
screenshotName
}
-diff.png`
;
const
diffPath
=
`
${
getScreenshotDir
(
)}
/
${
shot
.
screenshotName
}
-diff.png`
;
await
writePNG
(
diffPNG
,
diffPath
);
const
diffWebpPath
=
await
convertToWebP
(
diffPath
);
...
...
@@ -328,6 +327,10 @@ async function runTests(pendingTests) {
let
runtimeCode
=
await
buildRuntimeCode
();
runtimeCode
=
`window.__TEST_PLAYBACK_SPEED__ =
${
program
.
speed
||
1
}
;\n
${
runtimeCode
}
`
;
process
.
on
(
'
exit
'
,
()
=>
{
browser
.
close
();
});
try
{
for
(
let
testOpt
of
pendingTests
)
{
console
.
log
(
`Running test:
${
testOpt
.
name
}
, renderer:
${
program
.
renderer
}
`
);
...
...
test/runTest/client/client.css
浏览文件 @
5826ca55
...
...
@@ -30,17 +30,22 @@
}
.header
{
background-color
:
#
293c55
;
box-shadow
:
0
0
20px
rgba
(
0
,
0
,
0
,
0.
2
);
background-color
:
#
fff
;
box-shadow
:
0
0
20px
rgba
(
0
,
0
,
0
,
0.
05
);
position
:
relative
;
z-index
:
10
;
z-index
:
20
;
height
:
55px
;
}
.header
>*
{
display
:
inline-block
;
vertical-align
:
middle
;
}
.header
h1
{
color
:
#
fff
;
color
:
#
222
;
line-height
:
50px
;
margin
:
0
;
font-weight
:
200
;
font-size
:
20px
;
}
...
...
@@ -54,65 +59,111 @@
margin-right
:
20px
;
}
.nav-toolbar
{
.el-aside
{
box-shadow
:
0
0
20px
rgba
(
0
,
0
,
0
,
0.1
);
position
:
relative
;
z-index
:
10
;
}
.el-main
{
background
:
#f3f4fa
;
padding
:
0
;
}
.nav-toolbar
,
.test-run-controls
{
padding
:
10px
10px
;
background
:
#162436
;
box-shadow
:
inset
0
0
5px
black
;
background
:
#fff
;
position
:
fixed
;
top
:
50px
;
width
:
330px
;
z-index
:
2
;
/* box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); */
border-bottom
:
1px
solid
#eee
;
}
.nav-toolbar
.controls
{
margin-top
:
10px
;
.test-run-controls
{
z-index
:
1
;
position
:
sticky
;
width
:
100%
;
padding
:
5px
40px
;
top
:
0px
;
background
:
#896bda
;
box-shadow
:
0
0
20px
rgb
(
0
0
0
/
20%
);
border-bottom
:
none
;
}
.nav-toolbar
.controls
>*
{
.test-run-controls
>*
{
display
:
inline-block
;
vertical-align
:
middle
;
}
.nav-toolbar
.controls
.el-checkbox
{
margin-right
:
2px
;
}
.nav-toolbar
.el-icon-setting
{
color
:
#f3f3f3
;
font-size
:
20px
;
margin-left
:
5px
;
cursor
:
pointer
;
}
.nav-toolbar
.el-button
{
padding-left
:
8px
;
padding-right
:
8px
;
}
.run-config-item
{
margin
:
5px
0
;
margin
:
0
5px
;
color
:
#fff
;
font-size
:
12px
;
}
.run-config-item
>*
{
display
:
inline-block
;
vertical-align
:
middle
;
margin-right
:
10px
;
font-size
:
12px
;
color
:
#fff
;
}
.run-config-item
.el-progress-circle__track
{
stroke
:
rgba
(
255
,
255
,
255
,
0.3
)
!important
;
}
.run-config-item
.el-progress-circle__path
{
stroke
:
rgba
(
255
,
255
,
255
,
1
)
!important
;
}
.run-config-item
.el-input__inner
,
.run-config-item
.el-button-group
{
border
:
none
;
}
.run-config-item
.el-button
{
border-color
:
#fff
;
}
.run-config-item
.label
{
margin-right
:
2px
;
margin-left
:
5px
;
text-transform
:
uppercase
;
}
.test-list
{
overflow-x
:
hidden
;
background
:
#
293c55
;
background
:
#
fff
;
margin
:
0
;
padding
:
0
;
margin-top
:
80px
;
position
:
absolute
;
top
:
48px
;
bottom
:
0
;
right
:
0
;
left
:
0
;
overflow-y
:
scroll
;
}
.test-list
li
{
list-style
:
none
;
padding-left
:
10px
;
cursor
:
pointer
;
color
:
#f3f3f3
;
color
:
#222
;
}
.test-list
li
.el-checkbox
{
margin-right
:
5px
;
}
.test-list
li
a
.menu-link
{
display
:
inline-block
;
text-decoration
:
none
;
font-size
:
14px
;
line-height
:
40px
;
color
:
#
f3f3f3
;
color
:
#
222
;
margin-left
:
3px
;
cursor
:
pointer
;
}
...
...
@@ -122,10 +173,13 @@
}
.test-list
li
.active
{
background
:
#e43c59
;
background
:
#5470C6
;
}
.test-list
li
.active
a
{
color
:
#fff
;
}
.test-list
li
:hover
{
b
ackground
:
#162436
;
b
order-right
:
4px
solid
#5470C6
}
.test-list
li
>*
{
vertical-align
:
middle
;
...
...
@@ -136,6 +190,27 @@
font-size
:
12px
!important
;
}
.el-progress.is-success
.el-progress__text
{
color
:
#67C23A
;
-webkit-text-stroke
:
1px
#67C23A
;
}
.el-progress.is-exception
.el-progress__text
{
color
:
#F56C6C
;
-webkit-text-stroke
:
1px
#F56C6C
;
}
.no-result
{
text-align
:
center
;
font-size
:
30px
;
padding
:
100px
0
;
color
:
#ccc
;
}
.test-result
{
padding
:
20px
;
margin-top
:
20px
;
}
.test-result
.el-progress__text
{
font-size
:
14px
!important
;
}
...
...
@@ -147,6 +222,9 @@
margin
:
0
;
}
.test-result
.title
{
margin-left
:
20px
;
}
.test-result
.title
>*
{
display
:
inline-block
;
vertical-align
:
middle
;
...
...
@@ -161,6 +239,13 @@
text-decoration
:
underline
;
}
.single-test-ops
{
padding
:
20px
20px
0
10px
;
}
.single-test-ops
.el-button
{
margin-left
:
10px
;
}
.test-screenshots
{
margin-top
:
20px
;
padding
:
0
20px
;
...
...
@@ -173,6 +258,8 @@
.test-screenshots
.preview
{
cursor
:
pointer
;
color
:
#409eff
;
float
:
right
;
font-size
:
20px
;
}
.test-screenshots
.preview
:hover
{
text-decoration
:
underline
;
...
...
@@ -185,13 +272,12 @@
.test-screenshots
h4
{
font-size
:
30px
;
font-weight
:
200
;
margin-left
:
-20px
;
color
:
#162436
;
}
.test-errors
,
.test-logs
{
margin-top
:
20px
;
padding
:
0
5
0px
;
padding
:
0
2
0px
;
}
.test-logs
.log-item
{
...
...
@@ -209,6 +295,11 @@ iframe {
overflow
:
overlay
;
}
#tests-runs-dialog
.el-dialog
{
width
:
90%
;
max-width
:
1200px
;
}
::-webkit-scrollbar
{
height
:
8px
;
...
...
test/runTest/client/client.js
浏览文件 @
5826ca55
...
...
@@ -18,7 +18,40 @@
*/
const
socket
=
io
(
'
/client
'
);
const
LOCAL_SAVE_KEY
=
'
visual-regression-testing-config
'
;
// const LOCAL_SAVE_KEY = 'visual-regression-testing-config';
function
getChangedObject
(
target
,
source
)
{
let
changedObject
=
{};
Object
.
keys
(
source
).
forEach
(
key
=>
{
if
(
target
[
key
]
!==
source
[
key
])
{
changedObject
[
key
]
=
source
[
key
];
}
});
return
changedObject
;
}
function
parseParams
(
str
)
{
if
(
!
str
)
{
return
{};
}
const
parts
=
str
.
split
(
'
&
'
);
const
params
=
{};
parts
.
forEach
((
part
)
=>
{
const
kv
=
part
.
split
(
'
=
'
);
params
[
kv
[
0
]]
=
decodeURIComponent
(
kv
[
1
]);
});
return
params
;
}
function
assembleParams
(
paramsObj
)
{
const
paramsArr
=
[];
Object
.
keys
(
paramsObj
).
forEach
((
key
)
=>
{
let
val
=
paramsObj
[
key
];
paramsArr
.
push
(
key
+
'
=
'
+
encodeURIComponent
(
val
));
});
return
paramsArr
.
join
(
'
&
'
);
}
function
processTestsData
(
tests
,
oldTestsData
)
{
tests
.
forEach
((
test
,
idx
)
=>
{
...
...
@@ -45,6 +78,8 @@ function processTestsData(tests, oldTestsData) {
test
.
summary
=
'
warning
'
;
}
// To simplify the condition in sort
test
.
actualErrors
=
test
.
actualErrors
||
[];
// Keep select status not change.
if
(
oldTestsData
&&
oldTestsData
[
idx
])
{
test
.
selected
=
oldTestsData
[
idx
].
selected
;
...
...
@@ -56,52 +91,113 @@ function processTestsData(tests, oldTestsData) {
return
tests
;
}
const
urlRunConfig
=
{};
const
urlParams
=
parseParams
(
window
.
location
.
search
.
substr
(
1
))
// Save and restore
try
{
const
runConfig
=
JSON
.
parse
(
urlParams
.
runConfig
);
Object
.
assign
(
urlRunConfig
,
runConfig
);
}
catch
(
e
)
{}
const
app
=
new
Vue
({
el
:
'
#app
'
,
data
:
{
fullTests
:
[],
currentTestName
:
''
,
sortBy
:
'
name
'
,
currentTestName
:
urlParams
.
test
||
''
,
searchString
:
''
,
running
:
false
,
allSelected
:
false
,
lastSelectedIndex
:
-
1
,
versions
:
[],
expectedVersionsList
:
[],
actualVersionsList
:
[],
loadingVersion
:
false
,
showIframeDialog
:
false
,
previewIframeSrc
:
''
,
previewTitle
:
''
,
runConfig
:
{
noHeadless
:
false
,
replaySpeed
:
5
,
// List of all runs.
showRunsDialog
:
false
,
testsRuns
:
[],
pageInvisible
:
false
,
runConfig
:
Object
.
assign
({
sortBy
:
'
name
'
,
// replaySpeed: 5,
isActualNightly
:
false
,
isExpectedNightly
:
false
,
actualVersion
:
'
local
'
,
expectedVersion
:
null
,
renderer
:
'
canvas
'
,
threads
:
1
}
threads
:
4
}
,
urlRunConfig
)
},
mounted
()
{
// Sync config from server when first time open
// or switching back
socket
.
emit
(
'
syncRunConfig
'
,
{
runConfig
:
this
.
runConfig
,
// Override server config from URL.
forceSet
:
Object
.
keys
(
urlRunConfig
).
length
>
0
});
socket
.
on
(
'
syncRunConfig_return
'
,
res
=>
{
this
.
expectedVersionsList
=
res
.
expectedVersionsList
;
this
.
actualVersionsList
=
res
.
actualVersionsList
;
// Only assign on changed object to avoid unnecessary vue change.
Object
.
assign
(
this
.
runConfig
,
getChangedObject
(
this
.
runConfig
,
res
.
runConfig
));
updateUrl
();
});
setTimeout
(()
=>
{
this
.
scrollToCurrent
();
},
500
);
document
.
addEventListener
(
"
visibilitychange
"
,
()
=>
{
if
(
document
.
visibilityState
===
'
visible
'
)
{
this
.
pageInvisible
=
false
;
socket
.
emit
(
'
syncRunConfig
'
,
{});
}
else
{
this
.
pageInvisible
=
true
;
}
});
},
computed
:
{
finishedPercentage
()
{
let
finishedCount
=
0
;
this
.
fullTests
.
forEach
(
test
=>
{
if
(
test
.
status
===
'
finished
'
)
{
finishedCount
++
;
}
});
return
+
(
finishedCount
/
this
.
fullTests
.
length
*
100
).
toFixed
(
0
)
||
0
;
},
tests
()
{
let
sortFunc
=
this
.
sortBy
===
'
name
'
let
sortFunc
=
this
.
runConfig
.
sortBy
===
'
name
'
?
(
a
,
b
)
=>
a
.
name
.
localeCompare
(
b
.
name
)
:
(
a
,
b
)
=>
{
if
(
a
.
percentage
===
b
.
percentage
)
{
if
(
a
.
actualErrors
&&
b
.
actualErrors
)
{
if
(
a
.
actualErrors
.
length
===
b
.
actualErrors
.
length
)
{
return
a
.
name
.
localeCompare
(
b
.
name
);
}
else
{
return
b
.
actualErrors
.
length
-
a
.
actualErrors
.
length
;
}
if
(
a
.
actualErrors
.
length
===
b
.
actualErrors
.
length
)
{
if
(
a
.
percentage
===
b
.
percentage
)
{
return
a
.
name
.
localeCompare
(
b
.
name
);
}
else
{
return
a
.
name
.
localeCompare
(
b
.
name
)
;
return
a
.
percentage
-
b
.
percentage
;
}
}
return
a
.
percentage
-
b
.
percentage
;
return
b
.
actualErrors
.
length
-
a
.
actualErrors
.
length
;
};
if
(
!
this
.
searchString
)
{
...
...
@@ -116,7 +212,8 @@ const app = new Vue({
},
selectedTests
()
{
return
this
.
fullTests
.
filter
(
test
=>
{
// Only run visible tests.
return
this
.
tests
.
filter
(
test
=>
{
return
test
.
selected
;
});
},
...
...
@@ -159,12 +256,34 @@ const app = new Vue({
set
()
{}
}
},
watch
:
{
'
runConfig.sortBy
'
()
{
setTimeout
(()
=>
{
this
.
scrollToCurrent
();
},
100
);
}
},
methods
:
{
goto
(
url
)
{
window
.
location
.
hash
=
'
#
'
+
url
;
scrollToCurrent
()
{
const
el
=
document
.
querySelector
(
`.test-list>li[title="
${
this
.
currentTestName
}
"]`
);
if
(
el
)
{
el
.
scrollIntoView
({
behavior
:
'
smooth
'
,
block
:
'
center
'
});
}
},
changeTest
(
target
,
testName
)
{
if
(
!
target
.
matches
(
'
input[type="checkbox"]
'
)
&&
!
target
.
matches
(
'
.el-checkbox__inner
'
))
{
app
.
currentTestName
=
testName
;
updateUrl
();
}
},
toggleSort
()
{
this
.
sortBy
=
this
.
sortBy
===
'
name
'
?
'
percentage
'
:
'
name
'
;
this
.
runConfig
.
sortBy
=
this
.
runConfig
.
sortBy
===
'
name
'
?
'
percentage
'
:
'
name
'
;
},
handleSelectAllChange
(
val
)
{
// Only select filtered tests.
...
...
@@ -189,8 +308,8 @@ const app = new Vue({
this
.
tests
[
i
].
selected
=
selected
;
}
},
runSingleTest
(
testName
)
{
runTests
([
testName
]);
runSingleTest
(
testName
,
noHeadless
)
{
runTests
([
testName
]
,
noHeadless
);
},
run
(
runTarget
)
{
let
tests
;
...
...
@@ -206,7 +325,7 @@ const app = new Vue({
else
{
tests
=
this
.
fullTests
;
}
runTests
(
tests
.
map
(
test
=>
test
.
name
));
runTests
(
tests
.
map
(
test
=>
test
.
name
)
,
false
);
},
stopTests
()
{
this
.
running
=
false
;
...
...
@@ -230,20 +349,51 @@ const app = new Vue({
this
.
previewIframeSrc
=
`../../
${
src
}
`
;
this
.
previewTitle
=
src
;
this
.
showIframeDialog
=
true
;
},
showAllTestsRuns
()
{
this
.
showRunsDialog
=
true
;
socket
.
emit
(
'
getAllTestsRuns
'
);
},
switchTestsRun
(
runResult
)
{
this
.
runConfig
.
expectedVersion
=
runResult
.
expectedVersion
;
this
.
runConfig
.
actualVersion
=
runResult
.
actualVersion
;
// TODO
this
.
runConfig
.
isExpectedNightly
=
runResult
.
expectedVersion
.
includes
(
'
-dev.
'
);
this
.
runConfig
.
isActualNightly
=
runResult
.
actualVersion
.
includes
(
'
-dev.
'
);
this
.
runConfig
.
renderer
=
runResult
.
renderer
;
this
.
showRunsDialog
=
false
;
},
genTestsRunReport
(
runResult
)
{
socket
.
emit
(
'
genTestsRunReport
'
,
runResult
);
},
delTestsRun
(
runResult
)
{
app
.
$confirm
(
'
Are you sure to delete this run?
'
,
'
Warn
'
,
{
confirmButtonText
:
'
Yes
'
,
cancelButtonText
:
'
No
'
,
center
:
true
}).
then
(
value
=>
{
const
idx
=
this
.
testsRuns
.
indexOf
(
runResult
);
if
(
idx
>=
0
)
{
this
.
testsRuns
.
splice
(
idx
,
1
);
}
socket
.
emit
(
'
delTestsRun
'
,
{
id
:
runResult
.
id
});
}).
catch
(()
=>
{});
},
open
(
url
,
target
)
{
window
.
open
(
url
,
target
);
}
}
});
// Save and restore
try
{
Object
.
assign
(
app
.
runConfig
,
JSON
.
parse
(
localStorage
.
getItem
(
LOCAL_SAVE_KEY
)));
}
catch
(
e
)
{}
app
.
$watch
(
'
runConfig
'
,
()
=>
{
localStorage
.
setItem
(
LOCAL_SAVE_KEY
,
JSON
.
stringify
(
app
.
runConfig
));
},
{
deep
:
true
});
function
runTests
(
tests
)
{
function
runTests
(
tests
,
noHeadless
)
{
if
(
!
tests
.
length
)
{
app
.
$notify
({
title
:
'
No test selected.
'
,
...
...
@@ -265,9 +415,9 @@ function runTests(tests) {
actualVersion
:
app
.
runConfig
.
actualVersion
,
threads
:
app
.
runConfig
.
threads
,
renderer
:
app
.
runConfig
.
renderer
,
noHeadless
:
app
.
runConfig
.
noHeadless
,
noHeadless
,
replaySpeed
:
app
.
runConfig
.
noHeadless
?
app
.
runConfig
.
replaySpeed
?
1
:
5
// Force run at 5x speed
});
}
...
...
@@ -275,23 +425,24 @@ function runTests(tests) {
socket
.
on
(
'
connect
'
,
()
=>
{
console
.
log
(
'
Connected
'
);
app
.
$el
.
style
.
display
=
'
block
'
;
});
let
firstUpdate
=
true
;
socket
.
on
(
'
update
'
,
msg
=>
{
let
hasFinishedTest
=
!!
msg
.
tests
.
find
(
test
=>
test
.
status
===
'
finished
'
);
if
(
!
hasFinishedTest
&&
firstUpdate
)
{
app
.
$confirm
(
'
It seems you haven
\'
t run any test yet!<br />Do you want to start now?
'
,
'
Tip
'
,
{
confirmButtonText
:
'
Yes
'
,
cancelButtonText
:
'
No
'
,
dangerouslyUseHTMLString
:
true
,
center
:
true
}).
then
(
value
=>
{
runTests
(
msg
.
tests
.
map
(
test
=>
test
.
name
));
}).
catch
(()
=>
{});
}
app
.
$el
.
style
.
display
=
'
block
'
;
// let hasFinishedTest = !!msg.tests.find(test => test.status === 'finished');
// if (!hasFinishedTest && firstUpdate) {
// app.$confirm('You haven\'t run any test on these two versions yet!<br />Do you want to start now?', 'Tip', {
// confirmButtonText: 'Yes',
// cancelButtonText: 'No',
// dangerouslyUseHTMLString: true,
// center: true
// }).then(value => {
// runTests(msg.tests.map(test => test.name));
// }).catch(() => {});
// }
// TODO
app
.
running
=
!!
msg
.
running
;
app
.
fullTests
=
processTestsData
(
msg
.
tests
,
app
.
fullTests
);
...
...
@@ -317,18 +468,29 @@ socket.on('abort', res => {
});
app
.
running
=
false
;
});
socket
.
on
(
'
versions
'
,
versions
=>
{
app
.
versions
=
versions
.
filter
(
version
=>
{
return
!
version
.
startsWith
(
'
2.
'
);
}).
reverse
();
if
(
!
app
.
runConfig
.
expectedVersion
)
{
app
.
runConfig
.
expectedVersion
=
app
.
versions
[
0
];
}
app
.
versions
.
unshift
(
'
local
'
);
socket
.
on
(
'
getAllTestsRuns_return
'
,
res
=>
{
app
.
testsRuns
=
res
.
runs
;
});
socket
.
on
(
'
genTestsRunReport_return
'
,
res
=>
{
window
.
open
(
res
.
reportUrl
,
'
_blank
'
);
});
function
updateTestHash
()
{
app
.
currentTestName
=
window
.
location
.
hash
.
slice
(
1
);
function
updateUrl
()
{
const
searchUrl
=
assembleParams
({
test
:
app
.
currentTestName
,
runConfig
:
JSON
.
stringify
(
app
.
runConfig
)
});
history
.
pushState
({},
''
,
location
.
pathname
+
'
?
'
+
searchUrl
);
}
updateTestHash
();
window
.
addEventListener
(
'
hashchange
'
,
updateTestHash
);
\ No newline at end of file
// Only update url when version is changed.
app
.
$watch
(
'
runConfig
'
,
(
newVal
,
oldVal
)
=>
{
if
(
!
app
.
pageInvisible
)
{
socket
.
emit
(
'
syncRunConfig
'
,
{
runConfig
:
app
.
runConfig
,
// Override server config from URL.
forceSet
:
true
});
}
},
{
deep
:
true
});
\ No newline at end of file
test/runTest/client/index.html
浏览文件 @
5826ca55
此差异已折叠。
点击以展开。
test/runTest/genReport.js
浏览文件 @
5826ca55
...
...
@@ -21,17 +21,10 @@
const
fs
=
require
(
'
fs
'
);
const
path
=
require
(
'
path
'
);
const
util
=
require
(
'
util
'
);
// const jimp = require('jimp');
const
marked
=
require
(
'
marked
'
);
const
tests
=
JSON
.
parse
(
fs
.
readFileSync
(
path
.
join
(
__dirname
,
'
tmp/__cache__.json
'
),
'
utf-8
'
));
const
{
RESULT_FILE_NAME
}
=
require
(
'
./store
'
);
const
readFileAsync
=
util
.
promisify
(
fs
.
readFile
);
function
resolveImagePath
(
imageUrl
)
{
if
(
!
imageUrl
)
{
return
''
;
...
...
@@ -108,10 +101,15 @@ async function genDetail(test) {
};
}
async
function
run
(
)
{
module
.
exports
=
async
function
(
testDir
)
{
let
sections
=
[];
let
failedTest
=
0
;
const
tests
=
JSON
.
parse
(
fs
.
readFileSync
(
path
.
join
(
testDir
,
RESULT_FILE_NAME
),
'
utf-8
'
));
for
(
let
test
of
tests
)
{
let
detail
=
await
genDetail
(
test
);
...
...
@@ -119,20 +117,12 @@ async function run() {
failedTest
++
;
let
title
=
`
${
failedTest
}
.
${
test
.
name
}
(Failed
${
detail
.
failed
}
/
${
detail
.
total
}
)`
;
console
.
log
(
title
);
// let sectionText = `
// ## ${title}
// <details>
// <summary>Click to expand!</summary>
// ${detail.content}
// </details>
// `;
let
sectionText
=
`
<div style="margin-top: 100px;height: 20px;border-top: 1px solid #aaa"></div>
<a id="
${
test
.
name
}
"></a>
##
${
title
}
<h2>
${
title
}
</h2>
${
detail
.
content
}
`
;
...
...
@@ -145,22 +135,18 @@ ${detail.content}
}
}
let
mdText
=
'
# Visual Regression Test Report
\n
\n
'
;
md
Text
+=
`
let
htmlText
=
'
<h1> Visual Regression Test Report</h1>
\n
'
;
html
Text
+=
`
<p>Total:
${
tests
.
length
}
</p>
<p>Failed:
${
failedTest
}
</p>
`
;
mdText
+=
sections
.
map
(
section
=>
{
return
`+ [
${
section
.
title
}
](#
${
section
.
id
}
) `
;
}).
join
(
'
\n
'
);
mdText
+=
sections
.
map
(
section
=>
section
.
content
).
join
(
'
\n\n
'
);
fs
.
writeFileSync
(
__dirname
+
'
/tmp-report.md
'
,
mdText
,
'
utf-8
'
);
marked
(
mdText
,
{
smartLists
:
true
},
(
err
,
res
)
=>
{
fs
.
writeFileSync
(
__dirname
+
'
/tmp-report.html
'
,
res
,
'
utf-8
'
);
});
htmlText
+=
'
<ul>
\n
'
+
sections
.
map
(
section
=>
{
return
`<li><a href="
${
section
.
id
}
">
${
section
.
title
}
</a></li>`
;
}).
join
(
'
\n
'
)
+
'
</ul>
'
;
htmlText
+=
sections
.
map
(
section
=>
section
.
content
).
join
(
'
\n\n
'
);
const
file
=
path
.
join
(
testDir
,
'
report.html
'
);
fs
.
writeFileSync
(
file
,
htmlText
,
'
utf-8
'
);
return
file
;
}
run
();
test/runTest/server.js
浏览文件 @
5826ca55
...
...
@@ -24,11 +24,24 @@ const path = require('path');
const
{
fork
}
=
require
(
'
child_process
'
);
const
semver
=
require
(
'
semver
'
);
const
{
port
,
origin
}
=
require
(
'
./config
'
);
const
{
getTestsList
,
updateTestsList
,
saveTestsList
,
mergeTestsResults
,
updateActionsMeta
}
=
require
(
'
./store
'
);
const
{
getTestsList
,
updateTestsList
,
saveTestsList
,
mergeTestsResults
,
updateActionsMeta
,
getResultBaseDir
,
getRunHash
,
getAllTestsRuns
,
delTestsRun
,
RESULTS_ROOT_DIR
,
checkStoreVersion
}
=
require
(
'
./store
'
);
const
{
prepareEChartsLib
,
getActionsFullPath
,
fetchVersions
}
=
require
(
'
./util
'
);
const
fse
=
require
(
'
fs-extra
'
);
const
fs
=
require
(
'
fs
'
);
const
open
=
require
(
'
open
'
);
const
genReport
=
require
(
'
./genReport
'
);
function
serve
()
{
const
server
=
http
.
createServer
((
request
,
response
)
=>
{
...
...
@@ -52,7 +65,7 @@ function serve() {
let
runningThreads
=
[];
let
pendingTests
;
let
aborted
=
false
;
let
running
=
false
;
function
stopRunningTests
()
{
if
(
runningThreads
)
{
...
...
@@ -65,6 +78,7 @@ function stopRunningTests() {
testOpt
.
status
=
'
unsettled
'
;
}
});
saveTestsList
();
pendingTests
=
null
;
}
}
...
...
@@ -130,11 +144,17 @@ function startTests(testsNameList, socket, {
testOpt
.
status
=
'
pending
'
;
testOpt
.
results
=
[];
});
// Save status immediately
saveTestsList
();
if
(
!
aborted
)
{
socket
.
emit
(
'
update
'
,
{
tests
:
getTestsList
(),
running
:
true
});
if
(
running
)
{
socket
.
emit
(
'
update
'
,
{
tests
:
getTestsList
(),
running
:
true
});
}
}
let
runningCount
=
0
;
function
onExit
()
{
runningCount
--
;
...
...
@@ -145,8 +165,11 @@ function startTests(testsNameList, socket, {
}
function
onUpdate
()
{
// Merge tests.
if
(
!
aborted
&&
!
noSave
)
{
socket
.
emit
(
'
update
'
,
{
tests
:
getTestsList
(),
running
:
true
});
if
(
running
&&
!
noSave
)
{
socket
.
emit
(
'
update
'
,
{
tests
:
getTestsList
(),
running
:
true
});
}
}
threadsCount
=
Math
.
min
(
threadsCount
,
pendingTests
.
length
);
...
...
@@ -163,6 +186,7 @@ function startTests(testsNameList, socket, {
'
--actual
'
,
actualVersion
,
'
--expected
'
,
expectedVersion
,
'
--renderer
'
,
renderer
||
''
,
'
--dir
'
,
getResultBaseDir
(),
...(
noHeadless
?
[
'
--no-headless
'
]
:
[]),
...(
noSave
?
[
'
--no-save
'
]
:
[])
]);
...
...
@@ -186,6 +210,7 @@ function checkPuppeteer() {
}
}
async
function
start
()
{
if
(
!
checkPuppeteer
())
{
// TODO Check version.
...
...
@@ -193,10 +218,9 @@ async function start() {
return
;
}
let
[
versions
]
=
await
Promise
.
all
([
fetchVersions
(),
updateTestsList
(
true
)
]);
let
_currentTestHash
;
let
_currentRunConfig
;
// let runtimeCode = await buildRuntimeCode();
// fse.outputFileSync(path.join(__dirname, 'tmp/testRuntime.js'), runtimeCode, 'utf-8');
...
...
@@ -204,43 +228,103 @@ async function start() {
// Start a static server for puppeteer open the html test cases.
let
{
io
}
=
serve
();
io
.
of
(
'
/client
'
).
on
(
'
connect
'
,
async
socket
=>
{
await
updateTestsList
();
const
stableVersions
=
await
fetchVersions
(
false
);
const
nightlyVersions
=
await
fetchVersions
(
true
);
stableVersions
.
unshift
(
'
local
'
);
nightlyVersions
.
unshift
(
'
local
'
);
io
.
of
(
'
/client
'
).
on
(
'
connect
'
,
async
socket
=>
{
function
abortTests
()
{
if
(
!
running
)
{
return
;
}
stopRunningTests
();
io
.
of
(
'
/client
'
).
emit
(
'
abort
'
);
aborted
=
tru
e
;
running
=
fals
e
;
}
function
emitUpdatedList
()
{
socket
.
on
(
'
syncRunConfig
'
,
async
({
runConfig
,
forceSet
})
=>
{
// First time open.
if
((
!
_currentRunConfig
||
forceSet
)
&&
runConfig
)
{
_currentRunConfig
=
runConfig
;
}
if
(
!
_currentRunConfig
)
{
return
;
}
const
expectedVersionsList
=
_currentRunConfig
.
isExpectedNightly
?
nightlyVersions
:
stableVersions
;
const
actualVersionsList
=
_currentRunConfig
.
isActualNightly
?
nightlyVersions
:
stableVersions
;
if
(
!
expectedVersionsList
.
includes
(
_currentRunConfig
.
expectedVersion
))
{
// Pick first version not local
_currentRunConfig
.
expectedVersion
=
expectedVersionsList
[
1
];
}
if
(
!
actualVersionsList
.
includes
(
_currentRunConfig
.
actualVersion
))
{
_currentRunConfig
.
actualVersion
=
'
local
'
;
}
socket
.
emit
(
'
syncRunConfig_return
'
,
{
runConfig
:
_currentRunConfig
,
expectedVersionsList
,
actualVersionsList
});
if
(
_currentTestHash
!==
getRunHash
(
_currentRunConfig
))
{
abortTests
();
}
await
updateTestsList
(
_currentTestHash
=
getRunHash
(
_currentRunConfig
),
!
running
// Set to unsettled if not running
);
socket
.
emit
(
'
update
'
,
{
tests
:
getTestsList
(),
running
:
runningThreads
.
length
>
0
});
}
});
socket
.
on
(
'
getAllTestsRuns
'
,
async
()
=>
{
socket
.
emit
(
'
getAllTestsRuns_return
'
,
{
runs
:
await
getAllTestsRuns
()
});
});
emitUpdatedList
();
socket
.
on
(
'
genTestsRunReport
'
,
async
(
params
)
=>
{
const
absPath
=
await
genReport
(
path
.
join
(
RESULTS_ROOT_DIR
,
getRunHash
(
params
))
);
const
relativeUrl
=
path
.
join
(
'
../
'
,
path
.
relative
(
__dirname
,
absPath
));
socket
.
emit
(
'
genTestsRunReport_return
'
,
{
reportUrl
:
relativeUrl
});
});
socket
.
on
(
'
fetch
'
,
(
)
=>
{
abortTests
(
);
emitUpdatedList
(
);
socket
.
on
(
'
delTestsRun
'
,
async
(
params
)
=>
{
delTestsRun
(
params
.
id
);
console
.
log
(
'
Deleted
'
,
params
.
id
);
});
socket
.
on
(
'
run
'
,
async
data
=>
{
let
startTime
=
Date
.
now
();
aborted
=
fals
e
;
running
=
tru
e
;
await
prepareEChartsLib
(
data
.
expectedVersion
);
// Expected version.
await
prepareEChartsLib
(
data
.
actualVersion
);
// Version to test
if
(
aborted
)
{
// If it is aborted when downloading echarts lib.
if
(
!
running
)
{
// If it is aborted when downloading echarts lib.
return
;
}
// TODO Should broadcast to all sockets.
try
{
if
(
!
checkStoreVersion
(
data
))
{
throw
new
Error
(
'
Unmatched store version and run version.
'
);
}
await
startTests
(
data
.
tests
,
io
.
of
(
'
/client
'
),
...
...
@@ -259,13 +343,14 @@ async function start() {
console
.
error
(
e
);
}
if
(
!
aborted
)
{
if
(
running
)
{
console
.
log
(
'
Finished
'
);
io
.
of
(
'
/client
'
).
emit
(
'
finish
'
,
{
time
:
Date
.
now
()
-
startTime
,
count
:
data
.
tests
.
length
,
threads
:
data
.
threads
});
running
=
false
;
}
else
{
console
.
log
(
'
Aborted!
'
);
...
...
@@ -273,8 +358,6 @@ async function start() {
});
socket
.
on
(
'
stop
'
,
abortTests
);
socket
.
emit
(
'
versions
'
,
versions
);
});
io
.
of
(
'
/recorder
'
).
on
(
'
connect
'
,
async
socket
=>
{
...
...
test/runTest/store.js
浏览文件 @
5826ca55
...
...
@@ -20,14 +20,30 @@
const
path
=
require
(
'
path
'
);
const
fse
=
require
(
'
fs-extra
'
);
const
fs
=
require
(
'
fs
'
);
const
glob
=
require
(
'
glob
'
);
const
glob
by
=
require
(
'
globby
'
);
const
{
testNameFromFile
}
=
require
(
'
./util
'
);
const
util
=
require
(
'
util
'
);
const
{
blacklist
,
SVGBlacklist
}
=
require
(
'
./blacklist
'
);
let
_tests
=
[];
let
_testsMap
=
{};
let
_runHash
=
''
;
const
RESULT_FILE_NAME
=
'
__result__.json
'
;
const
RESULTS_ROOT_DIR
=
path
.
join
(
__dirname
,
'
tmp
'
,
'
result
'
);
module
.
exports
.
RESULT_FILE_NAME
=
RESULT_FILE_NAME
;
module
.
exports
.
RESULTS_ROOT_DIR
=
RESULTS_ROOT_DIR
;
const
TEST_HASH_SPLITTER
=
'
__
'
;
function
convertBytes
(
bytes
)
{
const
sizes
=
[
'
Bytes
'
,
'
KB
'
,
'
MB
'
,
'
GB
'
,
'
TB
'
]
if
(
bytes
==
0
)
{
return
'
N/A
'
;
}
const
i
=
Math
.
floor
(
Math
.
log
(
bytes
)
/
Math
.
log
(
1024
));
return
(
bytes
/
Math
.
pow
(
1024
,
i
)).
toFixed
(
Math
.
min
(
1
,
i
))
+
'
'
+
sizes
[
i
]
}
class
Test
{
constructor
(
fileUrl
)
{
this
.
fileUrl
=
fileUrl
;
...
...
@@ -58,10 +74,55 @@ class Test {
}
}
function
getCacheFilePath
()
{
return
path
.
join
(
__dirname
,
'
tmp/__cache__.json
'
);;
/**
* hash of each run is mainly for storing the results.
* It depends on two versions and rendering mode.
*/
function
getRunHash
(
params
)
{
return
[
params
.
expectedVersion
,
params
.
actualVersion
,
params
.
renderer
].
join
(
TEST_HASH_SPLITTER
);
}
/**
* Parse versions and rendering mode from run hash.
*/
function
parseRunHash
(
str
)
{
const
parts
=
str
.
split
(
TEST_HASH_SPLITTER
);
return
{
expectedVersion
:
parts
[
0
],
actualVersion
:
parts
[
1
],
renderer
:
parts
[
2
]
};
}
function
getResultBaseDir
()
{
return
path
.
join
(
RESULTS_ROOT_DIR
,
_runHash
);
}
module
.
exports
.
getResultBaseDir
=
getResultBaseDir
;
module
.
exports
.
getRunHash
=
getRunHash
;
/**
* Check run version is same with store version.
*/
module
.
exports
.
checkStoreVersion
=
function
(
runParams
)
{
const
storeParams
=
parseRunHash
(
_runHash
);
console
.
log
(
'
Store
'
,
_runHash
);
return
storeParams
.
expectedVersion
===
runParams
.
expectedVersion
&&
storeParams
.
actualVersion
===
runParams
.
actualVersion
&&
storeParams
.
renderer
===
runParams
.
renderer
;
}
function
getResultFilePath
()
{
return
path
.
join
(
getResultBaseDir
(),
RESULT_FILE_NAME
);
}
module
.
exports
.
getResultFilePath
=
getResultFilePath
;
module
.
exports
.
getTestsList
=
function
()
{
return
_tests
;
};
...
...
@@ -70,15 +131,21 @@ module.exports.getTestByFileUrl = function (url) {
return
_testsMap
[
url
];
};
module
.
exports
.
updateTestsList
=
async
function
(
setPendingTestToUnsettled
)
{
let
tmpFolder
=
path
.
join
(
__dirname
,
'
tmp
'
);
fse
.
ensureDirSync
(
tmpFolder
);
module
.
exports
.
updateTestsList
=
async
function
(
runHash
,
setPendingTestToUnsettled
)
{
_runHash
=
runHash
;
_tests
=
[];
_testsMap
=
{};
_testsExists
=
{};
fse
.
ensureDirSync
(
getResultBaseDir
());
try
{
let
cachedStr
=
fs
.
readFileSync
(
get
Cache
FilePath
(),
'
utf-8
'
);
_
tests
=
JSON
.
parse
(
cachedStr
);
_
tests
.
forEach
(
test
=>
{
let
cachedStr
=
fs
.
readFileSync
(
get
Result
FilePath
(),
'
utf-8
'
);
const
tests
=
JSON
.
parse
(
cachedStr
);
tests
.
forEach
(
test
=>
{
// In somehow tests are stopped and leave the status pending.
// Set the status to unsettled again.
if
(
setPendingTestToUnsettled
)
{
...
...
@@ -90,14 +157,15 @@ module.exports.updateTestsList = async function (setPendingTestToUnsettled) {
});
}
catch
(
e
)
{
_tests
=
[];
}
// Find if there is new html file
const
files
=
await
util
.
promisify
(
glob
)(
'
*
*.html
'
,
{
cwd
:
path
.
resolve
(
__dirname
,
'
../
'
)
});
const
files
=
await
globby
(
'
*.html
'
,
{
cwd
:
path
.
resolve
(
__dirname
,
'
../
'
)
});
files
.
forEach
(
fileUrl
=>
{
if
(
blacklist
.
includes
(
fileUrl
))
{
return
;
}
_testsExists
[
fileUrl
]
=
true
;
if
(
_testsMap
[
fileUrl
])
{
return
;
}
...
...
@@ -105,10 +173,14 @@ module.exports.updateTestsList = async function (setPendingTestToUnsettled) {
const
test
=
new
Test
(
fileUrl
);
test
.
ignoreSVG
=
SVGBlacklist
.
includes
(
fileUrl
);
_tests
.
push
(
test
);
_testsMap
[
fileUrl
]
=
test
;
});
// Exclude tests that there is no HTML files.
Object
.
keys
(
_testsExists
).
forEach
(
key
=>
{
_tests
.
push
(
_testsMap
[
key
]);
});
const
actionsMetaData
=
{};
const
metaPath
=
path
.
join
(
__dirname
,
'
actions/__meta__.json
'
);
try
{
...
...
@@ -119,11 +191,15 @@ module.exports.updateTestsList = async function (setPendingTestToUnsettled) {
_tests
.
forEach
(
testOpt
=>
{
testOpt
.
actions
=
actionsMetaData
[
testOpt
.
name
]
||
0
;
});
// Save once.
module
.
exports
.
saveTestsList
();
return
_tests
;
};
module
.
exports
.
saveTestsList
=
function
()
{
fse
.
outputFileSync
(
get
Cache
FilePath
(),
JSON
.
stringify
(
_tests
,
null
,
2
),
'
utf-8
'
);
fse
.
outputFileSync
(
get
Result
FilePath
(),
JSON
.
stringify
(
_tests
,
null
,
2
),
'
utf-8
'
);
};
module
.
exports
.
mergeTestsResults
=
function
(
testsResults
)
{
...
...
@@ -147,4 +223,89 @@ module.exports.updateActionsMeta = function (testName, actions) {
fs
.
writeFileSync
(
metaPath
,
JSON
.
stringify
(
metaData
,
Object
.
keys
(
metaData
).
sort
((
a
,
b
)
=>
a
.
localeCompare
(
b
)),
2
),
'
utf-8
'
);
};
\ No newline at end of file
};
async
function
getFolderSize
(
dir
)
{
const
files
=
await
globby
(
dir
);
let
size
=
0
;
for
(
let
file
of
files
)
{
size
+=
fs
.
statSync
(
file
).
size
;
}
return
size
;
// const statAsync = promisify(fs.stat);
// return Promise.all(
// files.map(file => statAsync(file))
// ).then(sizes => {
// return sizes.reduce((total, current) => {
// return total + current.size;
// }, 0)
// });
}
/**
* Get results of all runs
* @return [ { id, expectedVersion, actualVersion, renderer, lastRunTime, total, finished, passed, diskSize } ]
*/
module
.
exports
.
getAllTestsRuns
=
async
function
()
{
const
dirs
=
await
globby
(
'
*
'
,
{
cwd
:
RESULTS_ROOT_DIR
,
onlyDirectories
:
true
});
const
results
=
[];
function
f
(
number
)
{
return
number
<
10
?
'
0
'
+
number
:
number
;
}
function
formatDate
(
lastRunTime
)
{
const
date
=
new
Date
(
lastRunTime
);
return
`
${
date
.
getFullYear
()}
-
${
f
(
date
.
getMonth
()
+
1
)}
-
${
f
(
date
.
getDate
())}
${
f
(
date
.
getHours
())}
:
${
f
(
date
.
getMinutes
())}
:
${
f
(
date
.
getSeconds
())}
`
;
}
for
(
let
dir
of
dirs
)
{
const
params
=
parseRunHash
(
dir
);
const
resultJson
=
JSON
.
parse
(
fs
.
readFileSync
(
path
.
join
(
RESULTS_ROOT_DIR
,
dir
,
RESULT_FILE_NAME
),
'
utf-8
'
));
const
total
=
resultJson
.
length
;
let
lastRunTime
=
0
;
let
finishedCount
=
0
;
let
passedCount
=
0
;
resultJson
.
forEach
(
test
=>
{
lastRunTime
=
Math
.
max
(
test
.
lastRun
,
lastRunTime
);
if
(
test
.
status
===
'
finished
'
)
{
finishedCount
++
;
let
passed
=
true
;
test
.
results
.
forEach
(
result
=>
{
// Threshold?
if
(
result
.
diffRatio
>
0.0001
)
{
passed
=
false
;
}
});
if
(
passed
)
{
passedCount
++
;
}
}
});
if
(
finishedCount
===
0
)
{
// Cleanup empty runs
await
module
.
exports
.
delTestsRun
(
dir
);
continue
;
}
params
.
lastRunTime
=
lastRunTime
>
0
?
formatDate
(
lastRunTime
)
:
'
N/A
'
;
params
.
total
=
total
;
params
.
passed
=
passedCount
;
params
.
finished
=
finishedCount
;
params
.
id
=
dir
;
params
.
diskSize
=
convertBytes
(
await
getFolderSize
(
path
.
join
(
RESULTS_ROOT_DIR
,
dir
)));
results
.
push
(
params
);
};
return
results
;
}
module
.
exports
.
delTestsRun
=
async
function
(
hash
)
{
fse
.
removeSync
(
path
.
join
(
RESULTS_ROOT_DIR
,
hash
));
}
\ No newline at end of file
test/runTest/util.js
浏览文件 @
5826ca55
...
...
@@ -67,9 +67,11 @@ module.exports.prepareEChartsLib = function (version) {
let
testLibPath
=
`
${
versionFolder
}
/
${
module
.
exports
.
getEChartsTestFileName
()}
`
;
if
(
!
fs
.
existsSync
(
testLibPath
))
{
const
file
=
fs
.
createWriteStream
(
`
${
versionFolder
}
/echarts.js`
);
const
isNightly
=
version
.
includes
(
'
-dev
'
);
const
packageName
=
isNightly
?
'
echarts-nightly
'
:
'
echarts
'
console
.
log
(
`Downloading
echarts@
${
version
}
from `
,
`https://cdn.jsdelivr.net/npm/echarts
@
${
version
}
/dist/echarts.js`
);
https
.
get
(
`https://cdn.jsdelivr.net/npm/
echarts
@
${
version
}
/dist/echarts.js`
,
response
=>
{
console
.
log
(
`Downloading
${
packageName
}
@
${
version
}
from `
,
`https://cdn.jsdelivr.net/npm/
${
packageName
}
@
${
version
}
/dist/echarts.js`
);
https
.
get
(
`https://cdn.jsdelivr.net/npm/
${
packageName
}
@
${
version
}
/dist/echarts.js`
,
response
=>
{
response
.
pipe
(
file
);
file
.
on
(
'
finish
'
,
()
=>
{
...
...
@@ -85,9 +87,13 @@ module.exports.prepareEChartsLib = function (version) {
});
};
module
.
exports
.
fetchVersions
=
function
()
{
module
.
exports
.
fetchVersions
=
function
(
isNighlty
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
https
.
get
(
`https://registry.npmjs.org/echarts`
,
res
=>
{
https
.
get
(
isNighlty
?
`https://registry.npmjs.org/echarts-nightly`
:
`https://registry.npmjs.org/echarts`
,
res
=>
{
if
(
res
.
statusCode
!==
200
)
{
res
.
destroy
();
reject
(
'
Failed fetch versions from https://registry.npmjs.org/echarts
'
);
...
...
@@ -98,7 +104,7 @@ module.exports.fetchVersions = function () {
res
.
on
(
'
end
'
,
function
()
{
try
{
var
data
=
Buffer
.
concat
(
buffers
);
resolve
(
Object
.
keys
(
JSON
.
parse
(
data
).
versions
));
resolve
(
Object
.
keys
(
JSON
.
parse
(
data
).
versions
)
.
reverse
()
);
}
catch
(
e
)
{
reject
(
e
.
toString
());
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录