1 |
|
2 |
|
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);
|
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 | setupWriteFileFunction(p, k, fs.separator);
|
29 | };
|
30 | };
|
31 |
|
32 | for (i = 0, l = args.length; i < l; i++) {
|
33 | address = args[i];
|
34 | console.log("Loading " + address);
|
35 |
|
36 |
|
37 | address = address.indexOf("://") === -1 ? "file://" + address : address;
|
38 |
|
39 |
|
40 | page = require("webpage").create();
|
41 | page.url = address;
|
42 |
|
43 |
|
44 |
|
45 | resultsKey = "__jr" + Math.ceil(Math.random() * 1000000);
|
46 | page.onInitialized = setupPageFn(page, resultsKey);
|
47 | page.open(address, processPage(null, page, resultsKey));
|
48 | pages.push(page);
|
49 |
|
50 | page.onConsoleMessage = logAndWorkAroundDefaultLineBreaking;
|
51 | }
|
52 |
|
53 |
|
54 | setInterval(function(){
|
55 | var exit_code = 0;
|
56 | for (i = 0, l = pages.length; i < l; i++) {
|
57 | page = pages[i];
|
58 | if (page.__exit_code === null) {
|
59 |
|
60 | return;
|
61 | }
|
62 | exit_code |= page.__exit_code;
|
63 | }
|
64 | phantom.exit(exit_code);
|
65 | }, 100);
|
66 | }
|
67 |
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 | function logAndWorkAroundDefaultLineBreaking(msg) {
|
75 | var interpretAsWithoutNewline = /(^(\u001b\[\d+m)*[\.F](\u001b\[\d+m)*$)|( \.\.\.$)/;
|
76 | if (navigator.userAgent.indexOf("Windows") < 0 && interpretAsWithoutNewline.test(msg)) {
|
77 | try {
|
78 | system.stdout.write(msg);
|
79 | } catch (e) {
|
80 | var fs = require('fs');
|
81 | fs.write('/dev/stdout', msg, 'w');
|
82 | }
|
83 | } else {
|
84 | console.log(msg);
|
85 | }
|
86 | }
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | function replaceFunctionPlaceholders(fn, replacements) {
|
95 | if (replacements && typeof replacements === "object") {
|
96 | fn = fn.toString();
|
97 | for (var p in replacements) {
|
98 | if (replacements.hasOwnProperty(p)) {
|
99 | var match = new RegExp("%" + p + "%", "g");
|
100 | do {
|
101 | fn = fn.replace(match, replacements[p]);
|
102 | } while(fn.indexOf(match) !== -1);
|
103 | }
|
104 | }
|
105 | }
|
106 | return fn;
|
107 | }
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 | function evaluate(page, fn, replacements) {
|
117 | return page.evaluate(replaceFunctionPlaceholders(fn, replacements));
|
118 | }
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 | function setupWriteFileFunction(page, key, path_separator) {
|
128 | evaluate(page, function(){
|
129 | window["%resultsObj%"] = {};
|
130 | window.fs_path_separator = "%fs_path_separator%";
|
131 | window.__phantom_writeFile = function(filename, text) {
|
132 | window["%resultsObj%"][filename] = text;
|
133 | };
|
134 | }, {resultsObj: key, fs_path_separator: path_separator.replace("\\", "\\\\")});
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | function getXmlResults(page, key) {
|
145 | return evaluate(page, function(){
|
146 | return window["%resultsObj%"] || {};
|
147 | }, {resultsObj: key});
|
148 | }
|
149 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
156 | function processPage(status, page, resultsKey) {
|
157 | if (status === null && page) {
|
158 | page.__exit_code = null;
|
159 | return function(stat){
|
160 | processPage(stat, page, resultsKey);
|
161 | };
|
162 | }
|
163 | if (status !== "success") {
|
164 | console.error("Unable to load resource: " + address);
|
165 | page.__exit_code = 2;
|
166 | }
|
167 | else {
|
168 | var isFinished = function() {
|
169 | return evaluate(page, function(){
|
170 |
|
171 | if (window.jasmineReporters && window.jasmineReporters.startTime) {
|
172 | return !!window.jasmineReporters.endTime;
|
173 | }
|
174 |
|
175 | var durElem = document.querySelector(".html-reporter .duration");
|
176 | if (!durElem) {
|
177 | durElem = document.querySelector(".jasmine_html-reporter .duration");
|
178 | }
|
179 | return durElem && durElem.textContent && durElem.textContent.toLowerCase().indexOf("finished in") === 0;
|
180 | });
|
181 | };
|
182 | var getResultsFromHtmlRunner = function() {
|
183 | return evaluate(page, function(){
|
184 | var resultElem = document.querySelector(".html-reporter .alert .bar");
|
185 | if (!resultElem) {
|
186 | resultElem = document.querySelector(".jasmine_html-reporter .alert .bar");
|
187 | }
|
188 | return resultElem && resultElem.textContent &&
|
189 | resultElem.textContent.match(/(\d+) spec.* (\d+) failure.*/) ||
|
190 | ["Unable to determine success or failure."];
|
191 | });
|
192 | };
|
193 | var timeout = 60000;
|
194 | var loopInterval = 100;
|
195 | var ival = setInterval(function(){
|
196 | if (isFinished()) {
|
197 |
|
198 | var fs = require("fs"),
|
199 | xml_results = getXmlResults(page, resultsKey),
|
200 | output;
|
201 | for (var filename in xml_results) {
|
202 | if (xml_results.hasOwnProperty(filename) && (output = xml_results[filename]) && typeof(output) === "string") {
|
203 | fs.write(filename, output, "w");
|
204 | }
|
205 | }
|
206 |
|
207 |
|
208 | var results = getResultsFromHtmlRunner();
|
209 | var failures = Number(results[2]);
|
210 | if (failures > 0) {
|
211 | page.__exit_code = 1;
|
212 | clearInterval(ival);
|
213 | }
|
214 | else {
|
215 | page.__exit_code = 0;
|
216 | clearInterval(ival);
|
217 | }
|
218 | }
|
219 | else {
|
220 | timeout -= loopInterval;
|
221 | if (timeout <= 0) {
|
222 | console.log('Page has timed out; aborting.');
|
223 | page.__exit_code = 2;
|
224 | clearInterval(ival);
|
225 | }
|
226 | }
|
227 | }, loopInterval);
|
228 | }
|
229 | }
|