Permalink
Browse files

Add support for PhantomJS

It's nice to be able to run Hiro tests with PhantomJS. This patch
ports PhantomJS integration from Hiro 1 to Hiro 2 (notice changes
in hiro listeners, etc.)

To run existing Hiro tests do the following:

 1. Install PhantomJS
 2. Start serving files by running 'grunt run'
 3. In another terminal run 'phantomjs bin/phantom.js'

This patch closes GH-18.
  • Loading branch information...
1 parent a1fca3b commit 40a06f04258efd66b334ba4ebc75cc5540db7e99 @valueof committed May 13, 2012
Showing with 218 additions and 8 deletions.
  1. +139 −0 bin/phantom.js
  2. +3 −2 grunt.js
  3. +1 −1 src/test.js
  4. +2 −1 src/webui/index.html
  5. +63 −0 src/webui/phantom.js
  6. +10 −4 src/webui/webui.js
View
@@ -0,0 +1,139 @@
+/*jshint globalstrict:true */
+/*global require:false, phantom:false, console:false */
+
+"use strict";
+
+var fs = require("fs");
+var page = require("webpage").create();
+var stdout = fs.open("/dev/stdout", "w");
+var handlers = {};
+var tests = [];
+var startTime;
+
+function print(text) {
+ stdout.write(text);
+ stdout.flush();
+}
+
+function println(text) {
+ print((text || "") + "\n");
+}
+
+function seventy(str) {
+ return (new Array(70).join(str));
+}
+
+
+// Hiro handlers
+
+handlers["hiro.onStart"] = function () {
+ startTime = new Date();
+ println("Running all tests...");
+};
+
+handlers["hiro.onComplete"] = function () {
+ println();
+ println();
+
+ var failures = [];
+
+ function printFail(test) {
+ println(seventy("="));
+ println("FAIL: " + test.name);
+ println(seventy("-"));
+ print("Assertion: " + test.report.name);
+ println(" (" + test.report.expected + " != " + test.report.actual + ")");
+ println();
+ }
+
+ function printError(test) {
+ var msg = test.report.message;
+
+ if (msg.message)
+ msg = msg.message;
+
+ println(seventy("="));
+ println("ERROR: " + test.report.source);
+ println(seventy("-"));
+ println(msg);
+ println();
+ }
+
+ for (var i = 0; i < tests.length; i++) {
+ if (!tests[i].success) {
+ failures.push(tests[i]);
+
+ if (tests[i].report.message)
+ printError(tests[i]);
+ else
+ printFail(tests[i]);
+ }
+ }
+
+ println(seventy("-"));
+ println("Ran " + tests.length + " tests in " + (new Date() - startTime) / 1000.0 + "s");
+ println();
+
+ if (failures.length)
+ println("FAILED (failures=" + failures.length + ")");
+ else
+ println("OK");
+
+ phantom.exit(failures.length ? 1 : 0);
+};
+
+handlers["test.onComplete"] = function (args) {
+ if (!args)
+ return;
+
+ tests.push(args);
+
+ if (args.success)
+ print(".");
+ else
+ print(args.report.message ? "E" : "F");
+};
+
+// Phantom events
+
+page.onInitialized = function () {
+ /*global window:false */
+
+ page.evaluate(function () {
+ window.haunted = true;
+ });
+};
+
+page.onConsoleMessage = function (ev) {
+ ev = JSON.parse(ev);
+
+ if (handlers[ev.eventName])
+ handlers[ev.eventName](ev.data);
+};
+
+page.onError = function (msg) {
+ println(msg);
+};
+
+// Parse arguments and open the page. Default value
+// works if you run `make serve`.
+
+(function (args) {
+ var defpage = "http://localhost:7777/";
+ var kwargs = {};
+ var arg, url;
+
+ for (var i = 0; i < args.length; i++) {
+ arg = args[i].split("=");
+ kwargs[arg[0]] = arg.length > 1 ? arg[1] : null;
+ }
+
+ url = kwargs.page || defpage;
+ page.open(url, function (status) {
+ if (status !== "fail")
+ return;
+
+ console.log("FAILED to open page with URL: " + url);
+ phantom.exit(1);
+ });
+})(phantom.args);
View
@@ -27,7 +27,7 @@ module.exports = function (grunt) {
},
lint: {
- beforeconcat: [ "./src/**/*.js", "./test/**/*.js" ],
+ beforeconcat: [ "./src/**/*.js", "./test/**/*.js", "./bin/**/*.js" ],
afterconcat: [ "./dist/hiro.js" ]
},
@@ -100,7 +100,8 @@ module.exports = function (grunt) {
// Copy WebUI files.
- [ "icon.jpg", "webui.js", "webui.css", "index.html", "example.js" ].forEach(function (name) {
+ files = [ "icon.jpg", "webui.js", "webui.css", "index.html", "example.js", "phantom.js" ]
+ files.forEach(function (name) {
grunt.file.copy("./src/webui/" + name, "./dist/" + name);
});
View
@@ -80,7 +80,7 @@ Test.prototype = {
if (exp !== null) {
if (exp !== act) {
self.fail({
- source: "Test case:",
+ source: "Test case",
message: "Only " + act + " out of " + exp + " assertions were executed."
});
View
@@ -11,6 +11,7 @@
<script src="jquery.js"></script>
<script src="hiro.js"></script>
<script src="webui.js"></script>
+ <script src="phantom.js"></script>
</head>
<body onload="main();">
@@ -86,7 +87,7 @@
<script id="template-report-error" type="hiro/template">
<table>
<tr>
- <td><%= source %></td>
+ <td><%= source %>:</td>
<td><code><%= message %></code></td>
</tr>
</table>
View
@@ -0,0 +1,63 @@
+/*jshint browser:true */
+/*global hiro:false, console:false, $:false */
+
+(function () {
+ "use strict";
+
+ if (!window.haunted)
+ return;
+
+ // Wraps a Hiro listener and sends its return value
+ // to the console for PhantomJS to receive.
+
+ function bind(name, callback) {
+ callback = callback || function () { return {}; };
+
+ hiro.bind(name, function () {
+ console.log(JSON.stringify({
+ eventName: name,
+ data: callback.apply({}, arguments)
+ }));
+ });
+ }
+
+ // Hiro events
+
+ bind("hiro.onStart");
+ bind("hiro.onComplete");
+
+ // Suite specific events
+
+ bind("suite.onSetup", function (suite) {
+ return { name: suite.name };
+ });
+
+ bind("suite.onStart", function (suite) {
+ return { name: suite.name };
+ });
+
+ bind("suite.onComplete", function (suite, success) {
+ return { name: suite.name, success: success };
+ });
+
+ // Test specific events
+
+ bind("test.onStart", function (test) {
+ return { name: test.toString() };
+ });
+
+ bind("test.onComplete", function (test, success, report) {
+ if (!success) {
+ return {
+ name: test.name,
+ success: false,
+ report: report
+ };
+ }
+
+ return {
+ name: test.toString(),
+ success: true
+ };
+ });
+})();
View
@@ -23,17 +23,23 @@ var hiro, main;
}, 0);
});
- $("div.runall").click(function () {
- hiro.run();
- });
-
hiro.bind("hiro.onStart", function () {
$("div.runall button").attr("disabled", true);
});
hiro.bind("hiro.onComplete", function () {
$("div.runall button").removeAttr("disabled");
});
+
+ // If we're inside the PhantomJS environment start running tests
+ // right away. Otherwise assign a listener to the .runall button.
+
+ if (window.haunted)
+ return void hiro.run();
+
+ $("div.runall").click(function () {
+ hiro.run();
+ });
};
function SuiteView(name, model) {

0 comments on commit 40a06f0

Please sign in to comment.