123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- try {
- require('puppeteer')
- } catch (e) {
- console.error(
- "\nFATAL ERROR:" +
- "\n Package 'puppeteer' is not available." +
- "\n Run 'npm install --no-save puppeteer' before running this script" +
- "\n * You may use PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 environment variable to avoid automatic Chromium downloading" +
- "\n (specify own Chromium/Chrome version through PUPPETEER_EXECUTABLE_PATH=`which google-chrome` environment variable)" +
- "\n");
- process.exit(1);
- }
- const puppeteer = require('puppeteer')
- const colors = require("ansi-colors")
- const path = require("path");
- const fs = require("fs");
- const http = require("http");
- run_main(require('minimist')(process.argv.slice(2)));
- async function run_main(o = {}) {
- try {
- await main(o);
- console.magenta("FATAL: Unexpected exit!");
- process.exit(1);
- } catch (e) {
- console.error(colors.magenta("FATAL: Unexpected exception!"));
- console.error(e);
- process.exit(1);
- }
- }
- async function main(o = {}) {
- o = Object.assign({}, {
- buildFolder: __dirname,
- port: 8080,
- debug: false,
- noHeadless: false,
- serverPrefix: `http://localhost`,
- noExit: false,
- screenshot: undefined,
- help: false,
- noTryCatch: false,
- maxBlockDuration: 30000
- }, o)
- if (typeof o.screenshot == 'string' && o.screenshot == 'false') {
- console.log(colors.red('ERROR: misused screenshot option, use --no-screenshot instead'));
- }
- if (o.noExit) {
- o.maxBlockDuration = 999999999
- }
- o.debug && console.log('Current Options', o);
- if (o.help) {
- printHelpAndExit();
- }
- const serverAddress = `${o.serverPrefix}:${o.port}`
- const url = `${serverAddress}/tests.html${o.noTryCatch ? '?notrycatch=1' : ''}`;
- if (!fs.existsSync(o.buildFolder)) {
- console.error(`Expected folder "${o.buildFolder}" to exists. Aborting`);
- }
- o.debug && debug('Server Listening at ' + url);
- const server = await staticServer(o.buildFolder, o.port, m => debug, m => error);
- o.debug && debug(`Browser launching ${!o.noHeadless ? 'headless' : 'not headless'}`);
- const browser = await puppeteer.launch({ headless: !o.noHeadless });
- const page = await browser.newPage();
- page.on('console', e => {
- locationMsg = formatMessage(`${e.location().url}:${e.location().lineNumber}:${e.location().columnNumber}`);
- if (e.type() === 'error') {
- console.log(colors.red(formatMessage('' + e.text(), `-- ERROR:${locationMsg}: `, )));
- }
- else if (o.debug) {
- o.debug && console.log(colors.grey(formatMessage('' + e.text(), `-- ${locationMsg}: `)));
- }
- });
- o.debug && debug(`Opening page address ${url}`);
- await page.goto(url);
- await page.waitForFunction(() => (document.querySelector(`#qunit-testresult`) && document.querySelector(`#qunit-testresult`).textContent || '').trim().toLowerCase().startsWith('tests completed'));
- const text = await getText(`#qunit-testresult`);
- if (!text) {
- return await fail(`An error occurred extracting test results. Check the build folder ${o.buildFolder} is correct and has build with tests enabled.`);
- }
- o.debug && debug(colors.blackBright("* UserAgent: " + await getText('#qunit-userAgent')));
- const testFailed = !text.includes(' 0 failed');
- if (testFailed && !o.debug) {
- process.stdout.write(colors.grey("* Use '--debug' parameter to see details of failed tests.\n"));
- }
- if (o.screenshot || (o.screenshot === undefined && testFailed)) {
- await page.screenshot({ path: 'screenshot.png', fullPage: 'true' });
- process.stdout.write(colors.grey(`* Screenshot taken: ${o.buildFolder}/screenshot.png\n`));
- }
- if (testFailed) {
- const report = await failReport();
- process.stdout.write(`
- ${colors.red.bold.underline('Failed tests ! :(')}
- ${colors.redBright(colors.symbols.cross + ' ' + report.join(`\n${colors.symbols.cross} `))}
- ${colors.redBright(`=== Summary ===\n${text}`)}
- `);
- }
- else {
- process.stdout.write(colors.green(`
- ${colors.symbols.check} No Errors :)
- === Summary ===\n${text}
- `));
- }
- if (o.noExit) {
- while (true) {
- await new Promise(r => setTimeout(r, 5000));
- }
- }
- await server && server.close();
- await browser.close();
- process.exit(testFailed ? 1 : 0);
- async function getText(s) {
- return await page.evaluate((s) => (document.querySelector(s) && document.querySelector(s).innerText) || ''.trim(), s);
- }
- async function failReport() {
- const failures = await page.evaluate(() => Array.from(document.querySelectorAll('#qunit-tests .fail')).filter(e => e.querySelector('.module-name')).map(e => ({
- moduleName: e.querySelector('.module-name') && e.querySelector('.module-name').textContent,
- testName: e.querySelector('.test-name') && e.querySelector('.test-name').textContent,
- expected: e.querySelector('.test-expected pre') && e.querySelector('.test-expected pre').textContent,
- actual: e.querySelector('.test-actual pre') && e.querySelector('.test-actual pre').textContent,
- code: e.querySelector('.test-source') && e.querySelector('.test-source').textContent.replace("Source: at ", ""),
- })));
- return failures.map(f => `${f.moduleName}: ${f.testName} (${formatMessage(f.code)})`);
- }
- async function fail(s) {
- await failReport();
- process.stdout.write(colors.red(s) + '\n');
- if (o.screenshot || o.screenshot === undefined) {
- await page.screenshot({ path: 'screenshot.png', fullPage: 'true' });
- process.stdout.write(colors.grey(`* Screenshot taken: ${o.buildFolder}/screenshot.png\n`));
- }
- process.exit(1);
- }
- async function debug(s) {
- process.stdout.write(s + '\n');
- }
- async function error(s) {
- process.stdout.write(s + '\n');
- }
- function formatMessage(message, prefix) {
- prefix = prefix || '';
- return prefix + ('' + message).split('\n').map(l => l.replace(serverAddress, o.buildFolder)).join('\n' + prefix);
- }
- }
- function printHelpAndExit() {
- console.log(`
- Usage:
- # First, remember to build opencv.js with tests enabled:
- ${colors.blueBright(`python ./platforms/js/build_js.py build_js --build_test`)}
- # Install the tool locally (needed only once) and run it
- ${colors.blueBright(`cd build_js/bin`)}
- ${colors.blueBright(`npm install`)}
- ${colors.blueBright(`node run_puppeteer`)}
- By default will run a headless browser silently printing a small report in the terminal.
- But it could used to debug the tests in the browser, take screenshots, global tool or
- targeting external servers exposing the tests.
- TIP: you could install the tool globally (npm install --global build_js/bin) to execute it from any local folder.
- # Options
- * port?: number. Default 8080
- * buildFolder?: string. Default __dirname (this folder)
- * debug?: boolean. Default false
- * noHeadless?: boolean. Default false
- * serverPrefix?: string . Default http://localhost
- * help?: boolean
- * screenshot?: boolean . Make screenshot on failure by default. Use --no-screenshot to disable screenshots completely.
- * noExit?: boolean default false. If true it will keep running the server - together with noHeadless you can debug in the browser.
- * noTryCatch?: boolean will disable Qunit tryCatch - so exceptions are dump to stdout rather than in the browser.
- * maxBlockDuration: QUnit timeout. If noExit is given then is infinity.
- `);
- process.exit(0);
- }
- async function staticServer(basePath, port, onFound, onNotFound) {
- return new Promise(async (resolve) => {
- const server = http.createServer((req, res) => {
- var url = resolveUrl(req.url);
- onFound && onFound(url);
- var stream = fs.createReadStream(path.join(basePath, url || ''));
- stream.on('error', function () {
- onNotFound && onNotFound(url);
- res.writeHead(404);
- res.end();
- });
- stream.pipe(res);
- }).listen(port);
- server.on('listening', () => {
- resolve(server);
- });
- });
- function resolveUrl(url = '') {
- var i = url.indexOf('?');
- if (i != -1) {
- url = url.substr(0, i);
- }
- i = url.indexOf('#');
- if (i != -1) {
- url = url.substr(0, i);
- }
- return url;
- }
- }
|