Skip to content

Commit

Permalink
Adds an improved PhantomJS test runner.
Browse files Browse the repository at this point in the history
This version uses the special abbot urls to get the list of tests to run.
This means it can be used to run the SproutCore unit tests, but also to
run the unit tests for your SproutCore app.

This makes use of the callPhantom API in PhantomJS to get detailed
results of the tests without needing to screen scrape.

Output will be the results for each test file. Any failing assertions will
be output as well. You can make this more verbose with -v and -V.
Exit status will be 0 if all tests pass, 1 if any tests fail, and 2 if
the test runner itself encounters an error.

To run, do:
  > phantomjs phantomjs/test_runner.js

Use the -h option to get details on how to use it.

In addition, I updated the Travis CI configuration to use this.
It is set to run all tests except for those in the experimental frameworks.
  • Loading branch information
Greg Fairbanks committed Sep 12, 2013
1 parent 9c07b3e commit 7d744cd
Show file tree
Hide file tree
Showing 7 changed files with 2,841 additions and 627 deletions.
10 changes: 1 addition & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,10 @@ language: ruby
rvm:
- 1.9.3

env:
- IP=127.0.0.1 PORT=4020 VERBOSE=false PASTEBIN=

install: true

before_script:
- gem install pastebin
- ./scripts/run_sc_server_master.sh
- sleep 5 # give sc-server some time to start

script: phantomjs tests/phantomjs_runner.phantomjs

after_script:
- "test -z \"${PASTEBIN}\" || echo \"Sharing failures summaries on pastebin for 1 hour...\""
- "test -z \"${PASTEBIN}\" || for f in *.html ; do test -e $f && ( id=`basename $f .html`; echo -n \" ${id} : \"; pastebin -f \"$f\" -e '1H' ) ; done"
script: phantomjs phantomjs/test_runner.js --no-experimental
22 changes: 15 additions & 7 deletions frameworks/foundation/tests/validators/password.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@
// ========================================================================
// SC.Validator.Password Base Tests
// ========================================================================
/*globals module test ok isObj equals expects */
/*globals module, test, ok, isObj, equals, expects */
// htmlbody('<!-- Test Styles -->\
// <form id="form" action="formaction"><input type="password" name="action" value="Test" id="field" maxlength="30"/></form>\
// ');
// module("SC.Validator.password");
//
// test("Attaching the field to the form", function() {
// var a = SC.Validator.Password.attachTo(SC.$("#form"), SC.$('#field'));
// alert(a);
// });
module("SC.Validator.password");

test("Attaching the field to the form");
/* WON'T FIX AT THIS TIME.
This entire file was commented out.
An empty unit test file will cause a timeout in the test runner,
so I'm converting this to a warning.
, function() {
var a = SC.Validator.Password.attachTo(SC.$("#form"), SC.$('#field'));
alert(a);
});
*/
4 changes: 4 additions & 0 deletions frameworks/testing/system/runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,10 @@ CoreTest.Runner = {
}
catch (e) {}
}

if (typeof window.callPhantom === 'function') {
window.callPhantom(r);
}
},

planDidRecord: function(plan, module, test, assertions, timings) {
Expand Down
181 changes: 181 additions & 0 deletions phantomjs/minimist.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
module.exports = function (args, opts) {
if (!opts) opts = {};

var flags = { bools : {}, strings : {} };

[].concat(opts['boolean']).filter(Boolean).forEach(function (key) {
flags.bools[key] = true;
});

[].concat(opts.string).filter(Boolean).forEach(function (key) {
flags.strings[key] = true;
});

var aliases = {};
Object.keys(opts.alias || {}).forEach(function (key) {
aliases[key] = [].concat(opts.alias[key]);
aliases[key].forEach(function (x) {
aliases[x] = [key].concat(aliases[key].filter(function (y) {
return x !== y;
}));
});
});

var defaults = opts['default'] || {};

var argv = { _ : [] };
Object.keys(flags.bools).forEach(function (key) {
setArg(key, defaults[key] === undefined ? false : defaults[key]);
});

function setArg (key, val) {
var value = !flags.strings[key] && isNumber(val)
? Number(val) : val
;
setKey(argv, key.split('.'), value);

(aliases[key] || []).forEach(function (x) {
setKey(argv, x.split('.'), value);
});
}

for (var i = 0; i < args.length; i++) {
var arg = args[i];

if (arg === '--') {
argv._.push.apply(argv._, args.slice(i + 1));
break;
}
else if (arg.match(/^--.+=/)) {
// Using [\s\S] instead of . because js doesn't support the
// 'dotall' regex modifier. See:
// http://stackoverflow.com/a/1068308/13216
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
setArg(m[1], m[2]);
}
else if (arg.match(/^--no-.+/)) {
var key = arg.match(/^--no-(.+)/)[1];
setArg(key, false);
}
else if (arg.match(/^--.+/)) {
var key = arg.match(/^--(.+)/)[1];
var next = args[i + 1];
if (next !== undefined && !next.match(/^-/)
&& !flags.bools[key]
&& (aliases[key] ? !flags.bools[aliases[key]] : true)) {
setArg(key, next);
i++;
}
else if (/^(true|false)$/.test(next)) {
setArg(key, next === 'true');
i++;
}
else {
setArg(key, true);
}
}
else if (arg.match(/^-[^-]+/)) {
var letters = arg.slice(1,-1).split('');

var broken = false;
for (var j = 0; j < letters.length; j++) {
var next = arg.slice(j+2);

if (next === '-') {
setArg(letters[j], next)
continue;
}

if (/[A-Za-z]/.test(letters[j])
&& /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
setArg(letters[j], next);
broken = true;
break;
}

if (letters[j+1] && letters[j+1].match(/\W/)) {
setArg(letters[j], arg.slice(j+2));
broken = true;
break;
}
else {
setArg(letters[j], true);
}
}

var key = arg.slice(-1)[0];
if (!broken && key !== '-') {

if (args[i+1] && !/^(-|--)[^-]/.test(args[i+1])
&& !flags.bools[key]
&& (aliases[key] ? !flags.bools[aliases[key]] : true)) {
setArg(key, args[i+1]);
i++;
}
else if (args[i+1] && /true|false/.test(args[i+1])) {
setArg(key, args[i+1] === 'true');
i++;
}
else {
setArg(key, true);
}
}
}
else {
argv._.push(
flags.strings['_'] || !isNumber(arg) ? arg : Number(arg)
);
}
}

Object.keys(defaults).forEach(function (key) {
if (!hasKey(argv, key.split('.'))) {
setKey(argv, key.split('.'), defaults[key]);

(aliases[key] || []).forEach(function (x) {
setKey(argv, x.split('.'), defaults[key]);
});
}
});

return argv;
};

function hasKey (obj, keys) {
var o = obj;
keys.slice(0,-1).forEach(function (key) {
o = (o[key] || {});
});

var key = keys[keys.length - 1];
return key in o;
}

function setKey (obj, keys, value) {
var o = obj;
keys.slice(0,-1).forEach(function (key) {
if (o[key] === undefined) o[key] = {};
o = o[key];
});

var key = keys[keys.length - 1];
if (o[key] === undefined || typeof o[key] === 'boolean') {
o[key] = value;
}
else if (Array.isArray(o[key])) {
o[key].push(value);
}
else {
o[key] = [ o[key], value ];
}
}

function isNumber (x) {
if (typeof x === 'number') return true;
if (/^0x[0-9a-f]+$/i.test(x)) return true;
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
}

function longest (xs) {
return Math.max.apply(null, xs.map(function (x) { return x.length }));
}
Loading

0 comments on commit 7d744cd

Please sign in to comment.