diff --git a/test/runTest/cli.js b/test/runTest/cli.js index 4ba445f78e..0448f6821b 100644 --- a/test/runTest/cli.js +++ b/test/runTest/cli.js @@ -39,7 +39,9 @@ program .option('--no-headless', 'Not headless') .option('-s, --speed ', 'Playback speed') .option('--expected ', 'Expected version') + .option('--expected-source ', 'Expected source') .option('--actual ', 'Actual version') + .option('--actual-source ', 'Actual source') .option('--renderer ', 'svg/canvas renderer') .option('--use-coarse-pointer ', '"auto" (by default) or "true" or "false"') .option('--threads ', 'How many threads to run concurrently') @@ -78,12 +80,12 @@ function getClientRelativePath(absPath) { return path.join('../', path.relative(__dirname, absPath)); } -function replaceEChartsVersion(interceptedRequest, version) { +function replaceEChartsVersion(interceptedRequest, source, version) { // TODO Extensions and maps if (interceptedRequest.url().endsWith('dist/echarts.js')) { - console.log('Use echarts version: ' + version); + console.log('Use echarts version: ' + source + ' ' + version); interceptedRequest.continue({ - url: `${origin}/test/runTest/${getVersionDir(version)}/${getEChartsTestFileName()}` + url: `${origin}/test/runTest/${getVersionDir(source, version)}/${getEChartsTestFileName()}` }); } else { @@ -167,7 +169,7 @@ async function waitForNetworkIdle(page) { /** * @param {puppeteer.Browser} browser */ -async function runTestPage(browser, testOpt, version, runtimeCode, isExpected) { +async function runTestPage(browser, testOpt, source, version, runtimeCode, isExpected) { const fileUrl = testOpt.fileUrl; const screenshots = []; const logs = []; @@ -175,7 +177,7 @@ async function runTestPage(browser, testOpt, version, runtimeCode, isExpected) { const page = await browser.newPage(); page.setRequestInterception(true); - page.on('request', request => replaceEChartsVersion(request, version)); + page.on('request', request => replaceEChartsVersion(request, source, version)); async function pageScreenshot() { if (!program.save) { @@ -338,12 +340,12 @@ function writePNG(diffPNG, diffPath) { /** * @param {puppeteer.Browser} browser */ -async function runTest(browser, testOpt, runtimeCode, expectedVersion, actualVersion) { +async function runTest(browser, testOpt, runtimeCode, expectedSource, expectedVersion, actualSource, actualVersion) { if (program.save) { testOpt.status === 'running'; - const expectedResult = await runTestPage(browser, testOpt, expectedVersion, runtimeCode, true); - const actualResult = await runTestPage(browser, testOpt, actualVersion, runtimeCode, false); + const expectedResult = await runTestPage(browser, testOpt, expectedSource, expectedVersion, runtimeCode, true); + const actualResult = await runTestPage(browser, testOpt, actualSource, actualVersion, runtimeCode, false); // sortScreenshots(expectedResult.screenshots); // sortScreenshots(actualResult.screenshots); @@ -432,7 +434,7 @@ async function runTests(pendingTests) { async function eachTask(testOpt) { console.log(`Running test: ${testOpt.name}, renderer: ${program.renderer}, useCoarsePointer: ${program.useCoarsePointer}`); try { - await runTest(browser, testOpt, runtimeCode, program.expected, program.actual); + await runTest(browser, testOpt, runtimeCode, program.expectedSource, program.expected, program.actualSource, program.actual); } catch (e) { // Restore status diff --git a/test/runTest/client/client.js b/test/runTest/client/client.js index 0c34e0ff0e..ba8d811177 100644 --- a/test/runTest/client/client.js +++ b/test/runTest/client/client.js @@ -21,6 +21,8 @@ const socket = io('/client'); // const LOCAL_SAVE_KEY = 'visual-regression-testing-config'; +let handlingSourceChange = false; + function getChangedObject(target, source) { let changedObject = {}; Object.keys(source).forEach(key => { @@ -83,9 +85,15 @@ function processTestsData(tests, oldTestsData) { // Keep select status not change. if (oldTestsData && oldTestsData[idx]) { test.selected = oldTestsData[idx].selected; + // Keep source information + test.expectedSource = oldTestsData[idx].expectedSource; + test.actualSource = oldTestsData[idx].actualSource; } else { test.selected = false; + // Initialize source information + test.expectedSource = app.runConfig.expectedSource; + test.actualSource = app.runConfig.actualSource; } }); return tests; @@ -101,6 +109,22 @@ try { } catch (e) {} +function getVersionFromSource(source, versions, nightlyVersions) { + if (source === 'PR') { + // Default PR version can be empty since it needs to be manually selected + return '#'; + } + else if (source === 'nightly') { + return nightlyVersions.length ? nightlyVersions[0] : null; + } + else if (source === 'local') { + return 'local'; + } + else { + return versions.length ? versions[0] : null; + } +} + const app = new Vue({ el: '#app', data: { @@ -112,9 +136,6 @@ const app = new Vue({ allSelected: false, lastSelectedIndex: -1, - expectedVersionsList: [], - actualVersionsList: [], - loadingVersion: false, showIframeDialog: false, @@ -128,21 +149,27 @@ const app = new Vue({ pageInvisible: false, + versions: [], + nightlyVersions: [], + prVersions: [], + branchVersions: [], + runConfig: Object.assign({ sortBy: 'name', - - isActualNightly: false, - isExpectedNightly: false, actualVersion: 'local', expectedVersion: null, - + expectedSource: 'release', + actualSource: 'release', renderer: 'canvas', useCoarsePointer: 'auto', threads: 4 }, urlRunConfig) }, - mounted() { + async mounted() { + // Add call to fetch branches + await this.fetchBranchVersions(); + // Sync config from server when first time open // or switching back socket.emit('syncRunConfig', { @@ -151,12 +178,38 @@ const app = new Vue({ 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)); + this.versions = res.versions || []; + this.nightlyVersions = res.nightlyVersions || []; + this.prVersions = res.prVersions || []; + + // Only set versions if they haven't been manually set + handlingSourceChange = true; + this.$nextTick(() => { + if (!this.runConfig.expectedVersion) { + this.runConfig.expectedVersion = getVersionFromSource( + this.runConfig.expectedSource, + this.versions, + this.nightlyVersions + ); + } - updateUrl(); + if (!this.runConfig.actualVersion) { + this.runConfig.actualVersion = getVersionFromSource( + this.runConfig.actualSource, + this.versions, + this.nightlyVersions + ); + } + + // Only apply other config changes from server + const configWithoutVersions = { ...res.runConfig }; + delete configWithoutVersions.expectedVersion; + delete configWithoutVersions.actualVersion; + Object.assign(this.runConfig, getChangedObject(this.runConfig, configWithoutVersions)); + + handlingSourceChange = false; + updateUrl(); + }); }); setTimeout(() => { @@ -172,6 +225,16 @@ const app = new Vue({ this.pageInvisible = true; } }); + + socket.on('run_error', err => { + app.$notify({ + title: 'Error', + message: err.message, + type: 'error', + duration: 5000 + }); + app.running = false; + }); }, computed: { @@ -254,6 +317,36 @@ const app = new Vue({ }); }, set() {} + }, + + expectedVersionsList() { + switch (this.runConfig.expectedSource) { + case 'release': + return this.versions; + case 'nightly': + return this.nightlyVersions; + case 'PR': + return this.prVersions; + case 'local': + return ['local']; + default: + return []; + } + }, + + actualVersionsList() { + switch (this.runConfig.actualSource) { + case 'release': + return this.versions; + case 'nightly': + return this.nightlyVersions; + case 'PR': + return this.prVersions; + case 'local': + return ['local']; + default: + return []; + } } }, @@ -266,6 +359,44 @@ const app = new Vue({ 'currentTestName'(newVal, oldVal) { updateUrl(); + }, + + 'runConfig.expectedSource': { + handler(newVal, oldVal) { + if (newVal === oldVal) { + return; + } + + handlingSourceChange = true; + this.$nextTick(() => { + this.runConfig.expectedVersion = getVersionFromSource( + newVal, + this.versions, + this.nightlyVersions + ); + handlingSourceChange = false; + }); + }, + deep: false + }, + + 'runConfig.actualSource': { + handler(newVal, oldVal) { + if (newVal === oldVal) { + return; + } + + handlingSourceChange = true; + this.$nextTick(() => { + this.runConfig.actualVersion = getVersionFromSource( + newVal, + this.versions, + this.nightlyVersions + ); + handlingSourceChange = false; + }); + }, + deep: false } }, @@ -339,8 +470,12 @@ const app = new Vue({ let searches = []; let ecVersion = test[version + 'Version']; + let ecSource = test[version + 'Source']; if (ecVersion !== 'local') { - searches.push('__ECDIST__=' + ecVersion); + let distPath = ecSource === 'PR' + ? 'pr-' + ecVersion.replace(/^#/, '') + : ecVersion; + searches.push('__ECDIST__=' + distPath); } if (test.useSVG) { searches.push('__RENDERER__=svg'); @@ -367,8 +502,6 @@ const app = new Vue({ 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.runConfig.useCoarsePointer = runResult.useCoarsePointer; @@ -397,6 +530,17 @@ const app = new Vue({ open(url, target) { window.open(url, target); + }, + + async fetchBranchVersions() { + try { + const response = await fetch('https://api.github.com/repos/apache/echarts/branches?per_page=100'); + const branches = await response.json(); + this.branchVersions = branches.map(branch => branch.name); + } catch (error) { + console.error('Failed to fetch branches:', error); + this.branchVersions = []; + } } } }); @@ -419,7 +563,9 @@ function runTests(tests, noHeadless) { app.running = true; socket.emit('run', { tests, + expectedSource: app.runConfig.expectedSource, expectedVersion: app.runConfig.expectedVersion, + actualSource: app.runConfig.actualSource, actualVersion: app.runConfig.actualVersion, threads: app.runConfig.threads, renderer: app.runConfig.renderer, @@ -498,11 +644,19 @@ function updateUrl() { // Only update url when version is changed. app.$watch('runConfig', (newVal, oldVal) => { - if (!app.pageInvisible) { + if (!app.pageInvisible && !handlingSourceChange) { socket.emit('syncRunConfig', { runConfig: app.runConfig, - // Override server config from URL. forceSet: true + }, err => { + if (err) { + app.$notify({ + title: 'Error', + message: err, + type: 'error', + duration: 5000 + }); + } }); } }, { deep: true }); \ No newline at end of file diff --git a/test/runTest/client/index.html b/test/runTest/client/index.html index df8cdc9a78..f5270a90a5 100644 --- a/test/runTest/client/index.html +++ b/test/runTest/client/index.html @@ -1,4 +1,3 @@ - + Actual - - - - + + + + + + +
Renderer diff --git a/test/runTest/server.js b/test/runTest/server.js index 5361a1b940..a6f7e29337 100644 --- a/test/runTest/server.js +++ b/test/runTest/server.js @@ -48,9 +48,10 @@ const { getAllTestsRuns, delTestsRun, RESULTS_ROOT_DIR, - checkStoreVersion + checkStoreVersion, + clearStaledResults } = require('./store'); -const {prepareEChartsLib, getActionsFullPath, fetchVersions} = require('./util'); +const {prepareEChartsLib, getActionsFullPath, fetchVersions, cleanBranchDirectory, fetchRecentPRs} = require('./util'); const fse = require('fs-extra'); const fs = require('fs'); const open = require('open'); @@ -62,6 +63,8 @@ console.info(chalk.green('useCNMirror:'), useCNMirror); const CLI_FIXED_THREADS_COUNT = 1; function serve() { + clearStaledResults(); + const server = http.createServer((request, response) => { return handler(request, response, { cleanUrls: false, @@ -144,7 +147,9 @@ function startTests(testsNameList, socket, { noHeadless, threadsCount, replaySpeed, + actualSource, actualVersion, + expectedSource, expectedVersion, renderer, useCoarsePointer, @@ -206,6 +211,8 @@ function startTests(testsNameList, socket, { '--speed', replaySpeed || 5, '--actual', actualVersion, '--expected', expectedVersion, + '--actual-source', actualSource, + '--expected-source', expectedSource, '--renderer', renderer || '', '--use-coarse-pointer', useCoarsePointer, '--threads', Math.min(threadsCount, CLI_FIXED_THREADS_COUNT), @@ -230,8 +237,11 @@ function checkPuppeteer() { async function start() { + // Clean PR directories before starting + const {cleanPRDirectories} = require('./util'); + cleanPRDirectories(); + if (!checkPuppeteer()) { - // TODO Check version. console.error(`Can't find puppeteer >= 9.0.0, run 'npm install' to update in the 'test/runTest' folder`); return; } @@ -241,8 +251,6 @@ async function start() { try { stableVersions = await fetchVersions(false, useCNMirror); nightlyVersions = (await fetchVersions(true, useCNMirror)).slice(0, 100); - stableVersions.unshift('local'); - nightlyVersions.unshift('local'); } catch (e) { console.error('Failed to fetch version list:', e); console.log(`Try again later or try the CN mirror with: ${chalk.yellow('npm run test:visual -- -- --useCNMirror')}`) @@ -283,20 +291,10 @@ async function start() { 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 + versions: stableVersions, + nightlyVersions: nightlyVersions }); if (_currentTestHash !== getRunHash(_currentRunConfig)) { @@ -320,6 +318,7 @@ async function start() { }); socket.on('genTestsRunReport', async (params) => { + console.log('genTestsRunReport', params); const absPath = await genReport( path.join(RESULTS_ROOT_DIR, getRunHash(params)) ); @@ -340,8 +339,18 @@ async function start() { let startTime = Date.now(); - await prepareEChartsLib(data.expectedVersion, useCNMirror); // Expected version. - await prepareEChartsLib(data.actualVersion, useCNMirror); // Version to test + try { + await prepareEChartsLib(data.expectedSource, data.expectedVersion, useCNMirror); + await prepareEChartsLib(data.actualSource, data.actualVersion, useCNMirror); + } + catch (e) { + console.error(e); + // Send error to client + socket.emit('run_error', { + message: e.toString() + }); + return; + } // If aborted in the time downloading lib. if (isAborted) { @@ -349,30 +358,27 @@ async function start() { } // 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'), - { - noHeadless: data.noHeadless, - threadsCount: data.threads, - replaySpeed: data.replaySpeed, - actualVersion: data.actualVersion, - expectedVersion: data.expectedVersion, - renderer: data.renderer, - useCoarsePointer: data.useCoarsePointer, - noSave: false - } - ); - } - catch (e) { - console.error(e); + if (!checkStoreVersion(data)) { + throw new Error('Unmatched store version and run version.'); } + await startTests( + data.tests, + io.of('/client'), + { + noHeadless: data.noHeadless, + threadsCount: data.threads, + replaySpeed: data.replaySpeed, + actualSource: data.actualSource, + actualVersion: data.actualVersion, + expectedSource: data.expectedSource, + expectedVersion: data.expectedVersion, + renderer: data.renderer, + useCoarsePointer: data.useCoarsePointer, + noSave: false + } + ); + if (!isAborted) { const deltaTime = Date.now() - startTime; console.log('Finished in ', Math.round(deltaTime / 1000) + ' second'); diff --git a/test/runTest/store.js b/test/runTest/store.js index 3430bd47e8..21d1d2d49e 100644 --- a/test/runTest/store.js +++ b/test/runTest/store.js @@ -77,9 +77,19 @@ class Test { * It depends on two versions and rendering mode. */ function getRunHash(params) { + // Replace # with PR- in the hash to avoid URL issues + const expectedVersion = params.expectedSource === 'PR' + ? params.expectedVersion.replace('#', 'PR-') + : params.expectedVersion; + const actualVersion = params.actualSource === 'PR' + ? params.actualVersion.replace('#', 'PR-') + : params.actualVersion; + return [ - params.expectedVersion, - params.actualVersion, + params.expectedSource, + expectedVersion, + params.actualSource, + actualVersion, params.renderer, params.useCoarsePointer ].join(TEST_HASH_SPLITTER); @@ -90,11 +100,21 @@ function getRunHash(params) { */ function parseRunHash(str) { const parts = str.split(TEST_HASH_SPLITTER); + // Convert back PR-123 to #123 for PR versions + const expectedVersion = parts[0] === 'PR' + ? parts[1].replace('PR-', '#') + : parts[1]; + const actualVersion = parts[2] === 'PR' + ? parts[3].replace('PR-', '#') + : parts[3]; + return { - expectedVersion: parts[0], - actualVersion: parts[1], - renderer: parts[2], - useCoarsePointer: parts[3] + expectedSource: parts[0], + expectedVersion: expectedVersion, + actualSource: parts[2], + actualVersion: actualVersion, + renderer: parts[4], + useCoarsePointer: parts[5] }; } @@ -102,6 +122,22 @@ function getResultBaseDir() { return path.join(RESULTS_ROOT_DIR, _runHash); } +module.exports.clearStaledResults = async function () { + // If split by __ and there is no 6 parts, it is staled. + try { + const dirs = await globby('*', { cwd: RESULTS_ROOT_DIR, onlyDirectories: true }); + for (let dir of dirs) { + const parts = dir.split(TEST_HASH_SPLITTER); + if (parts.length !== 6) { + await module.exports.delTestsRun(dir); + } + } + } + catch(e) { + console.error('Failed to clear staled results', e); + } +} + module.exports.getResultBaseDir = getResultBaseDir; module.exports.getRunHash = getRunHash; @@ -111,7 +147,9 @@ module.exports.getRunHash = getRunHash; module.exports.checkStoreVersion = function (runParams) { const storeParams = parseRunHash(_runHash); console.log('Store ', _runHash); - return storeParams.expectedVersion === runParams.expectedVersion + return storeParams.expectedSource === runParams.expectedSource + && storeParams.expectedVersion === runParams.expectedVersion + && storeParams.actualSource === runParams.actualSource && storeParams.actualVersion === runParams.actualVersion && storeParams.renderer === runParams.renderer && storeParams.useCoarsePointer === runParams.useCoarsePointer; @@ -301,14 +339,22 @@ module.exports.getAllTestsRuns = async function () { 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); + const runData = { + expectedSource: params.expectedSource, + expectedVersion: params.expectedVersion, + actualSource: params.actualSource, + actualVersion: params.actualVersion, + renderer: params.renderer, + useCoarsePointer: params.useCoarsePointer, + lastRunTime: lastRunTime > 0 ? formatDate(lastRunTime) : 'N/A', + total: total, + passed: passedCount, + finished: finishedCount, + id: dir, + diskSize: convertBytes(await getFolderSize(path.join(RESULTS_ROOT_DIR, dir))) + }; + + results.push(runData); }; return results; } diff --git a/test/runTest/util.js b/test/runTest/util.js index fd718cd3c4..751b6a1bb3 100644 --- a/test/runTest/util.js +++ b/test/runTest/util.js @@ -42,10 +42,15 @@ module.exports.fileNameFromTest = function (testName) { return testName + '.html'; }; -function getVersionDir(version) { +function getVersionDir(source, version) { version = version || 'local'; + if (source === 'PR') { + // For PR preview artifacts + const prNumber = version.replace(/^#/, ''); + return `tmp/__version__/pr-${prNumber}`; + } return `tmp/__version__/${version}`; -}; +} module.exports.getVersionDir = getVersionDir; module.exports.getActionsFullPath = function (testName) { @@ -56,49 +61,84 @@ module.exports.getEChartsTestFileName = function () { return `echarts.test-${config.testVersion}.js`; }; -module.exports.prepareEChartsLib = function (version, useCNMirror) { +// Clean PR directories at the start of initing because PR code may change +module.exports.cleanPRDirectories = function () { + const baseDir = path.join(__dirname, 'tmp/__version__'); + if (fs.existsSync(baseDir)) { + const dirs = fs.readdirSync(baseDir); + dirs.forEach(dir => { + if (dir.startsWith('pr-')) { + fse.removeSync(path.join(baseDir, dir)); + } + }); + } +} + +module.exports.prepareEChartsLib = function (source, version, useCNMirror) { + console.log(`Preparing ECharts lib: ${source} ${version}`); - const versionFolder = path.join(__dirname, getVersionDir(version)); + const versionFolder = path.join(__dirname, getVersionDir(source, version)); const ecDownloadPath = `${versionFolder}/echarts.js`; + const testLibPath = `${versionFolder}/${module.exports.getEChartsTestFileName()}`; + fse.ensureDirSync(versionFolder); + if (!version || version === 'local') { // Developing version, make sure it's new build - fse.copySync(path.join(__dirname, '../../dist/echarts.js'), `${versionFolder}/echarts.js`); + fse.copySync(path.join(__dirname, '../../dist/echarts.js'), ecDownloadPath); let code = modifyEChartsCode(fs.readFileSync(ecDownloadPath, 'utf-8')); - fs.writeFileSync(`${versionFolder}/${module.exports.getEChartsTestFileName()}`, code, 'utf-8'); - + fs.writeFileSync(testLibPath, code, 'utf-8'); + return Promise.resolve(); + } + else if (fs.existsSync(ecDownloadPath) && fs.existsSync(testLibPath) && + fs.statSync(ecDownloadPath).size > 0 && fs.statSync(testLibPath).size > 0) { return Promise.resolve(); } + return new Promise((resolve, reject) => { - const testLibPath = `${versionFolder}/${module.exports.getEChartsTestFileName()}`; - if (!fs.existsSync(ecDownloadPath)) { - const file = fs.createWriteStream(ecDownloadPath); - const isNightly = version.includes('-dev'); - const packageName = isNightly ? 'echarts-nightly' : 'echarts' + let url; - const url = useCNMirror - ? `https://registry.npmmirror.com/${packageName}/${version}/files/dist/echarts.js` - : `https://unpkg.com/${packageName}@${version}/dist/echarts.js`; - console.log(`Downloading ${packageName}@${version} from ${url}`); - https.get(url, response => { - response.pipe(file); - - file.on('finish', () => { - const code = modifyEChartsCode(fs.readFileSync(ecDownloadPath, 'utf-8')); - fs.writeFileSync(testLibPath, code, 'utf-8'); - resolve(); - }); - }).on('error', (e) => { - reject(`Failed to download ${packageName}@${version} from ${url}: ${e}`); - }); + if (source === 'PR') { + const prNumber = version.replace(/^#/, ''); + if (!/^\d+$/.test(prNumber)) { + reject('Invalid PR number format. Should be #123'); + return; + } + url = `https://echarts-pr-${prNumber}.surge.sh/dist/echarts.js`; } else { - // Always do code modification. - // In case we need to do replacement on old downloads. - let code = modifyEChartsCode(fs.readFileSync(ecDownloadPath, 'utf-8')); - fs.writeFileSync(testLibPath, code, 'utf-8'); - resolve(); + const isNightly = source === 'nightly'; + const packageName = isNightly ? 'echarts-nightly' : 'echarts'; + url = useCNMirror + ? `https://registry.npmmirror.com/${packageName}/${version}/files/dist/echarts.js` + : `https://unpkg.com/${packageName}@${version}/dist/echarts.js`; } + + console.log(`Downloading ECharts from ${url}`); + https.get(url, response => { + if (response.statusCode === 404) { + reject(`PR artifact doesn't exist at ${url}. Make sure the PR build is complete.`); + return; + } + + let data = ''; + response.on('data', chunk => { + data += chunk; + }); + + response.on('end', () => { + if (!data) { + reject(`Downloaded file is empty from ${url}`); + return; + } + fs.writeFileSync(ecDownloadPath, data, 'utf-8'); + const code = modifyEChartsCode(data); + fs.writeFileSync(testLibPath, code, 'utf-8'); + resolve(); + }); + }).on('error', (e) => { + reject(`Failed to download from ${url}: ${e}`); + }); }); };