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 e31266d test: Use child process to run test. Add logs in dashboard e31266d is described below commit e31266d212770324c813203a5e1967714978c5f3 Author: pissang <bm2736...@gmail.com> AuthorDate: Sun Sep 1 16:42:31 2019 +0800 test: Use child process to run test. Add logs in dashboard --- package.json | 7 +- test/runTest/cli.js | 239 ++++++++++++++--------------------------- test/runTest/client/client.css | 15 +++ test/runTest/client/client.js | 28 ++++- test/runTest/client/index.html | 56 +++++++++- test/runTest/config.js | 4 + test/runTest/runtime.js | 71 ------------ test/runTest/runtime/main.js | 54 ++++++++++ test/runTest/runtime/shim.js | 9 ++ test/runTest/serve.js | 26 ----- test/runTest/server.js | 113 +++++++++++++++++++ test/runTest/store.js | 72 +++++++++++++ test/runTest/util.js | 45 ++++++++ 13 files changed, 476 insertions(+), 263 deletions(-) diff --git a/package.json b/package.json index fe0c6dc..5568c99 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "scripts": { "prepublish": "node build/build.js --prepublish", - "test:visual": "node build/build.js && node test/runTest/cli.js", + "test:visual": "node test/runTest/server.js", "test": "node build/build.js" }, "dependencies": { @@ -34,15 +34,18 @@ "estraverse": "4.1.1", "fs-extra": "^0.26.7", "glob": "7.0.0", + "lolex": "^4.2.0", "open": "^6.4.0", "pixelmatch": "^5.0.2", "pngjs": "^3.4.0", "puppeteer": "^1.19.0", "rollup": "0.50.0", + "rollup-plugin-commonjs": "^8.4.1", "rollup-plugin-node-resolve": "3.0.0", "rollup-plugin-uglify": "2.0.1", "seedrandom": "^3.0.3", "serve-handler": "^6.1.1", - "slugify": "^1.3.4" + "slugify": "^1.3.4", + "socket.io": "^2.2.0" } } diff --git a/test/runTest/cli.js b/test/runTest/cli.js index 104bf5f..60b70ed 100644 --- a/test/runTest/cli.js +++ b/test/runTest/cli.js @@ -2,38 +2,15 @@ const puppeteer = require('puppeteer'); const slugify = require('slugify'); const fse = require('fs-extra'); const fs = require('fs'); -const https = require('https'); const path = require('path'); -const open = require('open'); -const util = require('util'); -const glob = require('glob'); -const {serve, origin} = require('./serve'); const compareScreenshot = require('./compareScreenshot'); -const blacklist = require('./blacklist'); - -const seedrandomCode = fs.readFileSync( - path.join(__dirname, '../../node_modules/seedrandom/seedrandom.js'), - 'utf-8' -); -const runtimeCode = fs.readFileSync(path.join(__dirname, './runtime.js'), 'utf-8'); - -function getVersionDir(version) { - version = version || 'developing'; - return `tmp/__version__/${version}`; -} +const {getTestName, getVersionDir} = require('./util'); +const {origin} = require('./config'); function getScreenshotDir() { return 'tmp/__screenshot__'; } -function getTestName(fileUrl) { - return path.basename(fileUrl, '.html'); -} - -function getCacheFilePath() { - return path.join(__dirname, 'tmp/__cache__.json'); -} - function sortScreenshots(list) { return list.sort((a, b) => { return a.testName.localeCompare(b.testName); @@ -56,35 +33,6 @@ function replaceEChartsVersion(interceptedRequest, version) { } } -function prepareEChartsVersion(version) { - let versionFolder = path.join(__dirname, getVersionDir(version)); - fse.ensureDirSync(versionFolder); - if (!version) { - // Developing version, make sure it's new build - return fse.copy( - path.join(__dirname, '../../dist/echarts.js'), - `${versionFolder}/echarts.js` - ); - } - return new Promise(resolve => { - if (!fs.existsSync(`${versionFolder}/echarts.js`)) { - const file = fs.createWriteStream(`${versionFolder}/echarts.js`); - - console.log('Downloading echarts4.2.1 from ', `https://cdn.jsdelivr.net/npm/echarts@${version}/dist/echarts.js`); - https.get(`https://cdn.jsdelivr.net/npm/echarts@${version}/dist/echarts.js`, response => { - response.pipe(file); - - file.on('finish', () => { - resolve(); - }) - }); - } - else { - resolve(); - } - }); -} - function waitPageForFinish(page) { return new Promise(resolve => { page.exposeFunction('puppeteerFinishTest', () => { @@ -130,6 +78,7 @@ async function takeScreenshot(page, elementQuery, fileUrl, desc, version) { testName += '-' + slugify(desc, { replacement: '-', lower: true }) } let screenshotPrefix = version ? 'expected' : 'actual'; + fse.ensureDirSync(path.join(__dirname, getScreenshotDir())); let screenshotPath = path.join(__dirname, `${getScreenshotDir()}/${testName}-${screenshotPrefix}.png`); await target.screenshot({ path: screenshotPath, @@ -139,15 +88,16 @@ async function takeScreenshot(page, elementQuery, fileUrl, desc, version) { return {testName, screenshotPath}; } -async function runTestPage(browser, fileUrl, version) { - const {keepWait, waitTimeout} = createWaitTimeout(3200); - const testResults = []; +async function runTestPage(browser, fileUrl, version, runtimeCode) { + const {keepWait, waitTimeout} = createWaitTimeout(1500); + const screenshots = []; + const logs = []; + const errors = []; let screenshotPromises = []; const page = await browser.newPage(); page.setRequestInterception(true); page.on('request', replaceEChartsVersion); - await page.evaluateOnNewDocument(seedrandomCode); await page.evaluateOnNewDocument(runtimeCode); let descAutoCounter = 0; @@ -160,24 +110,25 @@ async function runTestPage(browser, fileUrl, version) { return; } const {testName, screenshotPath} = result; - testResults.push({testName, desc, screenshotPath}); + screenshots.push({testName, desc, screenshotPath}); }); screenshotPromises.push(promise); return promise; }); - // page.on('console', msg => { - // console.log(msg.text()); - // }); - // page.on('pageerror', error => { - // console.error(error); - // }) + page.on('console', msg => { + logs.push(msg.text()); + }); + page.on('pageerror', error => { + errors.push(error); + }); let pageFinishPromise = waitPageForFinish(page); try { await page.goto(`${origin}/test/${fileUrl}`, { waitUntil: 'networkidle2', + // waitUntil: 'domcontentloaded', timeout: 10000 }); } @@ -186,16 +137,17 @@ async function runTestPage(browser, fileUrl, version) { console.error(e); } - // Do auto screenshot for every 1 second. - let count = 1; - let autoSnapshotInterval = setInterval(async () => { - let desc = `autogen-${count++}`; - let promise = takeScreenshot(page, '', fileUrl, desc, version) - .then(({testName, screenshotPath}) => { - testResults.push({testName, desc, screenshotPath}); - }); - screenshotPromises.push(promise); - }, 1000); + // TODO Animation + + // Do auto screenshot after 100ms for animation. + // let autoSnapshotTimeout = setTimeout(async () => { + // let desc = `Animation Interval`; + // let promise = takeScreenshot(page, '', fileUrl, desc, version) + // .then(({testName, screenshotPath}) => { + // screenshots.push({testName, desc, screenshotPath}); + // }); + // screenshotPromises.push(promise); + // }, 100); // Wait for puppeteerFinishTest() is called @@ -204,33 +156,43 @@ async function runTestPage(browser, fileUrl, version) { pageFinishPromise, waitTimeout().then(() => { // console.warn('Test timeout after 3 seconds.'); + // Final shot. + let desc = 'Final Shot'; + return takeScreenshot(page, '', fileUrl, desc, version) + .then(({testName, screenshotPath}) => { + screenshots.push({testName, desc, screenshotPath}); + }); }) ]); - clearInterval(autoSnapshotInterval); + // clearTimeout(autoSnapshotTimeout); // Wait for screenshot finished. await Promise.all(screenshotPromises); await page.close(); - return testResults; + return { + logs, + errors, + screenshots: screenshots + }; } -async function runTest(browser, testOpt) { +async function runTest(browser, testOpt, runtimeCode) { testOpt.status === 'running'; const fileUrl = testOpt.fileUrl; - const expectedShots = await runTestPage(browser, fileUrl, '4.2.1'); - const actualShots = await runTestPage(browser, fileUrl); + const expectedResult = await runTestPage(browser, fileUrl, '4.2.1', runtimeCode); + const actualResult = await runTestPage(browser, fileUrl, '', runtimeCode); - sortScreenshots(expectedShots); - sortScreenshots(actualShots); + sortScreenshots(expectedResult.screenshots); + sortScreenshots(actualResult.screenshots); - const results = []; + const screenshots = []; let idx = 0; - for (let shot of expectedShots) { + for (let shot of expectedResult.screenshots) { let expected = shot; - let actual = actualShots[idx++]; + let actual = actualResult.screenshots[idx++]; let {diffRatio, diffPNG} = await compareScreenshot( expected.screenshotPath, actual.screenshotPath @@ -239,7 +201,7 @@ async function runTest(browser, testOpt) { let diffPath = `${path.resolve(__dirname, getScreenshotDir())}/${shot.testName}-diff.png`; diffPNG.pack().pipe(fs.createWriteStream(diffPath)); - results.push({ + screenshots.push({ actual: getClientRelativePath(actual.screenshotPath), expected: getClientRelativePath(expected.screenshotPath), diff: getClientRelativePath(diffPath), @@ -249,88 +211,47 @@ async function runTest(browser, testOpt) { }); } - testOpt.results = results; + testOpt.results = screenshots; testOpt.status = 'finished'; -} + testOpt.actualLogs = actualResult.logs; + testOpt.expectedLogs = expectedResult.logs; + testOpt.actualErrors = actualResult.errors; + testOpt.expectedErrors = expectedResult.errors; -function writeTestsToCache(tests) { - fs.writeFileSync(getCacheFilePath(), JSON.stringify(tests, null, 2), 'utf-8'); } -async function getTestsList() { - let tmpFolder = path.join(__dirname, 'tmp'); - fse.ensureDirSync(tmpFolder); - try { - let cachedStr = fs.readFileSync(getCacheFilePath(), 'utf-8'); - let tests = JSON.parse(cachedStr); - return tests; - } - catch(e) { - let files = await util.promisify(glob)('**.html', { cwd: path.resolve(__dirname, '../') }); - let tests = files.filter(fileUrl => { - return blacklist.includes(fileUrl); - }).map(fileUrl => { - return { - fileUrl, - name: getTestName(fileUrl), - status: 'pending', - results: [] - }; - }); - return tests; - } -} - -async function start() { - await prepareEChartsVersion('4.2.1'); // Expected version. - await prepareEChartsVersion(); // Version to test - - fse.ensureDirSync(path.join(__dirname, getScreenshotDir())); - // Start a static server for puppeteer open the html test cases. - let {broadcast, io} = serve(); - - const browser = await puppeteer.launch({ /* headless: false */ }); - - const tests = await getTestsList(); - - io.on('connect', socket => { - socket.emit('update', {tests}); - // TODO Stop previous? - socket.on('run', async testsNameList => { - console.log(testsNameList.join(',')); - - const pendingTests = tests.filter(testOpt => { - return testsNameList.includes(testOpt.name); - }); - - for (let testOpt of pendingTests) { - // Reset all tests results - testOpt.status = 'pending'; - testOpt.results = []; - } - - socket.emit('update', {tests}); +async function runTests(pendingTests) { + const browser = await puppeteer.launch({ headless: true }); + // TODO Not hardcoded. + let runtimeCode = fs.readFileSync(path.join(__dirname, 'tmp/testRuntime.js'), 'utf-8'); + try { + for (let testOpt of pendingTests) { + console.log('Running Test', testOpt.name); try { - for (let testOpt of pendingTests) { - console.log('Running Test', testOpt.name); - await runTest(browser, testOpt); - socket.emit('update', {tests}); - writeTestsToCache(tests); - } + await runTest(browser, testOpt, runtimeCode); } - catch(e) { + catch (e) { + // Restore status + testOpt.status = 'pending'; console.log(e); } - socket.emit('finish'); - }); - }); - - console.log(`Dashboard: ${origin}/test/runTest/client/index.html`); - // open(`${origin}/test/runTest/client/index.html`); - - // runTests(browser, tests, tests); + process.send(testOpt); + } + } + catch(e) { + console.log(e); + } } -start() \ No newline at end of file +// Handling input arguments. +const testsFileUrlList = process.argv[2] || ''; +runTests(testsFileUrlList.split(',').map(fileUrl => { + return { + fileUrl, + name: getTestName(fileUrl), + results: [], + status: 'pending' + }; +})); \ No newline at end of file diff --git a/test/runTest/client/client.css b/test/runTest/client/client.css index 62c1ca3..4a16049 100644 --- a/test/runTest/client/client.css +++ b/test/runTest/client/client.css @@ -96,6 +96,21 @@ margin-left: -20px; } +.test-errors, .test-logs { + margin-top: 20px; + padding: 0 50px; +} + +.test-logs .log-item { + margin: 10px 20px; + color: #909399; +} + +.test-errors .error-item { + margin: 10px 20px; + color: #f56c6c +} + ::-webkit-scrollbar { height:8px; diff --git a/test/runTest/client/client.js b/test/runTest/client/client.js index 2b302b9..99b8b98 100644 --- a/test/runTest/client/client.js +++ b/test/runTest/client/client.js @@ -50,7 +50,12 @@ socket.on('connect', () => { tests() { let sortFunc = this.sortBy === 'name' ? (a, b) => a.name.localeCompare(b.name) - : (a, b) => a.percentage - b.percentage; + : (a, b) => { + if (a.percentage === b.percentage) { + return a.name.localeCompare(b.name); + } + return a.percentage - b.percentage; + } if (!this.searchString) { return this.fullTests.sort(sortFunc); @@ -133,10 +138,31 @@ socket.on('connect', () => { }); 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 => { + app.running = true; + socket.emit('run', msg.tests.map(test => test.name)); + }).catch(() => {}) + } + // TODO + // app.running = !!msg.running; app.fullTests = processTestsData(msg.tests, app.fullTests); + + firstUpdate = false; }); socket.on('finish', () => { + app.$notify({ + title: 'Test Complete', + position: 'bottom-right' + }) app.running = false; }); diff --git a/test/runTest/client/index.html b/test/runTest/client/index.html index a5b8994..72fd6ad 100644 --- a/test/runTest/client/index.html +++ b/test/runTest/client/index.html @@ -20,6 +20,12 @@ <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-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> + + <el-button style="margin-left: 10px" title="Sort By Failue Percentage" @click="toggleSort" circle size="mini" type="primary" icon="el-icon-sort" @@ -28,10 +34,6 @@ <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> <el-button title="Refresh List" @click="refreshList" circle size="mini" type="primary" icon="el-icon-refresh"></el-button> </div> </div> @@ -92,6 +94,48 @@ </el-row> </div> + <div class="test-errors"> + <el-row :gutter="40"> + <el-col :span="12"> + <el-alert + title="Expected Errors" + type="error" + show-icon> + </el-alert> + <div class="error-item" v-for="error in currentTest.expectedErrors">{{error}}</div> + </el-col> + <el-col :span="12"> + <el-alert + title="Actual Errors" + type="error" + show-icon> + </el-alert> + <div class="error-item" v-for="error in currentTest.actualErrors">{{error}}</div> + </el-col> + </el-row> + </div> + + <div class="test-logs"> + <el-row :gutter="40"> + <el-col :span="12"> + <el-alert + title="Expected Logs" + type="info" + show-icon> + </el-alert> + <div class="log-item" v-for="log in currentTest.expectedLogs">{{log}}</div> + </el-col> + <el-col :span="12"> + <el-alert + title="Actual Logs" + type="info" + show-icon> + </el-alert> + <div class="log-item" v-for="log in currentTest.actualLogs">{{log}}</div> + </el-col> + </el-row> + </div> + <div class="test-result-nav"> </div> @@ -99,6 +143,9 @@ </el-main> </el-container> </el-container> + + + </div> <script src="../../../node_modules/socket.io-client/dist/socket.io.js"></script> @@ -111,6 +158,7 @@ <script src="client.js"></script> <link rel="stylesheet" href="client.css"> +<link rel="stylesheet" href=""> </body> </html> \ No newline at end of file diff --git a/test/runTest/config.js b/test/runTest/config.js new file mode 100644 index 0000000..7234965 --- /dev/null +++ b/test/runTest/config.js @@ -0,0 +1,4 @@ +module.exports = { + port: 8866, + origin: 'http://localhost:8866' +}; \ No newline at end of file diff --git a/test/runTest/runtime.js b/test/runTest/runtime.js deleted file mode 100644 index 1a17c6a..0000000 --- a/test/runTest/runtime.js +++ /dev/null @@ -1,71 +0,0 @@ -(function (global) { - if (global.autorun) { - return; - } - - var autorun = {}; - var inPuppeteer = typeof puppeteerScreenshot !== 'undefined'; - - var NativeDate = window.Date; - - var fixedTimestamp = 1566458693300; - var actualTimestamp = NativeDate.now(); - - function MockDate(params) { - if (!params) { - var elapsedTime = NativeDate.now() - actualTimestamp; - return new NativeDate(fixedTimestamp + elapsedTime); - } - else { - return new NativeDate(params); - } - } - MockDate.prototype = new Date(); - - autorun.createScreenshotTest = function (desc, elementQuery, waitTime) { - - } - - /** - * Take screenshot immediately. - * @param {string} desc - * @param {string} [elementQuery] If only screenshot specifed element. Will do full page screenshot if it's not give. - */ - autorun.compareScreenshot = function (desc, elementQuery) { - if (!inPuppeteer) { - return Promise.resolve(); - } - - return puppeteerScreenshot(desc, elementQuery); - }; - - autorun.shouldBe = function (expected, actual) { - - }; - - /** - * Finish the test. - */ - autorun.finish = function () { - if (!inPuppeteer) { - return Promise.resolve(); - } - return puppeteerFinishTest(); - }; - - - if (inPuppeteer) { - let myRandom = new Math.seedrandom('echarts-test'); - // Fixed random generator - Math.random = function () { - var val = myRandom(); - return val; - }; - - // Fixed date - window.Date = MockDate; - } - - global.autorun = autorun; - -})(window); \ No newline at end of file diff --git a/test/runTest/runtime/main.js b/test/runTest/runtime/main.js new file mode 100644 index 0000000..d98016d --- /dev/null +++ b/test/runTest/runtime/main.js @@ -0,0 +1,54 @@ +import seedrandom from 'seedrandom'; +import lolex from 'lolex'; + +const NativeDate = window.Date; + +const fixedTimestamp = 1566458693300; +// const actualTimestamp = NativeDate.now(); +// function MockDate(params) { +// if (!params) { +// const elapsedTime = NativeDate.now() - actualTimestamp; +// return new NativeDate(fixedTimestamp + elapsedTime); +// } +// else { +// return new NativeDate(params); +// } +// } +// MockDate.prototype = new Date(); + +export function createScreenshotTest (desc, elementQuery, waitTime) { + +} + +/** + * Take screenshot immediately. + * @param {string} desc + * @param {string} [elementQuery] If only screenshot specifed element. Will do full page screenshot if it's not give. + */ +export function compareScreenshot (desc, elementQuery) { + return puppeteerScreenshot(desc, elementQuery); +}; + +/** + * Finish the test. + */ +export function finish() { + return puppeteerFinishTest(); +}; + + +let myRandom = new seedrandom('echarts-test'); +// Fixed random generator +Math.random = function () { + const val = myRandom(); + return val; +}; + +// Fixed date +// window.Date = MockDate; + +lolex.install({ + shouldAdvanceTime: true, + advanceTimeDelta: 50, + now: fixedTimestamp +}); diff --git a/test/runTest/runtime/shim.js b/test/runTest/runtime/shim.js new file mode 100644 index 0000000..d0febb4 --- /dev/null +++ b/test/runTest/runtime/shim.js @@ -0,0 +1,9 @@ +(function () { + if (typeof autorun !== 'undefined') { + return; + } + var autorun = {}; + autorun.createScreenshotTest = function () {}; + autorun.compareScreenshot = function () {}; + autorun.finish = function () {}; +})(); \ No newline at end of file diff --git a/test/runTest/serve.js b/test/runTest/serve.js deleted file mode 100644 index 1baade6..0000000 --- a/test/runTest/serve.js +++ /dev/null @@ -1,26 +0,0 @@ -const handler = require('serve-handler'); -const http = require('http'); -const port = 8866; -const origin = `http://localhost:${port}`; - -function serve() { - const server = http.createServer((request, response) => { - return handler(request, response, { - cleanUrls: false, - // Root folder of echarts - public: __dirname + '/../../' - }); - }); - - server.listen(port, () => { - console.log(`Server started. ${origin}`); - }); - - - const io = require('socket.io')(server); - return { - io - } -} - -module.exports = {serve, origin}; \ No newline at end of file diff --git a/test/runTest/server.js b/test/runTest/server.js new file mode 100644 index 0000000..a35c51e --- /dev/null +++ b/test/runTest/server.js @@ -0,0 +1,113 @@ +const handler = require('serve-handler'); +const http = require('http'); +const rollup = require('rollup'); +const resolve = require('rollup-plugin-node-resolve'); +const commonjs = require('rollup-plugin-commonjs'); +const path = require('path'); +const open = require('open'); +const fse = require('fs-extra'); +const {fork} = require('child_process'); +const {port, origin} = require('./config'); +const {getTestsList, prepareTestsList, saveTestsList, mergeTestsResults} = require('./store'); +const {prepareEChartsVersion} = require('./util'); + +function serve() { + const server = http.createServer((request, response) => { + return handler(request, response, { + cleanUrls: false, + // Root folder of echarts + public: __dirname + '/../../' + }); + }); + + server.listen(port, () => { + console.log(`Server started. ${origin}`); + }); + + + const io = require('socket.io')(server); + return { + io + }; +}; + +async function buildRuntimeCode() { + const bundle = await rollup.rollup({ + input: path.join(__dirname, 'runtime/main.js'), + plugins: [ + resolve(), + commonjs() + ] + }); + const output = await bundle.generate({ + format: 'iife', + name: 'autorun' + }); + + return output.code; +} + + +function startTests(testsNameList, socket) { + console.log(testsNameList.join(',')); + + return new Promise(resolve => { + const pendingTests = getTestsList().filter(testOpt => { + return testsNameList.includes(testOpt.name); + }); + + for (let testOpt of pendingTests) { + // Reset all tests results + testOpt.status = 'pending'; + testOpt.results = []; + } + + socket.emit('update', {tests: getTestsList()}); + + let childProcess = fork(path.join(__dirname, 'cli.js'), [ + pendingTests.map(testOpt => testOpt.fileUrl) + ]); + // Finished one test + childProcess.on('message', testOpt => { + mergeTestsResults([testOpt]); + // Merge tests. + socket.emit('update', {tests: getTestsList(), running: true}); + saveTestsList(); + }); + // Finished all + childProcess.on('exit', () => { + resolve(); + }); + }); +} + +async function start() { + await prepareEChartsVersion('4.2.1'); // Expected version. + await prepareEChartsVersion(); // Version to test + + let runtimeCode = await buildRuntimeCode(); + // seedrandom use crypto as external module. Set it to null to avoid not defined error. + // TODO + runtimeCode = 'window.crypto = null\n' + runtimeCode; + fse.outputFileSync(path.join(__dirname, 'tmp/testRuntime.js'), runtimeCode, 'utf-8'); + + // Start a static server for puppeteer open the html test cases. + let {io} = serve(); + + io.on('connect', async socket => { + await prepareTestsList(); + + socket.emit('update', {tests: getTestsList()}); + // TODO Stop previous? + socket.on('run', async testsNameList => { + await startTests(testsNameList, socket); + socket.emit('finish'); + }); + }); + + console.log(`Dashboard: ${origin}/test/runTest/client/index.html`); + // open(`${origin}/test/runTest/client/index.html`); + +} + +start(); \ No newline at end of file diff --git a/test/runTest/store.js b/test/runTest/store.js new file mode 100644 index 0000000..dbfaae9 --- /dev/null +++ b/test/runTest/store.js @@ -0,0 +1,72 @@ +const path = require('path'); +const fse = require('fs-extra'); +const fs = require('fs'); +const glob = require('glob'); +const {getTestName} = require('./util'); +const util = require('util'); +const blacklist = require('./blacklist'); + +let _tests = []; +let _testsMap = {}; + +function getCacheFilePath() { + return path.join(__dirname, 'tmp/__cache__.json');; +} + +module.exports.getTestsList = function () { + return _tests; +}; + +module.exports.getTestByFileUrl = function (url) { + return _testsMap[url]; +}; + +module.exports.prepareTestsList = async function () { + let tmpFolder = path.join(__dirname, 'tmp'); + fse.ensureDirSync(tmpFolder); + _tests = []; + _testsMap = {}; + try { + let cachedStr = fs.readFileSync(getCacheFilePath(), 'utf-8'); + _tests = JSON.parse(cachedStr); + _tests.forEach(test => { + _testsMap[test.fileUrl] = test; + }); + } + catch(e) { + _tests = []; + } + // Find if there is new html file + let files = await util.promisify(glob)('**.html', { cwd: path.resolve(__dirname, '../') }); + files.forEach(fileUrl => { + if (blacklist.includes(fileUrl)) { + return; + } + if (_testsMap[fileUrl]) { + return; + } + + let test = { + fileUrl, + name: getTestName(fileUrl), + status: 'pending', + results: [] + }; + + _tests.push(test); + _testsMap[fileUrl] = test; + }); + return _tests; +}; + +module.exports.saveTestsList = function () { + fse.outputFileSync(getCacheFilePath(), JSON.stringify(_tests, null, 2), 'utf-8'); +}; + +module.exports.mergeTestsResults = function (testsResults) { + testsResults.forEach(testResult => { + if (_testsMap[testResult.fileUrl]) { + Object.assign(_testsMap[testResult.fileUrl], testResult); + } + }); +}; \ No newline at end of file diff --git a/test/runTest/util.js b/test/runTest/util.js new file mode 100644 index 0000000..cc72123 --- /dev/null +++ b/test/runTest/util.js @@ -0,0 +1,45 @@ +const path = require('path'); +const fse = require('fs-extra'); +const https = require('https'); +const fs = require('fs'); + +module.exports.getTestName = function(fileUrl) { + return path.basename(fileUrl, '.html'); +}; + + +function getVersionDir(version) { + version = version || 'developing'; + return `tmp/__version__/${version}`; +}; +module.exports.getVersionDir = getVersionDir; + + +module.exports.prepareEChartsVersion = function (version) { + let versionFolder = path.join(__dirname, getVersionDir(version)); + fse.ensureDirSync(versionFolder); + if (!version) { + // Developing version, make sure it's new build + return fse.copy( + path.join(__dirname, '../../dist/echarts.js'), + `${versionFolder}/echarts.js` + ); + } + return new Promise(resolve => { + if (!fs.existsSync(`${versionFolder}/echarts.js`)) { + const file = fs.createWriteStream(`${versionFolder}/echarts.js`); + + console.log('Downloading echarts4.2.1 from ', `https://cdn.jsdelivr.net/npm/echarts@${version}/dist/echarts.js`); + https.get(`https://cdn.jsdelivr.net/npm/echarts@${version}/dist/echarts.js`, response => { + response.pipe(file); + + file.on('finish', () => { + resolve(); + }); + }); + } + else { + resolve(); + } + }); +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@echarts.apache.org For additional commands, e-mail: commits-h...@echarts.apache.org