提交 ea440f6b 编写于 作者: P pissang

test: Improve visual regression test UI

上级 b6c67196
module.exports = [
'-cases.html',
'geo-random-stream.html'
'-cases.html',
'geo-random-stream.html'
];
\ No newline at end of file
......@@ -140,7 +140,6 @@ async function takeScreenshot(page, elementQuery, fileUrl, desc, version) {
}
async function runTestPage(browser, fileUrl, version) {
const {keepWait, waitTimeout} = createWaitTimeout(3200);
const testResults = [];
let screenshotPromises = [];
......@@ -176,10 +175,16 @@ async function runTestPage(browser, fileUrl, version) {
let pageFinishPromise = waitPageForFinish(page);
await page.goto(`${origin}/test/${fileUrl}`, {
waitUntil: 'networkidle2',
timeout: 10000
});
try {
await page.goto(`${origin}/test/${fileUrl}`, {
waitUntil: 'networkidle2',
timeout: 10000
});
}
catch(e) {
// TODO Timeout Error
console.error(e);
}
// Do auto screenshot for every 1 second.
let count = 1;
......@@ -213,6 +218,7 @@ async function runTestPage(browser, fileUrl, version) {
}
async function runTest(browser, testOpt) {
testOpt.status === 'running';
const fileUrl = testOpt.fileUrl;
const expectedShots = await runTestPage(browser, fileUrl, '4.2.1');
const actualShots = await runTestPage(browser, fileUrl);
......@@ -238,6 +244,7 @@ async function runTest(browser, testOpt) {
expected: getClientRelativePath(expected.screenshotPath),
diff: getClientRelativePath(diffPath),
name: actual.testName,
desc: actual.desc,
diffRatio
});
});
......@@ -292,21 +299,37 @@ async function start() {
io.on('connect', socket => {
broadcast({tests});
// TODO Stop previous?
socket.on('run', async testsNameList => {
console.log(testsNameList);
const pendingTests = tests.filter(testOpt => {
return testsNameList.includes(testOpt.name);
});
for (let testOpt of pendingTests) {
// Reset all tests results
testOpt.status = 'pending';
testOpt.results = [];
}
broadcast({tests});
try {
for (let testOpt of pendingTests) {
await runTest(browser, testOpt);
broadcast({tests});
writeTestsToCache(tests);
}
}
catch(e) {
console.log(e);
}
});
});
for (let testOpt of tests) {
if (testOpt.status === 'finished') {
continue;
}
console.log(`Running test ${testOpt.fileUrl}`)
await runTest(browser, testOpt);
broadcast({tests});
writeTestsToCache(tests);
}
// runTests(browser, tests, tests);
}
start().catch(e => {
console.log('Error during test');
console.log(e);
})
\ No newline at end of file
start()
\ No newline at end of file
......@@ -10,17 +10,112 @@
font-family: "Helvetica Neue",Helvetica,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","微软雅黑",Arial,sans-serif;
}
.menu-link {
display: block;
.header {
background-color: #293c55;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
position: relative;
z-index: 10;
}
.header h1 {
color: #fff;
line-height: 50px;
margin: 0;
font-weight: 200;
font-size: 20px;
}
#logo>* {
display: inline-block;
vertical-align: middle;
}
#logo img {
height: 30px;
margin-right: 20px;
}
.nav-toolbar {
padding: 10px 10px;
background: #162436;
box-shadow: inset 0 0 5px black;
}
.nav-toolbar .controls {
margin-top: 10px;
}
.test-list {
overflow-x: hidden;
background: #293c55;
margin: 0;
padding: 0;
}
.test-list li {
list-style: none;
padding-left: 10px;
cursor: pointer;
color: #f3f3f3;
}
.test-list li a.menu-link {
display: inline-block;
text-decoration: none;
font-size: 18px;
font-size: 14px;
line-height: 40px;
color: #f3f3f3;
margin-left: 3px;
cursor: pointer;
}
.test-screenshots {
margin-top: 50px;
.test-list li.active {
background: #e43c59;
}
.test-list li:hover {
background: #162436;
}
.test-list li>* {
vertical-align: middle;
display: inline-block
}
.test-screenshots img {
.test-list .el-progress__text {
font-size: 12px!important;
}
.test-result {
margin-top: 80px;
padding: 0 20px;
}
.test-result img {
/* height: 200px; */
width: 100%;
}
.test-result h4 {
font-size: 30px;
font-weight: 200;
margin-left: -20px;
}
::-webkit-scrollbar {
height:8px;
width:8px;
transition:all 0.3s ease-in-out;
border-radius:2px;
background: transparent;
}
::-webkit-scrollbar-button {
display:none;
}
::-webkit-scrollbar-thumb {
width:8px;
min-height:15px;
background:rgba(50, 50, 50, 0.6) !important;
transition:all 0.3s ease-in-out;border-radius:2px;
}
::-webkit-scrollbar-thumb:hover {
background:rgba(0, 0, 0, 0.5) !important;
}
\ No newline at end of file
const socket = io();
function processTestsData(tests) {
tests.forEach(test => {
let passed = 0;
test.results.forEach(result => {
// Threshold?
if (result.diffRatio < 0.001) {
passed++;
}
});
test.percentage = passed === 0 ? 0 : Math.round(passed / test.results.length * 100);
if (test.percentage === 100) {
test.summary = 'success';
}
else if (test.percentage < 50) {
test.summary = 'exception';
}
else {
test.summary = 'warning'
}
test.selected = false;
});
return tests;
}
socket.on('connect', () => {
console.log('Connected');
const app = new Vue({
el: '#app',
data: {
tests: [],
selectedTestName: ''
fullTests: [],
currentTestName: '',
sortBy: 'name',
searchString: '',
running: false,
allSelected: false
},
computed: {
selectedTest() {
let selectedTest = this.tests.find(item => item.name === this.selectedTestName);
if (!selectedTest) {
selectedTest = this.tests[0];
tests() {
let sortFunc = this.sortBy === 'name'
? (a, b) => a.name.localeCompare(b.name)
: (a, b) => a.percentage - b.percentage;
if (!this.searchString) {
return this.fullTests.sort(sortFunc);
}
return this.fullTests.filter(test => {
return test.name.match(this.searchString);
}).sort(sortFunc);
},
currentTest() {
let currentTest = this.fullTests.find(item => item.name === this.currentTestName);
if (!currentTest) {
currentTest = this.fullTests[0];
}
return currentTest;
},
isSelectAllIndeterminate() {
if (!this.tests.length) {
return true;
}
return this.tests.some(test => {
return test.selected !== this.tests[0].selected;
});
}
},
methods: {
goto(url) {
window.location.hash = '#' + url;
},
toggleSort() {
this.sortBy = this.sortBy === 'name' ? 'percentage' : 'name';
},
handleSelectAllChange(val) {
// Only select filtered tests.
this.tests.forEach(test => {
test.selected = val;
});
this.isSelectAllIndeterminate = false;
},
runSelectedTests() {
this.running = true;
const tests = this.fullTests.filter(test => {
return test.selected;
}).map(test => {
return test.name
});
if (tests.length > 0) {
socket.emit('run', tests);
}
return selectedTest;
},
stopTests() {
this.running = false;
socket.emit('stop');
}
}
});
app.$el.style.display = 'block';
socket.on('broadcast', msg => {
app.tests = msg.tests;
app.fullTests = processTestsData(msg.tests);
});
function updateTestHash() {
let testName = window.location.hash.slice(1);
app.selectedTestName = testName;
app.currentTestName = testName;
}
updateTestHash();
......
......@@ -8,22 +8,58 @@
<body>
<div id="app" style="display: none">
<el-container id="main">
<el-header>
<h1>Visual Regression Test</h1>
<el-header class="header" height="50">
<div id="logo">
<img src="https://echarts.apache.org/zh/images/logo.png" />
<h1>Visual Regression Test</h1>
</div>
</el-header>
<el-container style="min-height: 0"> <!-- https://juejin.im/post/5c642f2ff265da2de660ecfc -->
<el-aside width="250px">
<el-menu class="test-list">
<el-menu-item v-for="test in tests">
<el-aside width="300px">
<div class="nav-toolbar">
<el-input v-model="searchString" size="mini" placeholder="Filter Tests"></el-input>
<div class="controls">
<el-checkbox :indeterminate="isSelectAllIndeterminate" v-model="allSelected" @change="handleSelectAllChange"></el-checkbox>
<el-button
style="margin-left: 10px"
title="Sort By Failue Percentage" @click="toggleSort" circle size="mini" type="primary" icon="el-icon-sort"
></el-button>
<!-- <el-button-group>
<el-button title="Select All" @click="selectAllTests" circle size="mini" type="primary" icon="el-icon-check"></el-button>
<el-button title="Unselect All" @click="unselectAllTests" circle size="mini" type="primary" icon="el-icon-close"></el-button>
</el-button-group> -->
<el-button-group>
<el-button title="Run Selected" @click="runSelectedTests" :loading="running" circle size="mini" type="primary" icon="el-icon-caret-right"></el-button>
<el-button v-if="running" title="Run Selected" @click="stopTests" circle size="mini" type="primary" icon="el-icon-close"></el-button>
</el-button-group>
</div>
</div>
<ul class="test-list">
<li v-for="test in tests"
:title="test.name"
:class="{active: currentTest && currentTest.name === test.name}"
@click.self="goto(test.name)"
>
<el-checkbox v-model="test.selected"></el-checkbox>
<i class="el-icon-loading" v-if="test.status === 'pending' && running"></i>
<el-progress
v-if="test.status === 'finished'"
type="circle"
:width="20"
:stroke-width="2"
:percentage="test.percentage"
:status="test.summary"
></el-progress>
<a :href="'#' + test.name" class="menu-link">{{test.name}}</a>
</el-menu-item>
</el-menu>
</li>
</ul>
</el-aside>
<el-main>
<div v-if="selectedTest">
<div class="test-screenshots" v-for="result in selectedTest.results">
<h4 class="md-title">{{result.name}}</h4>
<el-row :gutter="20">
<div v-if="currentTest">
<div class="test-result" v-for="result in currentTest.results">
<h4>{{result.desc || result.name}}</h4>
<el-row :gutter="40" class="screenshots">
<el-col :span="8">
<el-card>
<div slot="header" class="clearfix">
......@@ -52,6 +88,10 @@
</el-col>
</el-row>
</div>
<div class="test-result-nav">
</div>
</div>
</el-main>
</el-container>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册