grunt-qunit-puppeteer
Version:
A test harness for running QUnit tests in headless Chromium (in Grunt)
249 lines (211 loc) • 9.89 kB
JavaScript
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 + ')');
});
});
};
;