This is an automated email from the ASF dual-hosted git repository. shenyi pushed a commit to branch test-autorun in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
The following commit(s) were added to refs/heads/test-autorun by this push: new ea440f6 test: Improve visual regression test UI ea440f6 is described below commit ea440f6b2584ad60e528f29627e5122d92ce6e7f Author: pissang <bm2736...@gmail.com> AuthorDate: Thu Aug 29 20:33:55 2019 +0800 test: Improve visual regression test UI --- test/runTest/blacklist.js | 4 +- test/runTest/cli.js | 59 ++++++++++++++++------- test/runTest/client/client.css | 107 ++++++++++++++++++++++++++++++++++++++--- test/runTest/client/client.js | 102 +++++++++++++++++++++++++++++++++++---- test/runTest/client/index.html | 62 +++++++++++++++++++----- 5 files changed, 287 insertions(+), 47 deletions(-) diff --git a/test/runTest/blacklist.js b/test/runTest/blacklist.js index e059607..77cdd23 100644 --- a/test/runTest/blacklist.js +++ b/test/runTest/blacklist.js @@ -1,4 +1,4 @@ module.exports = [ -'-cases.html', -'geo-random-stream.html' + '-cases.html', + 'geo-random-stream.html' ]; \ No newline at end of file diff --git a/test/runTest/cli.js b/test/runTest/cli.js index b560072..69d3188 100644 --- a/test/runTest/cli.js +++ b/test/runTest/cli.js @@ -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 diff --git a/test/runTest/client/client.css b/test/runTest/client/client.css index 22ce528..62c1ca3 100644 --- a/test/runTest/client/client.css +++ b/test/runTest/client/client.css @@ -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 diff --git a/test/runTest/client/client.js b/test/runTest/client/client.js index 5bc96b6..716fdad 100644 --- a/test/runTest/client/client.js +++ b/test/runTest/client/client.js @@ -1,32 +1,114 @@ 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(); diff --git a/test/runTest/client/index.html b/test/runTest/client/index.html index 8bfdf3b..e9797ec 100644 --- a/test/runTest/client/index.html +++ b/test/runTest/client/index.html @@ -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> --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For additional commands, e-mail: commits-h...@echarts.apache.org