Merge branch 'feature/saucelabs'

* feature/saucelabs:
  browser: build
  support: add mocha cloud runner, client, and html test page
  test: [saucelabs] add auth placeholder
  deps: add mocha-cloud
This commit is contained in:
Jake Luer 2013-02-03 13:01:23 -05:00
commit d8f01c0f36
7 changed files with 542 additions and 29 deletions

25
.gitignore vendored
View file

@ -1,7 +1,20 @@
.settings.xml
node_modules/
npm-debug.log
.DS_Store
*.swp
coverage.html
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
node_modules
npm-debug.log
coverage.html
test/auth/*
!test/auth/.gitkeep

139
chai.js
View file

@ -663,12 +663,13 @@
* expect([ 1, 2, 3 ]).to.eql([ 1, 2, 3 ]);
*
* @name eql
* @alias eqls
* @param {Mixed} value
* @param {String} message _optional_
* @api public
*/
Assertion.addMethod('eql', function (obj, msg) {
function assertEql(obj, msg) {
if (msg) flag(this, 'message', msg);
this.assert(
_.eql(obj, flag(this, 'object'))
@ -678,7 +679,10 @@
, this._obj
, true
);
});
}
Assertion.addMethod('eql', assertEql);
Assertion.addMethod('eqls', assertEql);
/**
* ### .above(value)
@ -1315,20 +1319,21 @@
if (!errMsg) return this;
}
// next, check message
if (err.message && errMsg && errMsg instanceof RegExp) {
var message = typeof(err) === 'object' && "message" in err ? err.message : '' + err;
if (message && errMsg && errMsg instanceof RegExp) {
this.assert(
errMsg.exec(err.message)
, 'expected #{this} to throw error matching ' + errMsg + ' but got ' + _.inspect(err.message)
errMsg.exec(message)
, 'expected #{this} to throw error matching ' + errMsg + ' but got ' + _.inspect(message)
, 'expected #{this} to throw error not matching ' + errMsg
);
return this;
} else if (err.message && errMsg && 'string' === typeof errMsg) {
} else if (message && errMsg && 'string' === typeof errMsg) {
this.assert(
~err.message.indexOf(errMsg)
~message.indexOf(errMsg)
, 'expected #{this} to throw error including #{exp} but got #{act}'
, 'expected #{this} to throw error not including #{act}'
, errMsg
, err.message
, message
);
return this;
} else {
@ -2626,6 +2631,18 @@
var transferFlags = require('./transferFlags');
/*!
* Module variables
*/
// Check whether `__proto__` is supported
var hasProtoSupport = '__proto__' in Object;
// Without `__proto__` support, this module will need to add properties to a function.
// However, some Function.prototype methods cannot be overwritten,
// and there seems no easy cross-platform way to detect them (@see chaijs/chai/issues/69).
var excludeNames = /^(?:length|name|arguments|caller)$/;
/**
* ### addChainableMethod (ctx, name, method, chainingBehavior)
*
@ -2667,16 +2684,20 @@
return result === undefined ? this : result;
};
// Re-enumerate every time to better accomodate plugins.
var asserterNames = Object.getOwnPropertyNames(ctx);
asserterNames.forEach(function (asserterName) {
var pd = Object.getOwnPropertyDescriptor(ctx, asserterName)
, functionProtoPD = Object.getOwnPropertyDescriptor(Function.prototype, asserterName);
// Avoid trying to overwrite things that we can't, like `length` and `arguments`.
if (functionProtoPD && !functionProtoPD.configurable) return;
if (asserterName === 'arguments') return; // @see chaijs/chai/issues/69
Object.defineProperty(assert, asserterName, pd);
});
// Use `__proto__` if available
if (hasProtoSupport) {
assert.__proto__ = this;
}
// Otherwise, redefine all properties (slow!)
else {
var asserterNames = Object.getOwnPropertyNames(ctx);
asserterNames.forEach(function (asserterName) {
if (!excludeNames.test(asserterName)) {
var pd = Object.getOwnPropertyDescriptor(ctx, asserterName);
Object.defineProperty(assert, asserterName, pd);
}
});
}
transferFlags(this, assert);
return assert;
@ -2778,6 +2799,8 @@
module.exports = _deepEqual;
var getEnumerableProperties = require('./getEnumerableProperties');
// for the browser
var Buffer;
try {
@ -2862,8 +2885,8 @@
return _deepEqual(a, b, memos);
}
try {
var ka = Object.keys(a),
kb = Object.keys(b),
var ka = getEnumerableProperties(a),
kb = getEnumerableProperties(b),
key;
} catch (e) {//happens when one is a string literal and the other isn't
return false;
@ -2957,6 +2980,35 @@
}); // module: chai/utils/getActual.js
require.register("chai/utils/getEnumerableProperties.js", function(module, exports, require){
/*!
* Chai - getEnumerableProperties utility
* Copyright(c) 2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/**
* ### .getEnumerableProperties(object)
*
* This allows the retrieval of enumerable property names of an object,
* inherited or not.
*
* @param {Object} object
* @returns {Array}
* @name getEnumerableProperties
* @api public
*/
module.exports = function getEnumerableProperties(object) {
var result = [];
for (var name in object) {
result.push(name);
}
return result;
};
}); // module: chai/utils/getEnumerableProperties.js
require.register("chai/utils/getMessage.js", function(module, exports, require){
/*!
* Chai - message composition utility
@ -3140,6 +3192,45 @@
}); // module: chai/utils/getPathValue.js
require.register("chai/utils/getProperties.js", function(module, exports, require){
/*!
* Chai - getProperties utility
* Copyright(c) 2012 Jake Luer <jake@alogicalparadox.com>
* MIT Licensed
*/
/**
* ### .getProperties(object)
*
* This allows the retrieval of property names of an object, enumerable or not,
* inherited or not.
*
* @param {Object} object
* @returns {Array}
* @name getProperties
* @api public
*/
module.exports = function getProperties(object) {
var result = Object.getOwnPropertyNames(subject);
function addProperty(property) {
if (result.indexOf(property) === -1) {
result.push(property);
}
}
var proto = Object.getPrototypeOf(subject);
while (proto !== null) {
Object.getOwnPropertyNames(proto).forEach(addProperty);
proto = Object.getPrototypeOf(proto);
}
return result;
};
}); // module: chai/utils/getProperties.js
require.register("chai/utils/index.js", function(module, exports, require){
/*!
* chai
@ -3251,6 +3342,8 @@
// https://github.com/joyent/node/blob/f8c335d0caf47f16d31413f89aa28eda3878e3aa/lib/util.js
var getName = require('./getName');
var getProperties = require('./getProperties');
var getEnumerableProperties = require('./getEnumerableProperties');
module.exports = inspect;
@ -3291,7 +3384,7 @@
return html;
}
};
// Returns true if object is a DOM element.
var isDOMElement = function (object) {
if (typeof HTMLElement === 'object') {
@ -3327,8 +3420,8 @@
}
// Look up the keys of the object.
var visibleKeys = Object.keys(value);
var keys = ctx.showHidden ? Object.getOwnPropertyNames(value) : visibleKeys;
var visibleKeys = getEnumerableProperties(value);
var keys = ctx.showHidden ? getProperties(value) : visibleKeys;
// Some type of object without properties can be shortcutted.
// In IE, errors have a single `stack` property, or if they are vanilla `Error`,

View file

@ -30,5 +30,7 @@
"devDependencies": {
"folio": "0.3.x"
, "mocha": "*"
, "mocha-cloud": "*"
, "connect": "2.7.x"
}
}

120
support/mocha-cloud.js Normal file
View file

@ -0,0 +1,120 @@
/*!
* Mocha Cloud (SauceLabs) Test Runner
*/
/*!
* Module dependencies
*/
var Cloud = require('mocha-cloud')
, connect = require('connect')
, http = require('http')
, resolve = require('path').resolve
, auth;
/*!
* Attempt to load saucelabs authentication
*/
try {
auth = require('../test/auth/sauce.json');
} catch (ex) {
console.error('Error loading SauceLabs authentication at "./test/auth/sauce.json"');
process.exit(1);
}
/*!
* Create cloud and test server
*/
var app = connect()
, cloud = new Cloud('chai.js', auth.username, auth.key)
, server = http.createServer(app);
/*!
* Connect Middleware
*/
app.use(connect.static(resolve(__dirname, '..')))
/*!
* SauceLabs configuration
*/
cloud.url('http://localhost:3000/test/browser/sauce.html');
/*!
* Chrome
*/
cloud.browser('chrome', null, 'Mac 10.6');
cloud.browser('chrome', null, 'Mac 10.8');
/*!
* Firefox
*/
//cloud.browser('firefox', '17', 'Windows 2012');
//cloud.browser('firefox', '18', 'Windows 2012');
/*!
* Safari
*/
// osx
cloud.browser('safari', '5', 'Mac 10.6');
cloud.browser('safari', '6', 'Mac 10.8');
// win
//cloud.browser('safari', '5', 'Windows 2008');
/*!
* Internet Explorer
*/
//cloud.browser('iexplore', '10', 'Windows 2012');
/*!
* iPad
*/
cloud.browser('ipad', '4.3', 'Mac 10.6');
cloud.browser('ipad', '5', 'Mac 10.6');
cloud.browser('ipad', '5.1', 'Mac 10.8');
cloud.browser('ipad', '6', 'Mac 10.8');
/*!
* iPhone
*/
cloud.browser('iphone', '4.3', 'Mac 10.6');
cloud.browser('iphone', '5', 'Mac 10.6');
cloud.browser('iphone', '5.1', 'Mac 10.8');
cloud.browser('iphone', '6', 'Mac 10.8');
/*!
* SauceLabs events
*/
cloud.on('init', function (browser) {
console.log(' init : %s %s', browser.browserName, browser.version);
});
cloud.on('start', function (browser) {
console.log(' start : %s %s', browser.browserName, browser.version);
});
cloud.on('end', function (browser, res) {
console.log(' end : %s %s : %d failures', browser.browserName, browser.version, res.failures);
});
/*!
* Start server
*/
server.listen(3000, function () {
cloud.start(function () {
console.log('done');
server.close();
});
});

0
test/auth/.gitkeep Normal file
View file

View file

@ -0,0 +1,249 @@
;(function(){
/**
* hasOwnProperty.
*/
var has = Object.prototype.hasOwnProperty;
/**
* Require the given path.
*
* @param {String} path
* @return {Object} exports
* @api public
*/
function require(path, parent, orig) {
var resolved = require.resolve(path);
// lookup failed
if (null == resolved) {
orig = orig || path;
parent = parent || 'root';
var err = new Error('Failed to require "' + orig + '" from "' + parent + '"');
err.path = orig;
err.parent = parent;
err.require = true;
throw err;
}
var module = require.modules[resolved];
// perform real require()
// by invoking the module's
// registered function
if (!module.exports) {
module.exports = {};
module.client = module.component = true;
module.call(this, module.exports, require.relative(resolved), module);
}
return module.exports;
}
/**
* Registered modules.
*/
require.modules = {};
/**
* Registered aliases.
*/
require.aliases = {};
/**
* Resolve `path`.
*
* Lookup:
*
* - PATH/index.js
* - PATH.js
* - PATH
*
* @param {String} path
* @return {String} path or null
* @api private
*/
require.resolve = function(path) {
var index = path + '/index.js';
var paths = [
path,
path + '.js',
path + '.json',
path + '/index.js',
path + '/index.json'
];
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
if (has.call(require.modules, path)) return path;
}
if (has.call(require.aliases, index)) {
return require.aliases[index];
}
};
/**
* Normalize `path` relative to the current path.
*
* @param {String} curr
* @param {String} path
* @return {String}
* @api private
*/
require.normalize = function(curr, path) {
var segs = [];
if ('.' != path.charAt(0)) return path;
curr = curr.split('/');
path = path.split('/');
for (var i = 0; i < path.length; ++i) {
if ('..' == path[i]) {
curr.pop();
} else if ('.' != path[i] && '' != path[i]) {
segs.push(path[i]);
}
}
return curr.concat(segs).join('/');
};
/**
* Register module at `path` with callback `definition`.
*
* @param {String} path
* @param {Function} definition
* @api private
*/
require.register = function(path, definition) {
require.modules[path] = definition;
};
/**
* Alias a module definition.
*
* @param {String} from
* @param {String} to
* @api private
*/
require.alias = function(from, to) {
if (!has.call(require.modules, from)) {
throw new Error('Failed to alias "' + from + '", it does not exist');
}
require.aliases[to] = from;
};
/**
* Return a require function relative to the `parent` path.
*
* @param {String} parent
* @return {Function}
* @api private
*/
require.relative = function(parent) {
var p = require.normalize(parent, '..');
/**
* lastIndexOf helper.
*/
function lastIndexOf(arr, obj) {
var i = arr.length;
while (i--) {
if (arr[i] === obj) return i;
}
return -1;
}
/**
* The relative require() itself.
*/
function localRequire(path) {
var resolved = localRequire.resolve(path);
return require(resolved, parent, path);
}
/**
* Resolve relative to the parent.
*/
localRequire.resolve = function(path) {
// resolve deps by returning
// the dep in the nearest "deps"
// directory
if ('.' != path.charAt(0)) {
var segs = parent.split('/');
var i = lastIndexOf(segs, 'deps') + 1;
if (!i) i = 0;
path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
return path;
}
return require.normalize(p, path);
};
/**
* Check if module is defined at `path`.
*/
localRequire.exists = function(path) {
return has.call(require.modules, localRequire.resolve(path));
};
return localRequire;
};
require.register("mocha-cloud/client.js", function(exports, require, module){
/**
* Listen to `runner` events to populate a global
* `.mochaResults` var which may be used by selenium
* to report on results.
*
* cloud(mocha.run());
*
* @param {Runner} runner
* @api public
*/
module.exports = function(runner){
var failed = [];
runner.on('fail', function(test, err){
failed.push({
title: test.title,
fullTitle: test.fullTitle(),
error: {
message: err.message,
stack: err.stack
}
});
});
runner.on('end', function(){
runner.stats.failed = failed;
global.mochaResults = runner.stats;
});
};
});
require.alias("mocha-cloud/client.js", "mocha-cloud/index.js");
if (typeof exports == "object") {
module.exports = require("mocha-cloud");
} else if (typeof define == "function" && define.amd) {
define(require("mocha-cloud"));
} else {
window["cloud"] = require("mocha-cloud");
}})();

36
test/browser/sauce.html Normal file
View file

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html>
<head>
<title>Mocha</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" href="../../node_modules/mocha/mocha.css" />
<script src="../../node_modules/mocha/mocha.js"></script>
<script src="./mocha-cloud-client.js"></script>
<script>mocha.setup('tdd')</script>
<script src="../../chai.js"></script>
<script>
err = function (fn, msg) {
try {
fn();
throw new chai.AssertionError({ message: 'Expected an error' });
} catch (err) {
chai.expect(err.message).to.equal(msg);
}
};
</script>
<script src="../configuration.js"></script>
<script src="../expect.js"></script>
<script src="../should.js"></script>
<script src="../assert.js"></script>
<script src="../plugins.js"></script>
<script src="../utilities.js"></script>
<script src="../globalShould.js"></script>
<script>onload = function() {
cloud(mocha.run());
}
</script>
</head>
<body>
<div id="mocha"></div>
</body>
</html>