mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-19 03:48:18 -07:00
231 lines
8.1 KiB
JavaScript
231 lines
8.1 KiB
JavaScript
/* globals jasmineRequire, phantom */
|
|
// Verify arguments
|
|
var system = require('system');
|
|
var args;
|
|
|
|
if(phantom.args) {
|
|
args = phantom.args;
|
|
} else {
|
|
args = system.args.slice(1);//use system args for phantom 2.0+
|
|
}
|
|
|
|
if (args.length === 0) {
|
|
console.log("Simple JasmineBDD test runner for phantom.js");
|
|
console.log("Usage: phantomjs-testrunner.js url_to_runner.html");
|
|
console.log("Accepts http:// and file:// urls");
|
|
console.log("");
|
|
console.log("NOTE: This script depends on jasmine.HtmlReporter being used\non the page, for the DOM elements it creates.\n");
|
|
phantom.exit(2);
|
|
}
|
|
else {
|
|
var fs = require("fs"),
|
|
pages = [],
|
|
page, address, resultsKey, i, l;
|
|
|
|
|
|
var setupPageFn = function(p, k) {
|
|
return function() {
|
|
overloadPageEvaluate(p);
|
|
setupWriteFileFunction(p, k, fs.separator);
|
|
};
|
|
};
|
|
|
|
for (i = 0, l = args.length; i < l; i++) {
|
|
address = args[i];
|
|
console.log("Loading " + address);
|
|
|
|
// if provided a url without a protocol, try to use file://
|
|
address = address.indexOf("://") === -1 ? "file://" + address : address;
|
|
|
|
// create a WebPage object to work with
|
|
page = require("webpage").create();
|
|
page.url = address;
|
|
|
|
// When initialized, inject the reporting functions before the page is loaded
|
|
// (and thus before it will try to utilize the functions)
|
|
resultsKey = "__jr" + Math.ceil(Math.random() * 1000000);
|
|
page.onInitialized = setupPageFn(page, resultsKey);
|
|
page.open(address, processPage(null, page, resultsKey));
|
|
pages.push(page);
|
|
|
|
page.onConsoleMessage = logAndWorkAroundDefaultLineBreaking;
|
|
}
|
|
|
|
// bail when all pages have been processed
|
|
setInterval(function(){
|
|
var exit_code = 0;
|
|
for (i = 0, l = pages.length; i < l; i++) {
|
|
page = pages[i];
|
|
if (page.__exit_code === null) {
|
|
// wait until later
|
|
return;
|
|
}
|
|
exit_code |= page.__exit_code;
|
|
}
|
|
phantom.exit(exit_code);
|
|
}, 100);
|
|
}
|
|
|
|
// Thanks to hoisting, these helpers are still available when needed above
|
|
/**
|
|
* Logs a message. Does not add a line-break for single characters '.' and 'F' or lines ending in ' ...'
|
|
*
|
|
* @param msg
|
|
*/
|
|
function logAndWorkAroundDefaultLineBreaking(msg) {
|
|
var interpretAsWithoutNewline = /(^(\033\[\d+m)*[\.F](\033\[\d+m)*$)|( \.\.\.$)/;
|
|
if (navigator.userAgent.indexOf("Windows") < 0 && interpretAsWithoutNewline.test(msg)) {
|
|
try {
|
|
system.stdout.write(msg);
|
|
} catch (e) {
|
|
var fs = require('fs');
|
|
fs.write('/dev/stdout', msg, 'w');
|
|
}
|
|
} else {
|
|
console.log(msg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stringifies the function, replacing any %placeholders% with mapped values.
|
|
*
|
|
* @param {function} fn The function to replace occurrences within.
|
|
* @param {object} replacements Key => Value object of string replacements.
|
|
*/
|
|
function replaceFunctionPlaceholders(fn, replacements) {
|
|
if (replacements && typeof replacements === "object") {
|
|
fn = fn.toString();
|
|
for (var p in replacements) {
|
|
if (replacements.hasOwnProperty(p)) {
|
|
var match = new RegExp("%" + p + "%", "g");
|
|
do {
|
|
fn = fn.replace(match, replacements[p]);
|
|
} while(fn.indexOf(match) !== -1);
|
|
}
|
|
}
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
/**
|
|
* Replaces the "evaluate" method with one we can easily do substitution with.
|
|
*
|
|
* @param {phantomjs.WebPage} page The WebPage object to overload
|
|
*/
|
|
function overloadPageEvaluate(page) {
|
|
page._evaluate = page.evaluate;
|
|
page.evaluate = function(fn, replacements) { return page._evaluate(replaceFunctionPlaceholders(fn, replacements)); };
|
|
return page;
|
|
}
|
|
|
|
/** Stubs a fake writeFile function into the test runner.
|
|
*
|
|
* @param {phantomjs.WebPage} page The WebPage object to inject functions into.
|
|
* @param {string} key The name of the global object in which file data should
|
|
* be stored for later retrieval.
|
|
*/
|
|
// TODO: not bothering with error checking for now (closed environment)
|
|
function setupWriteFileFunction(page, key, path_separator) {
|
|
page.evaluate(function(){
|
|
window["%resultsObj%"] = {};
|
|
window.fs_path_separator = "%fs_path_separator%";
|
|
window.__phantom_writeFile = function(filename, text) {
|
|
window["%resultsObj%"][filename] = text;
|
|
};
|
|
}, {resultsObj: key, fs_path_separator: path_separator.replace("\\", "\\\\")});
|
|
}
|
|
|
|
/**
|
|
* Returns the loaded page's filename => output object.
|
|
*
|
|
* @param {phantomjs.WebPage} page The WebPage object to retrieve data from.
|
|
* @param {string} key The name of the global object to be returned. Should
|
|
* be the same key provided to setupWriteFileFunction.
|
|
*/
|
|
function getXmlResults(page, key) {
|
|
return page.evaluate(function(){
|
|
return window["%resultsObj%"] || {};
|
|
}, {resultsObj: key});
|
|
}
|
|
|
|
/**
|
|
* Processes a page.
|
|
*
|
|
* @param {string} status The status from opening the page via WebPage#open.
|
|
* @param {phantomjs.WebPage} page The WebPage to be processed.
|
|
*/
|
|
function processPage(status, page, resultsKey) {
|
|
if (status === null && page) {
|
|
page.__exit_code = null;
|
|
return function(stat){
|
|
processPage(stat, page, resultsKey);
|
|
};
|
|
}
|
|
if (status !== "success") {
|
|
console.error("Unable to load resource: " + address);
|
|
page.__exit_code = 2;
|
|
}
|
|
else {
|
|
var isFinished = function() {
|
|
return page.evaluate(function(){
|
|
// if there's a JUnitXmlReporter, return a boolean indicating if it is finished
|
|
if (window.jasmineReporters && window.jasmineReporters.startTime) {
|
|
return !!window.jasmineReporters.endTime;
|
|
}
|
|
// otherwise, scrape the DOM for the HtmlReporter "finished in ..." output
|
|
var durElem = document.querySelector(".html-reporter .duration");
|
|
if (!durElem) {
|
|
durElem = document.querySelector(".jasmine_html-reporter .duration");
|
|
}
|
|
return durElem && durElem.textContent && durElem.textContent.toLowerCase().indexOf("finished in") === 0;
|
|
});
|
|
};
|
|
var getResultsFromHtmlRunner = function() {
|
|
return page.evaluate(function(){
|
|
var resultElem = document.querySelector(".html-reporter .alert .bar");
|
|
if (!resultElem) {
|
|
resultElem = document.querySelector(".jasmine_html-reporter .alert .bar");
|
|
}
|
|
return resultElem && resultElem.textContent &&
|
|
resultElem.textContent.match(/(\d+) spec.* (\d+) failure.*/) ||
|
|
["Unable to determine success or failure."];
|
|
});
|
|
};
|
|
var timeout = 60000;
|
|
var loopInterval = 100;
|
|
var ival = setInterval(function(){
|
|
if (isFinished()) {
|
|
// get the results that need to be written to disk
|
|
var fs = require("fs"),
|
|
xml_results = getXmlResults(page, resultsKey),
|
|
output;
|
|
for (var filename in xml_results) {
|
|
if (xml_results.hasOwnProperty(filename) && (output = xml_results[filename]) && typeof(output) === "string") {
|
|
fs.write(filename, output, "w");
|
|
}
|
|
}
|
|
|
|
// print out a success / failure message of the results
|
|
var results = getResultsFromHtmlRunner();
|
|
var failures = Number(results[2]);
|
|
if (failures > 0) {
|
|
page.__exit_code = 1;
|
|
clearInterval(ival);
|
|
}
|
|
else {
|
|
page.__exit_code = 0;
|
|
clearInterval(ival);
|
|
}
|
|
}
|
|
else {
|
|
timeout -= loopInterval;
|
|
if (timeout <= 0) {
|
|
console.log('Page has timed out; aborting.');
|
|
page.__exit_code = 2;
|
|
clearInterval(ival);
|
|
}
|
|
}
|
|
}, loopInterval);
|
|
}
|
|
}
|