UNPKG

grunt-qunit-puppeteer

Version:

A test harness for running QUnit tests in headless Chromium (in Grunt)

249 lines (211 loc) 9.89 kB
#! /usr/bin/env node 'use strict'; function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } var puppeteer = require('puppeteer'); var devices = require('puppeteer/DeviceDescriptors'); const regeneratorRuntime = require("regenerator-runtime"); module.exports = function (grunt) { grunt.registerMultiTask('qunit_puppeteer', 'Run QUnit-Tests with Headless Chrome', function () { var _this = this; var done = this.async(); var oOptions = this.options({ resources: {} }); if (!oOptions.qunitPage) { grunt.fail.warn('qunitPage missing.'); done(); return; } if (process.env.PUPPETEER_CHROMIUM_PATH) { oOptions.chromeExecutable = process.env.PUPPETEER_CHROMIUM_PATH; } oOptions.traceSettings = oOptions.traceSettings || {}; oOptions.traceSettings.outputConsole = typeof oOptions.traceSettings.outputConsole !== "undefined" ? oOptions.traceSettings.outputConsole : false; oOptions.traceSettings.outputAllAssertions = typeof oOptions.traceSettings.outputAllAssertions !== "undefined" ? oOptions.traceSettings.outputAllAssertions : false; if (typeof oOptions.headless === "undefined") { oOptions.headless = true; } oOptions.viewport = oOptions.viewport || {}; oOptions.viewport.width = oOptions.viewport.width || 1920; oOptions.viewport.height = oOptions.viewport.height || 1920; var oEmulate = null; if (oOptions.mobile && oOptions.mobile.emulate === true) { var sDevice = ""; if (oOptions.mobile.tablet === true) { sDevice = "iPad Pro"; } else if (oOptions.mobile.landscape === true && oOptions.mobile.tablet === false) { sDevice = "iPhone 6 Plus landscape"; } else if (oOptions.mobile.landscape === false && oOptions.mobile.tablet === false) { sDevice = "iPhone 6 Plus"; } if (!devices[sDevice]) { grunt.fail.warn('specified device emulated is not available.'); done(); return; } oEmulate = devices[sDevice]; } var targetURL = oOptions.qunitPage; var timeout = parseInt(300000, 10); _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() { var args, fnPromiseResolve, fnPromiseReject, oTestSuitePromise, browser, page; return regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: grunt.log.ok("Async Processing of test started"); args = ["--disable-web-security", "--ignore-certificate-errors"]; //hack to resolve everything at the end oTestSuitePromise = new Promise(function (resolve, reject) { fnPromiseResolve = resolve; fnPromiseReject = reject; }); _context.next = 5; return puppeteer.launch({ args: args, headless: oOptions.headless, ignoreHTTPSErrors: true, userDataDir: oOptions.userDataDir, executablePath: oOptions.chromeExecutable }); case 5: browser = _context.sent; _context.next = 8; return browser.newPage(); case 8: page = _context.sent; page.setViewport({ width: oOptions.viewport.width, height: oOptions.viewport.height }); if (!oEmulate) { _context.next = 13; break; } _context.next = 13; return page.emulate(oEmulate); case 13: if (!(oOptions.traceSettings.outputConsole === true)) { _context.next = 16; break; } _context.next = 16; return page.on('console', function () { for (var _len = arguments.length, params = Array(_len), _key = 0; _key < _len; _key++) { params[_key] = arguments[_key]; } for (var i = 0; i < params.length; ++i) { console.log('' + params[i]); } }); case 16: _context.next = 18; return page.exposeFunction('harness_moduleDone', function (context) { if (context.failed) { grunt.log.error("Module Failed: " + context.name + " ( " + context.failed + " / " + context.passed + " ) in " + context.runtime + "ms"); } else { grunt.log.ok("Module Succeeded: " + context.name + " (" + context.passed + " Tests) in " + context.runtime + "ms"); } }); case 18: _context.next = 20; return page.exposeFunction('harness_testDone', function (context) { if (context.failed) { grunt.log.error("Test Failed: " + context.name + " ( " + context.failed + " / " + context.passed + " ) in " + context.runtime + "ms"); } else { grunt.log.ok("Test Succeeded: " + context.name + " (" + context.passed + " Tests) in " + context.runtime + "ms"); } }); case 20: _context.next = 22; return page.exposeFunction('harness_moduleStart', function (context) { grunt.log.ok("Start Module:" + context.name); }); case 22: _context.next = 24; return page.exposeFunction('harness_testStart', function (context) { grunt.log.ok("Start Test:" + context.name); }); case 24: _context.next = 26; return page.exposeFunction('harness_log', function (context) { if (oOptions.traceSettings.outputAllAssertions === false && context.result) { return; } if (!context.result) { grunt.log.error("Assertion Failed: " + (context.message ? context.message : "unknown") + "; Values:" + context.expected + "/" + context.actual); } else { grunt.log.ok("Assertion Succeeded: " + (context.message ? context.message : "unknown") + "; Values:" + context.expected + "/" + context.actual); } }); case 26: _context.next = 28; return page.exposeFunction('harness_done', function (context) { var stats = ["Time: " + context.runtime + "ms", "Total: " + context.total, "Passed: " + context.passed, "Failed: " + context.failed]; grunt.log.ok(stats.join(", ")); //hacky coding - waiting for 500ms, will avoid unhandled open promises //we are in a completly different scope here (of the page from my understanding) //the promise might be resolved to early, in case we are not waiting.. setTimeout(function () { if (context.failed > 0) { fnPromiseReject({ context: context }); } fnPromiseResolve(); }, 500); }); case 28: _context.next = 30; return page.goto(targetURL, { timeout: 50000, waitUntil: "load" }); case 30: _context.next = 32; return page.evaluate(function () { QUnit.moduleStart(function (context) { window.harness_moduleStart(context); }); QUnit.moduleDone(function (context) { window.harness_moduleDone(context); }); QUnit.testStart(function (context) { window.harness_testStart(context); }); QUnit.testDone(function (context) { window.harness_testDone(context); }); QUnit.log(function (context) { window.harness_log(context); }); QUnit.done(function (context) { window.harness_done(context); }); }); case 32: _context.prev = 32; _context.next = 35; return oTestSuitePromise; case 35: _context.next = 42; break; case 37: _context.prev = 37; _context.t0 = _context['catch'](32); _context.next = 41; return browser.close(); case 41: //close always to avoid memory leaks grunt.fail.warn('OPA/QUnit identified errors (' + _context.t0.context.failed + ')'); case 42: _context.next = 44; return browser.close(); case 44: done(); case 45: case 'end': return _context.stop(); } } }, _callee, _this, [[32, 37]]); }))().catch(function (error) { console.error(error); grunt.fail.warn('QUnit found exception (' + error.message + ')'); }); }); };