Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
This is how I use phantomjs to inject my test suite and dependencies into a production HTML page. As with most websites there is a login to get past first, this script handles the asynchronous nature of javascript and page loading before executing the tests. This avoids having to manage any testing related logic in the website code. Adapted from…
/*
* QtWebKit-powered headless test runner using PhantomJS
*
* modified from https://github.com/jquery/qunit/tree/master/addons/phantomjs
*
* PhantomJS binaries: http://phantomjs.org/download.html
* Requires PhantomJS 1.6+ (1.7+ recommended)
*
* Run with:
* phantomjs runner.js [url-of-your-qunit-testsuite]
*
* e.g.
* phantomjs runner.js http://localhost/qunit/test/index.html
*/
/*global phantom:false, require:false, console:false, window:false, QUnit:false */
(function () {
'use strict';
var url, page, timeout, loadInProgress = false,
args = require('system').args;
// arg[0]: scriptName, args[1...]: arguments
if (args.length < 2 || args.length > 3) {
console.error('Usage:\n phantomjs runner.js [url-of-your-qunit-testsuite] [timeout-in-seconds]');
phantom.exit(1);
}
url = args[1];
page = require('webpage').create();
if (args[2] !== undefined) {
timeout = parseInt(args[2], 10);
}
// Route `console.log()` calls from within the Page context to the main Phantom context (i.e. current `this`)
page.onConsoleMessage = function (msg) {
console.log(msg);
};
page.onLoadStarted = function() {
loadInProgress = true;
// console.log("Page load started");
};
page.onLoadFinished = function() {
loadInProgress = false;
// console.log("Page load finished");
// now we have a loaded DOM we can inject our tests
page.evaluate(addPageScopeFunctionsToPage);
page.evaluate(addExecutePageScopeFunctionsToPage);
page.evaluate(setEventListener_extractQunitResults);
};
// Introduced: PhantomJS 1.3
// This callback is invoked after the web page object is created but before a URL is loaded. The callback may be used to change global objects.
page.onInitialized = function () {
// console.log("page.onInitialized triggered");
addQUnitToPage();
addQUnitTestsToPage();
};
// Stability: EXPERIMENTAL
// Introduced: PhantomJS 1.6
// This callback is invoked when there is a JavaScript window.callPhantom call made on the web page. The only argument passed to the callback is a data object.
page.onCallback = function (message) {
var result,
failed;
if (message) {
if (message.name === 'QUnit.done') {
result = message.data;
failed = !result || result.failed;
phantom.exit(failed ? 1 : 0);
}
}
};
page.open(url, function (status) {
if (status !== 'success') {
console.error('Unable to access network: ' + status);
phantom.exit(1);
} else {
// Cannot do this verification with the 'DOMContentLoaded' handler because it
// will be too late to attach it if a page does not have any script tags.
var qunitMissing = page.evaluate(function () {
return (typeof QUnit === 'undefined' || !QUnit);
});
if (qunitMissing) {
console.error('The `QUnit` object is not present on this page.');
phantom.exit(1);
}
// Set a timeout on the test running, otherwise tests with async problems will hang forever
if (typeof timeout === 'number') {
setTimeout(function () {
console.error('The specified timeout of ' + timeout + ' seconds has expired. Aborting...');
phantom.exit(1);
}, timeout * 1000);
}
// Do nothing... the callback mechanism will handle everything else!
}
});
function addQUnitToPage(){
console.log( page.injectJs("lib/QUnit/qunit-1.12.0.js") ? '' : "injecting QUnit fail! Check the $PWD?!" );
}
function addQUnitTestsToPage(){
console.log( page.injectJs("bulkloaderQunitTests.js") ? '' : "injecting Tests failed! Check the $PWD?!" );
}
function setEventListener_extractQunitResults() {
window.document.addEventListener('DOMContentLoaded', executePageScopeFunctions(), false);
}
function addExecutePageScopeFunctionsToPage(){
window.executePageScopeFunctions = function(){
// console.log('Executing: executePageScopeFunctions, document.readyState: '+document.readyState);
// are we on the login page?
if( document.getElementById("loginForm")){
pageLogin();
}else{
// guess we've logged in, so run the tests
executeQunitTests();
extractQunitResults();
}
};
}
function addPageScopeFunctionsToPage(){
// console.log('Executing: addPageScopeFunctionsToPage');
// login ( with help from http://stackoverflow.com/questions/9246438/how-to-submit-a-form-using-phantomjs )
window.pageLogin = function(){
// console.log('Logging in...');
var loginForm = document.getElementById("loginForm") ;
var method = loginForm.getAttribute('method');
if ( "post" == method){
// console.log('post method found: '+method+' in '+loginForm.innerHTML);
loginForm.elements["username"].value = 'fargle';
loginForm.elements["password"].value = 'xx';
// console.log('username: ' + loginForm.elements["username"].value);
// console.log('password: ' + loginForm.elements["password"].value);
loginForm.submit();
// now return to wait for page to load again
return;
}else{
console.error('post method NOT found: '+method);
dumpPageContents();
}
};
window.dumpPageContents = function () {
console.log(document.querySelectorAll('html')[0].outerHTML);
};
window.extractQunitResults = function() {
var currentTestAssertions = [];
QUnit.log(function (details) {
var response;
// Ignore passing assertions
if (details.result) {
return;
}
response = details.message || '';
if (typeof details.expected !== 'undefined') {
if (response) {
response += ', ';
}
response += 'expected: ' + details.expected + ', but was: ' + details.actual;
}
if (details.source) {
response += "\n" + details.source;
}
currentTestAssertions.push('Failed assertion: ' + response);
});
QUnit.testDone(function (result) {
var i,
len,
name = result.module + ': ' + result.name;
if (result.failed) {
console.log('Test failed: ' + name);
for (i = 0, len = currentTestAssertions.length; i < len; i++) {
console.log(' ' + currentTestAssertions[i]);
}
}
currentTestAssertions.length = 0;
});
QUnit.done(function (result) {
console.log('Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.');
if (typeof window.callPhantom === 'function') {
window.callPhantom({
'name': 'QUnit.done',
'data': result
});
}
});
};
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment