211 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'use strict';
 | 
						|
 | 
						|
const path = require('path');
 | 
						|
const util = require('util');
 | 
						|
const { chromium } = require('playwright');
 | 
						|
const TIMEOUT_MILLISECONDS = 60000;
 | 
						|
 | 
						|
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;
 | 
						|
 | 
						|
    let viewport = page.viewport();
 | 
						|
    width && (viewport.width = width);
 | 
						|
    height && (viewport.height = height);
 | 
						|
 | 
						|
    return page.setViewport(viewport).then(() => page);
 | 
						|
}
 | 
						|
 | 
						|
function handleConsole(msg) {
 | 
						|
    const args = msg.args() || [];
 | 
						|
 | 
						|
    Promise.all(args.map(a => a.jsonValue().catch(error => {
 | 
						|
        console.warn('Failed to retrieve JSON value from argument:', error);
 | 
						|
        return '';
 | 
						|
    })))
 | 
						|
        .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 = TIMEOUT_MILLISECONDS);
 | 
						|
        /^\d+$/.test(polling) && (polling = parseInt(polling));
 | 
						|
 | 
						|
        const url = prepareUrl(file);
 | 
						|
 | 
						|
        const options = {
 | 
						|
            ignoreHTTPSErrors: true,
 | 
						|
            headless: !visible,
 | 
						|
            executablePath,
 | 
						|
            args
 | 
						|
        };
 | 
						|
 | 
						|
        const result = chromium.launch(options)
 | 
						|
            .then(browser => browser.newContext()
 | 
						|
                .then(context => context.newPage()
 | 
						|
                    .then(page => {
 | 
						|
                        if (width || height) {
 | 
						|
                            return page.setViewportSize({ width: width || 800, height: height || 600 }).then(() => page);
 | 
						|
                        }
 | 
						|
                        return page;
 | 
						|
                    })
 | 
						|
                    .then(page => {
 | 
						|
                        page.on('console', handleConsole);
 | 
						|
                        page.on('dialog', dialog => dialog.dismiss());
 | 
						|
                        page.on('pageerror', err => console.error(err));
 | 
						|
 | 
						|
                        return page.addInitScript(initMocha, reporter)
 | 
						|
                            .then(() => page.goto(url))
 | 
						|
                            .then(() => page.waitForFunction(() => window.__mochaResult__, { timeout, polling }))
 | 
						|
                            .then(() => page.evaluate(() => window.__mochaResult__))
 | 
						|
                            .then(result => {
 | 
						|
                                if (!result) {
 | 
						|
                                    throw new Error('Mocha results not found after waiting. The tests may not have run correctly.');
 | 
						|
                                }
 | 
						|
                                // Close browser before resolving result
 | 
						|
                                return browser.close().then(() => result);
 | 
						|
                            });
 | 
						|
                    })
 | 
						|
                )
 | 
						|
            );
 | 
						|
 | 
						|
        resolve(result);
 | 
						|
    });
 | 
						|
};
 |