scratch – Blame information for rev 58

Subversion Repositories:
Rev:
Rev Author Line No. Line
58 office 1 /* globals jasmineRequire, phantom */
2 // Verify arguments
3 var system = require('system');
4 var args;
5  
6 if(phantom.args) {
7 args = phantom.args;
8 } else {
9 args = system.args.slice(1);//use system args for phantom 2.0+
10 }
11  
12 if (args.length === 0) {
13 console.log("Simple JasmineBDD test runner for phantom.js");
14 console.log("Usage: phantomjs-testrunner.js url_to_runner.html");
15 console.log("Accepts http:// and file:// urls");
16 console.log("");
17 console.log("NOTE: This script depends on jasmine.HtmlReporter being used\non the page, for the DOM elements it creates.\n");
18 phantom.exit(2);
19 }
20 else {
21 var fs = require("fs"),
22 pages = [],
23 page, address, resultsKey, i, l;
24  
25  
26 var setupPageFn = function(p, k) {
27 return function() {
28 overloadPageEvaluate(p);
29 setupWriteFileFunction(p, k, fs.separator);
30 };
31 };
32  
33 for (i = 0, l = args.length; i < l; i++) {
34 address = args[i];
35 console.log("Loading " + address);
36  
37 // if provided a url without a protocol, try to use file://
38 address = address.indexOf("://") === -1 ? "file://" + address : address;
39  
40 // create a WebPage object to work with
41 page = require("webpage").create();
42 page.url = address;
43  
44 // When initialized, inject the reporting functions before the page is loaded
45 // (and thus before it will try to utilize the functions)
46 resultsKey = "__jr" + Math.ceil(Math.random() * 1000000);
47 page.onInitialized = setupPageFn(page, resultsKey);
48 page.open(address, processPage(null, page, resultsKey));
49 pages.push(page);
50  
51 page.onConsoleMessage = logAndWorkAroundDefaultLineBreaking;
52 }
53  
54 // bail when all pages have been processed
55 setInterval(function(){
56 var exit_code = 0;
57 for (i = 0, l = pages.length; i < l; i++) {
58 page = pages[i];
59 if (page.__exit_code === null) {
60 // wait until later
61 return;
62 }
63 exit_code |= page.__exit_code;
64 }
65 phantom.exit(exit_code);
66 }, 100);
67 }
68  
69 // Thanks to hoisting, these helpers are still available when needed above
70 /**
71 * Logs a message. Does not add a line-break for single characters '.' and 'F' or lines ending in ' ...'
72 *
73 * @param msg
74 */
75 function logAndWorkAroundDefaultLineBreaking(msg) {
76 var interpretAsWithoutNewline = /(^(\033\[\d+m)*[\.F](\033\[\d+m)*$)|( \.\.\.$)/;
77 if (navigator.userAgent.indexOf("Windows") < 0 && interpretAsWithoutNewline.test(msg)) {
78 try {
79 system.stdout.write(msg);
80 } catch (e) {
81 var fs = require('fs');
82 fs.write('/dev/stdout', msg, 'w');
83 }
84 } else {
85 console.log(msg);
86 }
87 }
88  
89 /**
90 * Stringifies the function, replacing any %placeholders% with mapped values.
91 *
92 * @param {function} fn The function to replace occurrences within.
93 * @param {object} replacements Key => Value object of string replacements.
94 */
95 function replaceFunctionPlaceholders(fn, replacements) {
96 if (replacements && typeof replacements === "object") {
97 fn = fn.toString();
98 for (var p in replacements) {
99 if (replacements.hasOwnProperty(p)) {
100 var match = new RegExp("%" + p + "%", "g");
101 do {
102 fn = fn.replace(match, replacements[p]);
103 } while(fn.indexOf(match) !== -1);
104 }
105 }
106 }
107 return fn;
108 }
109  
110 /**
111 * Replaces the "evaluate" method with one we can easily do substitution with.
112 *
113 * @param {phantomjs.WebPage} page The WebPage object to overload
114 */
115 function overloadPageEvaluate(page) {
116 page._evaluate = page.evaluate;
117 page.evaluate = function(fn, replacements) { return page._evaluate(replaceFunctionPlaceholders(fn, replacements)); };
118 return page;
119 }
120  
121 /** Stubs a fake writeFile function into the test runner.
122 *
123 * @param {phantomjs.WebPage} page The WebPage object to inject functions into.
124 * @param {string} key The name of the global object in which file data should
125 * be stored for later retrieval.
126 */
127 // TODO: not bothering with error checking for now (closed environment)
128 function setupWriteFileFunction(page, key, path_separator) {
129 page.evaluate(function(){
130 window["%resultsObj%"] = {};
131 window.fs_path_separator = "%fs_path_separator%";
132 window.__phantom_writeFile = function(filename, text) {
133 window["%resultsObj%"][filename] = text;
134 };
135 }, {resultsObj: key, fs_path_separator: path_separator.replace("\\", "\\\\")});
136 }
137  
138 /**
139 * Returns the loaded page's filename => output object.
140 *
141 * @param {phantomjs.WebPage} page The WebPage object to retrieve data from.
142 * @param {string} key The name of the global object to be returned. Should
143 * be the same key provided to setupWriteFileFunction.
144 */
145 function getXmlResults(page, key) {
146 return page.evaluate(function(){
147 return window["%resultsObj%"] || {};
148 }, {resultsObj: key});
149 }
150  
151 /**
152 * Processes a page.
153 *
154 * @param {string} status The status from opening the page via WebPage#open.
155 * @param {phantomjs.WebPage} page The WebPage to be processed.
156 */
157 function processPage(status, page, resultsKey) {
158 if (status === null && page) {
159 page.__exit_code = null;
160 return function(stat){
161 processPage(stat, page, resultsKey);
162 };
163 }
164 if (status !== "success") {
165 console.error("Unable to load resource: " + address);
166 page.__exit_code = 2;
167 }
168 else {
169 var isFinished = function() {
170 return page.evaluate(function(){
171 // if there's a JUnitXmlReporter, return a boolean indicating if it is finished
172 if (window.jasmineReporters && window.jasmineReporters.startTime) {
173 return !!window.jasmineReporters.endTime;
174 }
175 // otherwise, scrape the DOM for the HtmlReporter "finished in ..." output
176 var durElem = document.querySelector(".html-reporter .duration");
177 if (!durElem) {
178 durElem = document.querySelector(".jasmine_html-reporter .duration");
179 }
180 return durElem && durElem.textContent && durElem.textContent.toLowerCase().indexOf("finished in") === 0;
181 });
182 };
183 var getResultsFromHtmlRunner = function() {
184 return page.evaluate(function(){
185 var resultElem = document.querySelector(".html-reporter .alert .bar");
186 if (!resultElem) {
187 resultElem = document.querySelector(".jasmine_html-reporter .alert .bar");
188 }
189 return resultElem && resultElem.textContent &&
190 resultElem.textContent.match(/(\d+) spec.* (\d+) failure.*/) ||
191 ["Unable to determine success or failure."];
192 });
193 };
194 var timeout = 60000;
195 var loopInterval = 100;
196 var ival = setInterval(function(){
197 if (isFinished()) {
198 // get the results that need to be written to disk
199 var fs = require("fs"),
200 xml_results = getXmlResults(page, resultsKey),
201 output;
202 for (var filename in xml_results) {
203 if (xml_results.hasOwnProperty(filename) && (output = xml_results[filename]) && typeof(output) === "string") {
204 fs.write(filename, output, "w");
205 }
206 }
207  
208 // print out a success / failure message of the results
209 var results = getResultsFromHtmlRunner();
210 var failures = Number(results[2]);
211 if (failures > 0) {
212 page.__exit_code = 1;
213 clearInterval(ival);
214 }
215 else {
216 page.__exit_code = 0;
217 clearInterval(ival);
218 }
219 }
220 else {
221 timeout -= loopInterval;
222 if (timeout <= 0) {
223 console.log('Page has timed out; aborting.');
224 page.__exit_code = 2;
225 clearInterval(ival);
226 }
227 }
228 }, loopInterval);
229 }
230 }