UNPKG

mocha-headless-chrome

Version:

Run client-side mocha tests in the command line through headless Chrome

231 lines (198 loc) 5.6 kB
'use strict'; const path = require('node:path'); const util = require('node:util'); const puppeteer = require('puppeteer'); function initMocha(reporter) { console.log = ((console) => { const log = console.log.bind(console); return (...args) => (args.length ? log(...args) : log('')); })(console); function shimMochaInstance(m) { const originalReporter = m.reporter.bind(m); let reporterIsChanged = false; m.reporter = (...args) => { reporterIsChanged = true; originalReporter(...args); }; const run = m.run.bind(m); m.run = () => { const all = [], pending = [], failures = [], passes = []; function error(err) { if (!err) return {}; let res = {}; Object.getOwnPropertyNames(err).forEach((key) => (res[key] = err[key])); return res; } function clean(test) { return { title: test.title, fullTitle: test.fullTitle(), duration: test.duration, err: error(test.err), }; } function result(stats) { return { result: { stats: { tests: all.length, passes: passes.length, pending: pending.length, failures: failures.length, start: stats.start.toISOString(), end: stats.end.toISOString(), duration: stats.duration, }, tests: all.map(clean), pending: pending.map(clean), failures: failures.map(clean), passes: passes.map(clean), }, coverage: window.__coverage__, }; } function setResult() { !window.__mochaResult__ && (window.__mochaResult__ = result(this.stats)); } !reporterIsChanged && m.setup({ reporter: Mocha.reporters[reporter] || Mocha.reporters.spec, }); const runner = run(() => setTimeout(() => setResult.call(runner), 0)) .on('pass', (test) => { passes.push(test); all.push(test); }) .on('fail', (test) => { failures.push(test); all.push(test); }) .on('pending', (test) => { pending.push(test); all.push(test); }) .on('end', setResult); return runner; }; } function shimMochaProcess(M) { // Mocha needs a process.stdout.write in order to change the cursor position. !M.process && (M.process = {}); !M.process.stdout && (M.process.stdout = {}); M.process.stdout.write = (data) => console.log('stdout:', data); M.reporters.Base.useColors = true; M.reporters.none = function None(runner) { M.reporters.Base.call(this, runner); }; } Object.defineProperty(window, 'mocha', { get: function () { return undefined; }, set: function (m) { shimMochaInstance(m); delete window.mocha; window.mocha = m; }, configurable: true, }); Object.defineProperty(window, 'Mocha', { get: function () { return undefined; }, set: function (m) { shimMochaProcess(m); delete window.Mocha; window.Mocha = m; }, configurable: true, }); } function configureViewport(width, height, page) { if (!width && !height) return page; const viewport = page.viewport(); return page .setViewport({ width: width || viewport.width, height: height || viewport.height, }) .then(() => page); } function handleConsole(msg) { const args = msg.args(); Promise.all(args.map((a) => a.jsonValue().catch(() => ''))).then((args) => { // process stdout stub let isStdout = args[0] === 'stdout:'; isStdout && (args = args.slice(1)); let msg = util.format(...args); !isStdout && msg && (msg += '\n'); process.stdout.write(msg); }); } function prepareUrl(filePath) { if (/^[a-zA-Z]+:\/\//.test(filePath)) { // path is URL return filePath; } // local path let resolvedPath = path.resolve(filePath); return `file://${resolvedPath}`; } exports.runner = function ({ file, reporter, timeout, width, height, args, executablePath, visible, polling, }) { return new Promise((resolve) => { // validate options if (!file) { throw new Error('Test page path is required.'); } args = [].concat(args || []).map((arg) => '--' + arg); !timeout && (timeout = 60000); /^\d+$/.test(polling) && (polling = parseInt(polling)); const url = prepareUrl(file); const options = { ignoreHTTPSErrors: true, headless: !visible, executablePath, args, }; const result = puppeteer.launch(options).then((browser) => browser .pages() .then((pages) => pages.pop()) .then(configureViewport.bind(this, width, height)) .then((page) => { page.on('console', handleConsole); page.on('dialog', (dialog) => dialog.dismiss()); page.on('pageerror', (err) => console.error(err)); return page .evaluateOnNewDocument(initMocha, reporter) .then(() => page.goto(url)) .then(() => page.waitForFunction(() => window.__mochaResult__, { timeout, polling, }) ) .then(() => page.evaluate(() => window.__mochaResult__)) .then((obj) => { browser.close(); return obj; }); }) ); resolve(result); }); };