', test.title, encodeURIComponent(test.fullTitle()));
var str = test.err.stack || test.err.toString();
// FF / Opera do not add the message
if (!~str.indexOf(test.err.message)) {
str = test.err.message + '\n' + str;
}
// <=IE7 stringifies to [Object Error]. Since it can be overloaded, we
// check for the result of the stringifying.
if ('[object Error]' == str) str = test.err.message;
// Safari doesn't give you a stack. Let's at least provide a source line.
if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) {
str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")";
}
el.appendChild(fragment('
%e
', str));
}
// toggle code
// TODO: defer
if (!test.pending) {
var h2 = el.getElementsByTagName('h2')[0];
on(h2, 'click', function(){
pre.style.display = 'none' == pre.style.display
? 'block'
: 'none';
});
var pre = fragment('
%e
', utils.clean(test.fn.toString()));
el.appendChild(pre);
pre.style.display = 'none';
}
// Don't call .appendChild if #mocha-report was already .shift()'ed off the stack.
if (stack[0]) stack[0].appendChild(el);
});
}
/**
* Display error `msg`.
*/
function error(msg) {
document.body.appendChild(fragment('
%s
', msg));
}
/**
* Return a DOM fragment from `html`.
*/
function fragment(html) {
var args = arguments
, div = document.createElement('div')
, i = 1;
div.innerHTML = html.replace(/%([se])/g, function(_, type){
switch (type) {
case 's': return String(args[i++]);
case 'e': return escape(args[i++]);
}
});
return div.firstChild;
}
/**
* Check for suites that do not have elements
* with `classname`, and hide them.
*/
function hideSuitesWithout(classname) {
var suites = document.getElementsByClassName('suite');
for (var i = 0; i < suites.length; i++) {
var els = suites[i].getElementsByClassName(classname);
if (0 == els.length) suites[i].className += ' hidden';
}
}
/**
* Unhide .hidden suites.
*/
function unhide() {
var els = document.getElementsByClassName('suite hidden');
for (var i = 0; i < els.length; ++i) {
els[i].className = els[i].className.replace('suite hidden', 'suite');
}
}
/**
* Set `el` text to `str`.
*/
function text(el, str) {
if (el.textContent) {
el.textContent = str;
} else {
el.innerText = str;
}
}
/**
* Listen on `event` with callback `fn`.
*/
function on(el, event, fn) {
if (el.addEventListener) {
el.addEventListener(event, fn, false);
} else {
el.attachEvent('on' + event, fn);
}
}
}); // module: reporters/html.js
require.register("reporters/index.js", function(module, exports, require){
exports.Base = require('./base');
exports.Dot = require('./dot');
exports.Doc = require('./doc');
exports.TAP = require('./tap');
exports.JSON = require('./json');
exports.HTML = require('./html');
exports.List = require('./list');
exports.Min = require('./min');
exports.Spec = require('./spec');
exports.Nyan = require('./nyan');
exports.XUnit = require('./xunit');
exports.Markdown = require('./markdown');
exports.Progress = require('./progress');
exports.Landing = require('./landing');
exports.JSONCov = require('./json-cov');
exports.HTMLCov = require('./html-cov');
exports.JSONStream = require('./json-stream');
exports.Teamcity = require('./teamcity');
}); // module: reporters/index.js
require.register("reporters/json-cov.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base');
/**
* Expose `JSONCov`.
*/
exports = module.exports = JSONCov;
/**
* Initialize a new `JsCoverage` reporter.
*
* @param {Runner} runner
* @param {Boolean} output
* @api public
*/
function JSONCov(runner, output) {
var self = this
, output = 1 == arguments.length ? true : output;
Base.call(this, runner);
var tests = []
, failures = []
, passes = [];
runner.on('test end', function(test){
tests.push(test);
});
runner.on('pass', function(test){
passes.push(test);
});
runner.on('fail', function(test){
failures.push(test);
});
runner.on('end', function(){
var cov = global._$jscoverage || {};
var result = self.cov = map(cov);
result.stats = self.stats;
result.tests = tests.map(clean);
result.failures = failures.map(clean);
result.passes = passes.map(clean);
if (!output) return;
process.stdout.write(JSON.stringify(result, null, 2 ));
});
}
/**
* Map jscoverage data to a JSON structure
* suitable for reporting.
*
* @param {Object} cov
* @return {Object}
* @api private
*/
function map(cov) {
var ret = {
instrumentation: 'node-jscoverage'
, sloc: 0
, hits: 0
, misses: 0
, coverage: 0
, files: []
};
for (var filename in cov) {
var data = coverage(filename, cov[filename]);
ret.files.push(data);
ret.hits += data.hits;
ret.misses += data.misses;
ret.sloc += data.sloc;
}
ret.files.sort(function(a, b) {
return a.filename.localeCompare(b.filename);
});
if (ret.sloc > 0) {
ret.coverage = (ret.hits / ret.sloc) * 100;
}
return ret;
};
/**
* Map jscoverage data for a single source file
* to a JSON structure suitable for reporting.
*
* @param {String} filename name of the source file
* @param {Object} data jscoverage coverage data
* @return {Object}
* @api private
*/
function coverage(filename, data) {
var ret = {
filename: filename,
coverage: 0,
hits: 0,
misses: 0,
sloc: 0,
source: {}
};
data.source.forEach(function(line, num){
num++;
if (data[num] === 0) {
ret.misses++;
ret.sloc++;
} else if (data[num] !== undefined) {
ret.hits++;
ret.sloc++;
}
ret.source[num] = {
source: line
, coverage: data[num] === undefined
? ''
: data[num]
};
});
ret.coverage = ret.hits / ret.sloc * 100;
return ret;
}
/**
* Return a plain-object representation of `test`
* free of cyclic properties etc.
*
* @param {Object} test
* @return {Object}
* @api private
*/
function clean(test) {
return {
title: test.title
, fullTitle: test.fullTitle()
, duration: test.duration
}
}
}); // module: reporters/json-cov.js
require.register("reporters/json-stream.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, color = Base.color;
/**
* Expose `List`.
*/
exports = module.exports = List;
/**
* Initialize a new `List` test reporter.
*
* @param {Runner} runner
* @api public
*/
function List(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, total = runner.total;
runner.on('start', function(){
console.log(JSON.stringify(['start', { total: total }]));
});
runner.on('pass', function(test){
console.log(JSON.stringify(['pass', clean(test)]));
});
runner.on('fail', function(test, err){
console.log(JSON.stringify(['fail', clean(test)]));
});
runner.on('end', function(){
process.stdout.write(JSON.stringify(['end', self.stats]));
});
}
/**
* Return a plain-object representation of `test`
* free of cyclic properties etc.
*
* @param {Object} test
* @return {Object}
* @api private
*/
function clean(test) {
return {
title: test.title
, fullTitle: test.fullTitle()
, duration: test.duration
}
}
}); // module: reporters/json-stream.js
require.register("reporters/json.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `JSON`.
*/
exports = module.exports = JSONReporter;
/**
* Initialize a new `JSON` reporter.
*
* @param {Runner} runner
* @api public
*/
function JSONReporter(runner) {
var self = this;
Base.call(this, runner);
var tests = []
, failures = []
, passes = [];
runner.on('test end', function(test){
tests.push(test);
});
runner.on('pass', function(test){
passes.push(test);
});
runner.on('fail', function(test){
failures.push(test);
});
runner.on('end', function(){
var obj = {
stats: self.stats
, tests: tests.map(clean)
, failures: failures.map(clean)
, passes: passes.map(clean)
};
process.stdout.write(JSON.stringify(obj, null, 2));
});
}
/**
* Return a plain-object representation of `test`
* free of cyclic properties etc.
*
* @param {Object} test
* @return {Object}
* @api private
*/
function clean(test) {
return {
title: test.title
, fullTitle: test.fullTitle()
, duration: test.duration
}
}
}); // module: reporters/json.js
require.register("reporters/landing.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `Landing`.
*/
exports = module.exports = Landing;
/**
* Airplane color.
*/
Base.colors.plane = 0;
/**
* Airplane crash color.
*/
Base.colors['plane crash'] = 31;
/**
* Runway color.
*/
Base.colors.runway = 90;
/**
* Initialize a new `Landing` reporter.
*
* @param {Runner} runner
* @api public
*/
function Landing(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, width = Base.window.width * .75 | 0
, total = runner.total
, stream = process.stdout
, plane = color('plane', '✈')
, crashed = -1
, n = 0;
function runway() {
var buf = Array(width).join('-');
return ' ' + color('runway', buf);
}
runner.on('start', function(){
stream.write('\n ');
cursor.hide();
});
runner.on('test end', function(test){
// check if the plane crashed
var col = -1 == crashed
? width * ++n / total | 0
: crashed;
// show the crash
if ('failed' == test.state) {
plane = color('plane crash', '✈');
crashed = col;
}
// render landing strip
stream.write('\u001b[4F\n\n');
stream.write(runway());
stream.write('\n ');
stream.write(color('runway', Array(col).join('⋅')));
stream.write(plane)
stream.write(color('runway', Array(width - col).join('⋅') + '\n'));
stream.write(runway());
stream.write('\u001b[0m');
});
runner.on('end', function(){
cursor.show();
console.log();
self.epilogue();
});
}
/**
* Inherit from `Base.prototype`.
*/
function F(){};
F.prototype = Base.prototype;
Landing.prototype = new F;
Landing.prototype.constructor = Landing;
}); // module: reporters/landing.js
require.register("reporters/list.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `List`.
*/
exports = module.exports = List;
/**
* Initialize a new `List` test reporter.
*
* @param {Runner} runner
* @api public
*/
function List(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, n = 0;
runner.on('start', function(){
console.log();
});
runner.on('test', function(test){
process.stdout.write(color('pass', ' ' + test.fullTitle() + ': '));
});
runner.on('pending', function(test){
var fmt = color('checkmark', ' -')
+ color('pending', ' %s');
console.log(fmt, test.fullTitle());
});
runner.on('pass', function(test){
var fmt = color('checkmark', ' '+Base.symbols.dot)
+ color('pass', ' %s: ')
+ color(test.speed, '%dms');
cursor.CR();
console.log(fmt, test.fullTitle(), test.duration);
});
runner.on('fail', function(test, err){
cursor.CR();
console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
});
runner.on('end', self.epilogue.bind(self));
}
/**
* Inherit from `Base.prototype`.
*/
function F(){};
F.prototype = Base.prototype;
List.prototype = new F;
List.prototype.constructor = List;
}); // module: reporters/list.js
require.register("reporters/markdown.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, utils = require('../utils');
/**
* Expose `Markdown`.
*/
exports = module.exports = Markdown;
/**
* Initialize a new `Markdown` reporter.
*
* @param {Runner} runner
* @api public
*/
function Markdown(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, level = 0
, buf = '';
function title(str) {
return Array(level).join('#') + ' ' + str;
}
function indent() {
return Array(level).join(' ');
}
function mapTOC(suite, obj) {
var ret = obj;
obj = obj[suite.title] = obj[suite.title] || { suite: suite };
suite.suites.forEach(function(suite){
mapTOC(suite, obj);
});
return ret;
}
function stringifyTOC(obj, level) {
++level;
var buf = '';
var link;
for (var key in obj) {
if ('suite' == key) continue;
if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
if (key) buf += Array(level).join(' ') + link;
buf += stringifyTOC(obj[key], level);
}
--level;
return buf;
}
function generateTOC(suite) {
var obj = mapTOC(suite, {});
return stringifyTOC(obj, 0);
}
generateTOC(runner.suite);
runner.on('suite', function(suite){
++level;
var slug = utils.slug(suite.fullTitle());
buf += '' + '\n';
buf += title(suite.title) + '\n';
});
runner.on('suite end', function(suite){
--level;
});
runner.on('pass', function(test){
var code = utils.clean(test.fn.toString());
buf += test.title + '.\n';
buf += '\n```js\n';
buf += code + '\n';
buf += '```\n\n';
});
runner.on('end', function(){
process.stdout.write('# TOC\n');
process.stdout.write(generateTOC(runner.suite));
process.stdout.write(buf);
});
}
}); // module: reporters/markdown.js
require.register("reporters/min.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base');
/**
* Expose `Min`.
*/
exports = module.exports = Min;
/**
* Initialize a new `Min` minimal test reporter (best used with --watch).
*
* @param {Runner} runner
* @api public
*/
function Min(runner) {
Base.call(this, runner);
runner.on('start', function(){
// clear screen
process.stdout.write('\u001b[2J');
// set cursor position
process.stdout.write('\u001b[1;3H');
});
runner.on('end', this.epilogue.bind(this));
}
/**
* Inherit from `Base.prototype`.
*/
function F(){};
F.prototype = Base.prototype;
Min.prototype = new F;
Min.prototype.constructor = Min;
}); // module: reporters/min.js
require.register("reporters/nyan.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, color = Base.color;
/**
* Expose `Dot`.
*/
exports = module.exports = NyanCat;
/**
* Initialize a new `Dot` matrix test reporter.
*
* @param {Runner} runner
* @api public
*/
function NyanCat(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, width = Base.window.width * .75 | 0
, rainbowColors = this.rainbowColors = self.generateColors()
, colorIndex = this.colorIndex = 0
, numerOfLines = this.numberOfLines = 4
, trajectories = this.trajectories = [[], [], [], []]
, nyanCatWidth = this.nyanCatWidth = 11
, trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth)
, scoreboardWidth = this.scoreboardWidth = 5
, tick = this.tick = 0
, n = 0;
runner.on('start', function(){
Base.cursor.hide();
self.draw('start');
});
runner.on('pending', function(test){
self.draw('pending');
});
runner.on('pass', function(test){
self.draw('pass');
});
runner.on('fail', function(test, err){
self.draw('fail');
});
runner.on('end', function(){
Base.cursor.show();
for (var i = 0; i < self.numberOfLines; i++) write('\n');
self.epilogue();
});
}
/**
* Draw the nyan cat with runner `status`.
*
* @param {String} status
* @api private
*/
NyanCat.prototype.draw = function(status){
this.appendRainbow();
this.drawScoreboard();
this.drawRainbow();
this.drawNyanCat(status);
this.tick = !this.tick;
};
/**
* Draw the "scoreboard" showing the number
* of passes, failures and pending tests.
*
* @api private
*/
NyanCat.prototype.drawScoreboard = function(){
var stats = this.stats;
var colors = Base.colors;
function draw(color, n) {
write(' ');
write('\u001b[' + color + 'm' + n + '\u001b[0m');
write('\n');
}
draw(colors.green, stats.passes);
draw(colors.fail, stats.failures);
draw(colors.pending, stats.pending);
write('\n');
this.cursorUp(this.numberOfLines);
};
/**
* Append the rainbow.
*
* @api private
*/
NyanCat.prototype.appendRainbow = function(){
var segment = this.tick ? '_' : '-';
var rainbowified = this.rainbowify(segment);
for (var index = 0; index < this.numberOfLines; index++) {
var trajectory = this.trajectories[index];
if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift();
trajectory.push(rainbowified);
}
};
/**
* Draw the rainbow.
*
* @api private
*/
NyanCat.prototype.drawRainbow = function(){
var self = this;
this.trajectories.forEach(function(line, index) {
write('\u001b[' + self.scoreboardWidth + 'C');
write(line.join(''));
write('\n');
});
this.cursorUp(this.numberOfLines);
};
/**
* Draw the nyan cat with `status`.
*
* @param {String} status
* @api private
*/
NyanCat.prototype.drawNyanCat = function(status) {
var self = this;
var startWidth = this.scoreboardWidth + this.trajectories[0].length;
var color = '\u001b[' + startWidth + 'C';
var padding = '';
write(color);
write('_,------,');
write('\n');
write(color);
padding = self.tick ? ' ' : ' ';
write('_|' + padding + '/\\_/\\ ');
write('\n');
write(color);
padding = self.tick ? '_' : '__';
var tail = self.tick ? '~' : '^';
var face;
switch (status) {
case 'pass':
face = '( ^ .^)';
break;
case 'fail':
face = '( o .o)';
break;
default:
face = '( - .-)';
}
write(tail + '|' + padding + face + ' ');
write('\n');
write(color);
padding = self.tick ? ' ' : ' ';
write(padding + '"" "" ');
write('\n');
this.cursorUp(this.numberOfLines);
};
/**
* Move cursor up `n`.
*
* @param {Number} n
* @api private
*/
NyanCat.prototype.cursorUp = function(n) {
write('\u001b[' + n + 'A');
};
/**
* Move cursor down `n`.
*
* @param {Number} n
* @api private
*/
NyanCat.prototype.cursorDown = function(n) {
write('\u001b[' + n + 'B');
};
/**
* Generate rainbow colors.
*
* @return {Array}
* @api private
*/
NyanCat.prototype.generateColors = function(){
var colors = [];
for (var i = 0; i < (6 * 7); i++) {
var pi3 = Math.floor(Math.PI / 3);
var n = (i * (1.0 / 6));
var r = Math.floor(3 * Math.sin(n) + 3);
var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3);
var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3);
colors.push(36 * r + 6 * g + b + 16);
}
return colors;
};
/**
* Apply rainbow to the given `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
NyanCat.prototype.rainbowify = function(str){
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
this.colorIndex += 1;
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
};
/**
* Stdout helper.
*/
function write(string) {
process.stdout.write(string);
}
/**
* Inherit from `Base.prototype`.
*/
function F(){};
F.prototype = Base.prototype;
NyanCat.prototype = new F;
NyanCat.prototype.constructor = NyanCat;
}); // module: reporters/nyan.js
require.register("reporters/progress.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `Progress`.
*/
exports = module.exports = Progress;
/**
* General progress bar color.
*/
Base.colors.progress = 90;
/**
* Initialize a new `Progress` bar test reporter.
*
* @param {Runner} runner
* @param {Object} options
* @api public
*/
function Progress(runner, options) {
Base.call(this, runner);
var self = this
, options = options || {}
, stats = this.stats
, width = Base.window.width * .50 | 0
, total = runner.total
, complete = 0
, max = Math.max;
// default chars
options.open = options.open || '[';
options.complete = options.complete || '▬';
options.incomplete = options.incomplete || Base.symbols.dot;
options.close = options.close || ']';
options.verbose = false;
// tests started
runner.on('start', function(){
console.log();
cursor.hide();
});
// tests complete
runner.on('test end', function(){
complete++;
var incomplete = total - complete
, percent = complete / total
, n = width * percent | 0
, i = width - n;
cursor.CR();
process.stdout.write('\u001b[J');
process.stdout.write(color('progress', ' ' + options.open));
process.stdout.write(Array(n).join(options.complete));
process.stdout.write(Array(i).join(options.incomplete));
process.stdout.write(color('progress', options.close));
if (options.verbose) {
process.stdout.write(color('progress', ' ' + complete + ' of ' + total));
}
});
// tests are complete, output some stats
// and the failures if any
runner.on('end', function(){
cursor.show();
console.log();
self.epilogue();
});
}
/**
* Inherit from `Base.prototype`.
*/
function F(){};
F.prototype = Base.prototype;
Progress.prototype = new F;
Progress.prototype.constructor = Progress;
}); // module: reporters/progress.js
require.register("reporters/spec.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `Spec`.
*/
exports = module.exports = Spec;
/**
* Initialize a new `Spec` test reporter.
*
* @param {Runner} runner
* @api public
*/
function Spec(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, indents = 0
, n = 0;
function indent() {
return Array(indents).join(' ')
}
runner.on('start', function(){
console.log();
});
runner.on('suite', function(suite){
++indents;
console.log(color('suite', '%s%s'), indent(), suite.title);
});
runner.on('suite end', function(suite){
--indents;
if (1 == indents) console.log();
});
runner.on('test', function(test){
process.stdout.write(indent() + color('pass', ' ◦ ' + test.title + ': '));
});
runner.on('pending', function(test){
var fmt = indent() + color('pending', ' - %s');
console.log(fmt, test.title);
});
runner.on('pass', function(test){
if ('fast' == test.speed) {
var fmt = indent()
+ color('checkmark', ' ' + Base.symbols.ok)
+ color('pass', ' %s ');
cursor.CR();
console.log(fmt, test.title);
} else {
var fmt = indent()
+ color('checkmark', ' ' + Base.symbols.ok)
+ color('pass', ' %s ')
+ color(test.speed, '(%dms)');
cursor.CR();
console.log(fmt, test.title, test.duration);
}
});
runner.on('fail', function(test, err){
cursor.CR();
console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
});
runner.on('end', self.epilogue.bind(self));
}
/**
* Inherit from `Base.prototype`.
*/
function F(){};
F.prototype = Base.prototype;
Spec.prototype = new F;
Spec.prototype.constructor = Spec;
}); // module: reporters/spec.js
require.register("reporters/tap.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, cursor = Base.cursor
, color = Base.color;
/**
* Expose `TAP`.
*/
exports = module.exports = TAP;
/**
* Initialize a new `TAP` reporter.
*
* @param {Runner} runner
* @api public
*/
function TAP(runner) {
Base.call(this, runner);
var self = this
, stats = this.stats
, n = 1
, passes = 0
, failures = 0;
runner.on('start', function(){
var total = runner.grepTotal(runner.suite);
console.log('%d..%d', 1, total);
});
runner.on('test end', function(){
++n;
});
runner.on('pending', function(test){
console.log('ok %d %s # SKIP -', n, title(test));
});
runner.on('pass', function(test){
passes++;
console.log('ok %d %s', n, title(test));
});
runner.on('fail', function(test, err){
failures++;
console.log('not ok %d %s', n, title(test));
if (err.stack) console.log(err.stack.replace(/^/gm, ' '));
});
runner.on('end', function(){
console.log('# tests ' + (passes + failures));
console.log('# pass ' + passes);
console.log('# fail ' + failures);
});
}
/**
* Return a TAP-safe title of `test`
*
* @param {Object} test
* @return {String}
* @api private
*/
function title(test) {
return test.fullTitle().replace(/#/g, '');
}
}); // module: reporters/tap.js
require.register("reporters/teamcity.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base');
/**
* Expose `Teamcity`.
*/
exports = module.exports = Teamcity;
/**
* Initialize a new `Teamcity` reporter.
*
* @param {Runner} runner
* @api public
*/
function Teamcity(runner) {
Base.call(this, runner);
var stats = this.stats;
runner.on('start', function() {
console.log("##teamcity[testSuiteStarted name='mocha.suite']");
});
runner.on('test', function(test) {
console.log("##teamcity[testStarted name='" + escape(test.fullTitle()) + "']");
});
runner.on('fail', function(test, err) {
console.log("##teamcity[testFailed name='" + escape(test.fullTitle()) + "' message='" + escape(err.message) + "']");
});
runner.on('pending', function(test) {
console.log("##teamcity[testIgnored name='" + escape(test.fullTitle()) + "' message='pending']");
});
runner.on('test end', function(test) {
console.log("##teamcity[testFinished name='" + escape(test.fullTitle()) + "' duration='" + test.duration + "']");
});
runner.on('end', function() {
console.log("##teamcity[testSuiteFinished name='mocha.suite' duration='" + stats.duration + "']");
});
}
/**
* Escape the given `str`.
*/
function escape(str) {
return str
.replace(/\|/g, "||")
.replace(/\n/g, "|n")
.replace(/\r/g, "|r")
.replace(/\[/g, "|[")
.replace(/\]/g, "|]")
.replace(/\u0085/g, "|x")
.replace(/\u2028/g, "|l")
.replace(/\u2029/g, "|p")
.replace(/'/g, "|'");
}
}); // module: reporters/teamcity.js
require.register("reporters/xunit.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Base = require('./base')
, utils = require('../utils')
, escape = utils.escape;
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = global.Date
, setTimeout = global.setTimeout
, setInterval = global.setInterval
, clearTimeout = global.clearTimeout
, clearInterval = global.clearInterval;
/**
* Expose `XUnit`.
*/
exports = module.exports = XUnit;
/**
* Initialize a new `XUnit` reporter.
*
* @param {Runner} runner
* @api public
*/
function XUnit(runner) {
Base.call(this, runner);
var stats = this.stats
, tests = []
, self = this;
runner.on('pass', function(test){
tests.push(test);
});
runner.on('fail', function(test){
tests.push(test);
});
runner.on('end', function(){
console.log(tag('testsuite', {
name: 'Mocha Tests'
, tests: stats.tests
, failures: stats.failures
, errors: stats.failures
, skip: stats.tests - stats.failures - stats.passes
, timestamp: (new Date).toUTCString()
, time: stats.duration / 1000
}, false));
tests.forEach(test);
console.log('');
});
}
/**
* Inherit from `Base.prototype`.
*/
function F(){};
F.prototype = Base.prototype;
XUnit.prototype = new F;
XUnit.prototype.constructor = XUnit;
/**
* Output tag for the given `test.`
*/
function test(test) {
var attrs = {
classname: test.parent.fullTitle()
, name: test.title
, time: test.duration / 1000
};
if ('failed' == test.state) {
var err = test.err;
attrs.message = escape(err.message);
console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
} else if (test.pending) {
console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
} else {
console.log(tag('testcase', attrs, true) );
}
}
/**
* HTML tag helper.
*/
function tag(name, attrs, close, content) {
var end = close ? '/>' : '>'
, pairs = []
, tag;
for (var key in attrs) {
pairs.push(key + '="' + escape(attrs[key]) + '"');
}
tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end;
if (content) tag += content + '' + name + end;
return tag;
}
/**
* Return cdata escaped CDATA `str`.
*/
function cdata(str) {
return '';
}
}); // module: reporters/xunit.js
require.register("runnable.js", function(module, exports, require){
/**
* Module dependencies.
*/
var EventEmitter = require('browser/events').EventEmitter
, debug = require('browser/debug')('mocha:runnable')
, milliseconds = require('./ms');
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = global.Date
, setTimeout = global.setTimeout
, setInterval = global.setInterval
, clearTimeout = global.clearTimeout
, clearInterval = global.clearInterval;
/**
* Object#toString().
*/
var toString = Object.prototype.toString;
/**
* Expose `Runnable`.
*/
module.exports = Runnable;
/**
* Initialize a new `Runnable` with the given `title` and callback `fn`.
*
* @param {String} title
* @param {Function} fn
* @api private
*/
function Runnable(title, fn) {
this.title = title;
this.fn = fn;
this.async = fn && fn.length;
this.sync = ! this.async;
this._timeout = 2000;
this._slow = 75;
this.timedOut = false;
}
/**
* Inherit from `EventEmitter.prototype`.
*/
function F(){};
F.prototype = EventEmitter.prototype;
Runnable.prototype = new F;
Runnable.prototype.constructor = Runnable;
/**
* Set & get timeout `ms`.
*
* @param {Number|String} ms
* @return {Runnable|Number} ms or self
* @api private
*/
Runnable.prototype.timeout = function(ms){
if (0 == arguments.length) return this._timeout;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('timeout %d', ms);
this._timeout = ms;
if (this.timer) this.resetTimeout();
return this;
};
/**
* Set & get slow `ms`.
*
* @param {Number|String} ms
* @return {Runnable|Number} ms or self
* @api private
*/
Runnable.prototype.slow = function(ms){
if (0 === arguments.length) return this._slow;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('timeout %d', ms);
this._slow = ms;
return this;
};
/**
* Return the full title generated by recursively
* concatenating the parent's full title.
*
* @return {String}
* @api public
*/
Runnable.prototype.fullTitle = function(){
return this.parent.fullTitle() + ' ' + this.title;
};
/**
* Clear the timeout.
*
* @api private
*/
Runnable.prototype.clearTimeout = function(){
clearTimeout(this.timer);
};
/**
* Inspect the runnable void of private properties.
*
* @return {String}
* @api private
*/
Runnable.prototype.inspect = function(){
return JSON.stringify(this, function(key, val){
if ('_' == key[0]) return;
if ('parent' == key) return '#';
if ('ctx' == key) return '#';
return val;
}, 2);
};
/**
* Reset the timeout.
*
* @api private
*/
Runnable.prototype.resetTimeout = function(){
var self = this
, ms = this.timeout();
this.clearTimeout();
if (ms) {
this.timer = setTimeout(function(){
self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
self.timedOut = true;
}, ms);
}
};
/**
* Run the test and invoke `fn(err)`.
*
* @param {Function} fn
* @api private
*/
Runnable.prototype.run = function(fn){
var self = this
, ms = this.timeout()
, start = new Date
, ctx = this.ctx
, finished
, emitted;
if (ctx) ctx.runnable(this);
// timeout
if (this.async) {
if (ms) {
this.timer = setTimeout(function(){
done(new Error('timeout of ' + ms + 'ms exceeded'));
self.timedOut = true;
}, ms);
}
}
// called multiple times
function multiple(err) {
if (emitted) return;
emitted = true;
self.emit('error', err || new Error('done() called multiple times'));
}
// finished
function done(err) {
if (self.timedOut) return;
if (finished) return multiple(err);
self.clearTimeout();
self.duration = new Date - start;
finished = true;
fn(err);
}
// for .resetTimeout()
this.callback = done;
// async
if (this.async) {
try {
this.fn.call(ctx, function(err){
if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
done();
});
} catch (err) {
done(err);
}
return;
}
if (this.asyncOnly) {
return done(new Error('--async-only option in use without declaring `done()`'));
}
// sync
try {
if (!this.pending) this.fn.call(ctx);
this.duration = new Date - start;
fn();
} catch (err) {
fn(err);
}
};
}); // module: runnable.js
require.register("runner.js", function(module, exports, require){
/**
* Module dependencies.
*/
var EventEmitter = require('browser/events').EventEmitter
, debug = require('browser/debug')('mocha:runner')
, Test = require('./test')
, utils = require('./utils')
, filter = utils.filter
, keys = utils.keys;
/**
* Non-enumerable globals.
*/
var globals = [
'setTimeout',
'clearTimeout',
'setInterval',
'clearInterval',
'XMLHttpRequest',
'Date'
];
/**
* Expose `Runner`.
*/
module.exports = Runner;
/**
* Initialize a `Runner` for the given `suite`.
*
* Events:
*
* - `start` execution started
* - `end` execution complete
* - `suite` (suite) test suite execution started
* - `suite end` (suite) all tests (and sub-suites) have finished
* - `test` (test) test execution started
* - `test end` (test) test completed
* - `hook` (hook) hook execution started
* - `hook end` (hook) hook complete
* - `pass` (test) test passed
* - `fail` (test, err) test failed
*
* @api public
*/
function Runner(suite) {
var self = this;
this._globals = [];
this.suite = suite;
this.total = suite.total();
this.failures = 0;
this.on('test end', function(test){ self.checkGlobals(test); });
this.on('hook end', function(hook){ self.checkGlobals(hook); });
this.grep(/.*/);
this.globals(this.globalProps().concat(['errno']));
}
/**
* Wrapper for setImmediate, process.nextTick, or browser polyfill.
*
* @param {Function} fn
* @api private
*/
Runner.immediately = global.setImmediate || process.nextTick;
/**
* Inherit from `EventEmitter.prototype`.
*/
function F(){};
F.prototype = EventEmitter.prototype;
Runner.prototype = new F;
Runner.prototype.constructor = Runner;
/**
* Run tests with full titles matching `re`. Updates runner.total
* with number of tests matched.
*
* @param {RegExp} re
* @param {Boolean} invert
* @return {Runner} for chaining
* @api public
*/
Runner.prototype.grep = function(re, invert){
debug('grep %s', re);
this._grep = re;
this._invert = invert;
this.total = this.grepTotal(this.suite);
return this;
};
/**
* Returns the number of tests matching the grep search for the
* given suite.
*
* @param {Suite} suite
* @return {Number}
* @api public
*/
Runner.prototype.grepTotal = function(suite) {
var self = this;
var total = 0;
suite.eachTest(function(test){
var match = self._grep.test(test.fullTitle());
if (self._invert) match = !match;
if (match) total++;
});
return total;
};
/**
* Return a list of global properties.
*
* @return {Array}
* @api private
*/
Runner.prototype.globalProps = function() {
var props = utils.keys(global);
// non-enumerables
for (var i = 0; i < globals.length; ++i) {
if (~utils.indexOf(props, globals[i])) continue;
props.push(globals[i]);
}
return props;
};
/**
* Allow the given `arr` of globals.
*
* @param {Array} arr
* @return {Runner} for chaining
* @api public
*/
Runner.prototype.globals = function(arr){
if (0 == arguments.length) return this._globals;
debug('globals %j', arr);
utils.forEach(arr, function(arr){
this._globals.push(arr);
}, this);
return this;
};
/**
* Check for global variable leaks.
*
* @api private
*/
Runner.prototype.checkGlobals = function(test){
if (this.ignoreLeaks) return;
var ok = this._globals;
var globals = this.globalProps();
var isNode = process.kill;
var leaks;
// check length - 2 ('errno' and 'location' globals)
if (isNode && 1 == ok.length - globals.length) return
else if (2 == ok.length - globals.length) return;
leaks = filterLeaks(ok, globals);
this._globals = this._globals.concat(leaks);
if (leaks.length > 1) {
this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + ''));
} else if (leaks.length) {
this.fail(test, new Error('global leak detected: ' + leaks[0]));
}
};
/**
* Fail the given `test`.
*
* @param {Test} test
* @param {Error} err
* @api private
*/
Runner.prototype.fail = function(test, err){
++this.failures;
test.state = 'failed';
if ('string' == typeof err) {
err = new Error('the string "' + err + '" was thrown, throw an Error :)');
}
this.emit('fail', test, err);
};
/**
* Fail the given `hook` with `err`.
*
* Hook failures (currently) hard-end due
* to that fact that a failing hook will
* surely cause subsequent tests to fail,
* causing jumbled reporting.
*
* @param {Hook} hook
* @param {Error} err
* @api private
*/
Runner.prototype.failHook = function(hook, err){
this.fail(hook, err);
this.emit('end');
};
/**
* Run hook `name` callbacks and then invoke `fn()`.
*
* @param {String} name
* @param {Function} function
* @api private
*/
Runner.prototype.hook = function(name, fn){
var suite = this.suite
, hooks = suite['_' + name]
, self = this
, timer;
function next(i) {
var hook = hooks[i];
if (!hook) return fn();
self.currentRunnable = hook;
self.emit('hook', hook);
hook.on('error', function(err){
self.failHook(hook, err);
});
hook.run(function(err){
hook.removeAllListeners('error');
var testError = hook.error();
if (testError) self.fail(self.test, testError);
if (err) return self.failHook(hook, err);
self.emit('hook end', hook);
next(++i);
});
}
Runner.immediately(function(){
next(0);
});
};
/**
* Run hook `name` for the given array of `suites`
* in order, and callback `fn(err)`.
*
* @param {String} name
* @param {Array} suites
* @param {Function} fn
* @api private
*/
Runner.prototype.hooks = function(name, suites, fn){
var self = this
, orig = this.suite;
function next(suite) {
self.suite = suite;
if (!suite) {
self.suite = orig;
return fn();
}
self.hook(name, function(err){
if (err) {
self.suite = orig;
return fn(err);
}
next(suites.pop());
});
}
next(suites.pop());
};
/**
* Run hooks from the top level down.
*
* @param {String} name
* @param {Function} fn
* @api private
*/
Runner.prototype.hookUp = function(name, fn){
var suites = [this.suite].concat(this.parents()).reverse();
this.hooks(name, suites, fn);
};
/**
* Run hooks from the bottom up.
*
* @param {String} name
* @param {Function} fn
* @api private
*/
Runner.prototype.hookDown = function(name, fn){
var suites = [this.suite].concat(this.parents());
this.hooks(name, suites, fn);
};
/**
* Return an array of parent Suites from
* closest to furthest.
*
* @return {Array}
* @api private
*/
Runner.prototype.parents = function(){
var suite = this.suite
, suites = [];
while (suite = suite.parent) suites.push(suite);
return suites;
};
/**
* Run the current test and callback `fn(err)`.
*
* @param {Function} fn
* @api private
*/
Runner.prototype.runTest = function(fn){
var test = this.test
, self = this;
if (this.asyncOnly) test.asyncOnly = true;
try {
test.on('error', function(err){
self.fail(test, err);
});
test.run(fn);
} catch (err) {
fn(err);
}
};
/**
* Run tests in the given `suite` and invoke
* the callback `fn()` when complete.
*
* @param {Suite} suite
* @param {Function} fn
* @api private
*/
Runner.prototype.runTests = function(suite, fn){
var self = this
, tests = suite.tests.slice()
, test;
function next(err) {
// if we bail after first err
if (self.failures && suite._bail) return fn();
// next test
test = tests.shift();
// all done
if (!test) return fn();
// grep
var match = self._grep.test(test.fullTitle());
if (self._invert) match = !match;
if (!match) return next();
// pending
if (test.pending) {
self.emit('pending', test);
self.emit('test end', test);
return next();
}
// execute test and hook(s)
self.emit('test', self.test = test);
self.hookDown('beforeEach', function(){
self.currentRunnable = self.test;
self.runTest(function(err){
test = self.test;
if (err) {
self.fail(test, err);
self.emit('test end', test);
return self.hookUp('afterEach', next);
}
test.state = 'passed';
self.emit('pass', test);
self.emit('test end', test);
self.hookUp('afterEach', next);
});
});
}
this.next = next;
next();
};
/**
* Run the given `suite` and invoke the
* callback `fn()` when complete.
*
* @param {Suite} suite
* @param {Function} fn
* @api private
*/
Runner.prototype.runSuite = function(suite, fn){
var total = this.grepTotal(suite)
, self = this
, i = 0;
debug('run suite %s', suite.fullTitle());
if (!total) return fn();
this.emit('suite', this.suite = suite);
function next() {
var curr = suite.suites[i++];
if (!curr) return done();
self.runSuite(curr, next);
}
function done() {
self.suite = suite;
self.hook('afterAll', function(){
self.emit('suite end', suite);
fn();
});
}
this.hook('beforeAll', function(){
self.runTests(suite, next);
});
};
/**
* Handle uncaught exceptions.
*
* @param {Error} err
* @api private
*/
Runner.prototype.uncaught = function(err){
debug('uncaught exception %s', err.message);
var runnable = this.currentRunnable;
if (!runnable || 'failed' == runnable.state) return;
runnable.clearTimeout();
err.uncaught = true;
this.fail(runnable, err);
// recover from test
if ('test' == runnable.type) {
this.emit('test end', runnable);
this.hookUp('afterEach', this.next);
return;
}
// bail on hooks
this.emit('end');
};
/**
* Run the root suite and invoke `fn(failures)`
* on completion.
*
* @param {Function} fn
* @return {Runner} for chaining
* @api public
*/
Runner.prototype.run = function(fn){
var self = this
, fn = fn || function(){};
function uncaught(err){
self.uncaught(err);
}
debug('start');
// callback
this.on('end', function(){
debug('end');
process.removeListener('uncaughtException', uncaught);
fn(self.failures);
});
// run suites
this.emit('start');
this.runSuite(this.suite, function(){
debug('finished running');
self.emit('end');
});
// uncaught exception
process.on('uncaughtException', uncaught);
return this;
};
/**
* Filter leaks with the given globals flagged as `ok`.
*
* @param {Array} ok
* @param {Array} globals
* @return {Array}
* @api private
*/
function filterLeaks(ok, globals) {
return filter(globals, function(key){
// Firefox and Chrome exposes iframes as index inside the window object
if (/^d+/.test(key)) return false;
var matched = filter(ok, function(ok){
if (~ok.indexOf('*')) return 0 == key.indexOf(ok.split('*')[0]);
// Opera and IE expose global variables for HTML element IDs (issue #243)
if (/^mocha-/.test(key)) return true;
return key == ok;
});
return matched.length == 0 && (!global.navigator || 'onerror' !== key);
});
}
}); // module: runner.js
require.register("suite.js", function(module, exports, require){
/**
* Module dependencies.
*/
var EventEmitter = require('browser/events').EventEmitter
, debug = require('browser/debug')('mocha:suite')
, milliseconds = require('./ms')
, utils = require('./utils')
, Hook = require('./hook');
/**
* Expose `Suite`.
*/
exports = module.exports = Suite;
/**
* Create a new `Suite` with the given `title`
* and parent `Suite`. When a suite with the
* same title is already present, that suite
* is returned to provide nicer reporter
* and more flexible meta-testing.
*
* @param {Suite} parent
* @param {String} title
* @return {Suite}
* @api public
*/
exports.create = function(parent, title){
var suite = new Suite(title, parent.ctx);
suite.parent = parent;
if (parent.pending) suite.pending = true;
title = suite.fullTitle();
parent.addSuite(suite);
return suite;
};
/**
* Initialize a new `Suite` with the given
* `title` and `ctx`.
*
* @param {String} title
* @param {Context} ctx
* @api private
*/
function Suite(title, ctx) {
this.title = title;
this.ctx = ctx;
this.suites = [];
this.tests = [];
this.pending = false;
this._beforeEach = [];
this._beforeAll = [];
this._afterEach = [];
this._afterAll = [];
this.root = !title;
this._timeout = 2000;
this._slow = 75;
this._bail = false;
}
/**
* Inherit from `EventEmitter.prototype`.
*/
function F(){};
F.prototype = EventEmitter.prototype;
Suite.prototype = new F;
Suite.prototype.constructor = Suite;
/**
* Return a clone of this `Suite`.
*
* @return {Suite}
* @api private
*/
Suite.prototype.clone = function(){
var suite = new Suite(this.title);
debug('clone');
suite.ctx = this.ctx;
suite.timeout(this.timeout());
suite.slow(this.slow());
suite.bail(this.bail());
return suite;
};
/**
* Set timeout `ms` or short-hand such as "2s".
*
* @param {Number|String} ms
* @return {Suite|Number} for chaining
* @api private
*/
Suite.prototype.timeout = function(ms){
if (0 == arguments.length) return this._timeout;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('timeout %d', ms);
this._timeout = parseInt(ms, 10);
return this;
};
/**
* Set slow `ms` or short-hand such as "2s".
*
* @param {Number|String} ms
* @return {Suite|Number} for chaining
* @api private
*/
Suite.prototype.slow = function(ms){
if (0 === arguments.length) return this._slow;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('slow %d', ms);
this._slow = ms;
return this;
};
/**
* Sets whether to bail after first error.
*
* @parma {Boolean} bail
* @return {Suite|Number} for chaining
* @api private
*/
Suite.prototype.bail = function(bail){
if (0 == arguments.length) return this._bail;
debug('bail %s', bail);
this._bail = bail;
return this;
};
/**
* Run `fn(test[, done])` before running tests.
*
* @param {Function} fn
* @return {Suite} for chaining
* @api private
*/
Suite.prototype.beforeAll = function(fn){
if (this.pending) return this;
var hook = new Hook('"before all" hook', fn);
hook.parent = this;
hook.timeout(this.timeout());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._beforeAll.push(hook);
this.emit('beforeAll', hook);
return this;
};
/**
* Run `fn(test[, done])` after running tests.
*
* @param {Function} fn
* @return {Suite} for chaining
* @api private
*/
Suite.prototype.afterAll = function(fn){
if (this.pending) return this;
var hook = new Hook('"after all" hook', fn);
hook.parent = this;
hook.timeout(this.timeout());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._afterAll.push(hook);
this.emit('afterAll', hook);
return this;
};
/**
* Run `fn(test[, done])` before each test case.
*
* @param {Function} fn
* @return {Suite} for chaining
* @api private
*/
Suite.prototype.beforeEach = function(fn){
if (this.pending) return this;
var hook = new Hook('"before each" hook', fn);
hook.parent = this;
hook.timeout(this.timeout());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._beforeEach.push(hook);
this.emit('beforeEach', hook);
return this;
};
/**
* Run `fn(test[, done])` after each test case.
*
* @param {Function} fn
* @return {Suite} for chaining
* @api private
*/
Suite.prototype.afterEach = function(fn){
if (this.pending) return this;
var hook = new Hook('"after each" hook', fn);
hook.parent = this;
hook.timeout(this.timeout());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._afterEach.push(hook);
this.emit('afterEach', hook);
return this;
};
/**
* Add a test `suite`.
*
* @param {Suite} suite
* @return {Suite} for chaining
* @api private
*/
Suite.prototype.addSuite = function(suite){
suite.parent = this;
suite.timeout(this.timeout());
suite.slow(this.slow());
suite.bail(this.bail());
this.suites.push(suite);
this.emit('suite', suite);
return this;
};
/**
* Add a `test` to this suite.
*
* @param {Test} test
* @return {Suite} for chaining
* @api private
*/
Suite.prototype.addTest = function(test){
test.parent = this;
test.timeout(this.timeout());
test.slow(this.slow());
test.ctx = this.ctx;
this.tests.push(test);
this.emit('test', test);
return this;
};
/**
* Return the full title generated by recursively
* concatenating the parent's full title.
*
* @return {String}
* @api public
*/
Suite.prototype.fullTitle = function(){
if (this.parent) {
var full = this.parent.fullTitle();
if (full) return full + ' ' + this.title;
}
return this.title;
};
/**
* Return the total number of tests.
*
* @return {Number}
* @api public
*/
Suite.prototype.total = function(){
return utils.reduce(this.suites, function(sum, suite){
return sum + suite.total();
}, 0) + this.tests.length;
};
/**
* Iterates through each suite recursively to find
* all tests. Applies a function in the format
* `fn(test)`.
*
* @param {Function} fn
* @return {Suite}
* @api private
*/
Suite.prototype.eachTest = function(fn){
utils.forEach(this.tests, fn);
utils.forEach(this.suites, function(suite){
suite.eachTest(fn);
});
return this;
};
}); // module: suite.js
require.register("test.js", function(module, exports, require){
/**
* Module dependencies.
*/
var Runnable = require('./runnable');
/**
* Expose `Test`.
*/
module.exports = Test;
/**
* Initialize a new `Test` with the given `title` and callback `fn`.
*
* @param {String} title
* @param {Function} fn
* @api private
*/
function Test(title, fn) {
Runnable.call(this, title, fn);
this.pending = !fn;
this.type = 'test';
}
/**
* Inherit from `Runnable.prototype`.
*/
function F(){};
F.prototype = Runnable.prototype;
Test.prototype = new F;
Test.prototype.constructor = Test;
}); // module: test.js
require.register("utils.js", function(module, exports, require){
/**
* Module dependencies.
*/
var fs = require('browser/fs')
, path = require('browser/path')
, join = path.join
, debug = require('browser/debug')('mocha:watch');
/**
* Ignored directories.
*/
var ignore = ['node_modules', '.git'];
/**
* Escape special characters in the given string of html.
*
* @param {String} html
* @return {String}
* @api private
*/
exports.escape = function(html){
return String(html)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(//g, '>');
};
/**
* Array#forEach (<=IE8)
*
* @param {Array} array
* @param {Function} fn
* @param {Object} scope
* @api private
*/
exports.forEach = function(arr, fn, scope){
for (var i = 0, l = arr.length; i < l; i++)
fn.call(scope, arr[i], i);
};
/**
* Array#indexOf (<=IE8)
*
* @parma {Array} arr
* @param {Object} obj to find index of
* @param {Number} start
* @api private
*/
exports.indexOf = function(arr, obj, start){
for (var i = start || 0, l = arr.length; i < l; i++) {
if (arr[i] === obj)
return i;
}
return -1;
};
/**
* Array#reduce (<=IE8)
*
* @param {Array} array
* @param {Function} fn
* @param {Object} initial value
* @api private
*/
exports.reduce = function(arr, fn, val){
var rval = val;
for (var i = 0, l = arr.length; i < l; i++) {
rval = fn(rval, arr[i], i, arr);
}
return rval;
};
/**
* Array#filter (<=IE8)
*
* @param {Array} array
* @param {Function} fn
* @api private
*/
exports.filter = function(arr, fn){
var ret = [];
for (var i = 0, l = arr.length; i < l; i++) {
var val = arr[i];
if (fn(val, i, arr)) ret.push(val);
}
return ret;
};
/**
* Object.keys (<=IE8)
*
* @param {Object} obj
* @return {Array} keys
* @api private
*/
exports.keys = Object.keys || function(obj) {
var keys = []
, has = Object.prototype.hasOwnProperty // for `window` on <=IE8
for (var key in obj) {
if (has.call(obj, key)) {
keys.push(key);
}
}
return keys;
};
/**
* Watch the given `files` for changes
* and invoke `fn(file)` on modification.
*
* @param {Array} files
* @param {Function} fn
* @api private
*/
exports.watch = function(files, fn){
var options = { interval: 100 };
files.forEach(function(file){
debug('file %s', file);
fs.watchFile(file, options, function(curr, prev){
if (prev.mtime < curr.mtime) fn(file);
});
});
};
/**
* Ignored files.
*/
function ignored(path){
return !~ignore.indexOf(path);
}
/**
* Lookup files in the given `dir`.
*
* @return {Array}
* @api private
*/
exports.files = function(dir, ret){
ret = ret || [];
fs.readdirSync(dir)
.filter(ignored)
.forEach(function(path){
path = join(dir, path);
if (fs.statSync(path).isDirectory()) {
exports.files(path, ret);
} else if (path.match(/\.(js|coffee)$/)) {
ret.push(path);
}
});
return ret;
};
/**
* Compute a slug from the given `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.slug = function(str){
return str
.toLowerCase()
.replace(/ +/g, '-')
.replace(/[^-\w]/g, '');
};
/**
* Strip the function definition from `str`,
* and re-indent for pre whitespace.
*/
exports.clean = function(str) {
str = str
.replace(/^function *\(.*\) *{/, '')
.replace(/\s+\}$/, '');
var spaces = str.match(/^\n?( *)/)[1].length
, re = new RegExp('^ {' + spaces + '}', 'gm');
str = str.replace(re, '');
return exports.trim(str);
};
/**
* Escape regular expression characters in `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.escapeRegexp = function(str){
return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
};
/**
* Trim the given `str`.
*
* @param {String} str
* @return {String}
* @api private
*/
exports.trim = function(str){
return str.replace(/^\s+|\s+$/g, '');
};
/**
* Parse the given `qs`.
*
* @param {String} qs
* @return {Object}
* @api private
*/
exports.parseQuery = function(qs){
return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){
var i = pair.indexOf('=')
, key = pair.slice(0, i)
, val = pair.slice(++i);
obj[key] = decodeURIComponent(val);
return obj;
}, {});
};
/**
* Highlight the given string of `js`.
*
* @param {String} js
* @return {String}
* @api private
*/
function highlight(js) {
return js
.replace(//g, '>')
.replace(/\/\/(.*)/gm, '//$1')
.replace(/('.*?')/gm, '$1')
.replace(/(\d+\.\d+)/gm, '$1')
.replace(/(\d+)/gm, '$1')
.replace(/\bnew *(\w+)/gm, 'new$1')
.replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1')
}
/**
* Highlight the contents of tag `name`.
*
* @param {String} name
* @api private
*/
exports.highlightTags = function(name) {
var code = document.getElementsByTagName(name);
for (var i = 0, len = code.length; i < len; ++i) {
code[i].innerHTML = highlight(code[i].innerHTML);
}
};
}); // module: utils.js
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = window.Date;
var setTimeout = window.setTimeout;
var setInterval = window.setInterval;
var clearTimeout = window.clearTimeout;
var clearInterval = window.clearInterval;
/**
* Node shims.
*
* These are meant only to allow
* mocha.js to run untouched, not
* to allow running node code in
* the browser.
*/
var process = {};
process.exit = function(status){};
process.stdout = {};
global = window;
/**
* Remove uncaughtException listener.
*/
process.removeListener = function(e){
if ('uncaughtException' == e) {
window.onerror = null;
}
};
/**
* Implements uncaughtException listener.
*/
process.on = function(e, fn){
if ('uncaughtException' == e) {
window.onerror = function(err, url, line){
fn(new Error(err + ' (' + url + ':' + line + ')'));
};
}
};
/**
* Expose mocha.
*/
var Mocha = window.Mocha = require('mocha'),
mocha = window.mocha = new Mocha({ reporter: 'html' });
var immediateQueue = []
, immediateTimeout;
function timeslice() {
var immediateStart = new Date().getTime();
while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) {
immediateQueue.shift()();
}
if (immediateQueue.length) {
immediateTimeout = setTimeout(timeslice, 0);
} else {
immediateTimeout = null;
}
}
/**
* High-performance override of Runner.immediately.
*/
Mocha.Runner.immediately = function(callback) {
immediateQueue.push(callback);
if (!immediateTimeout) {
immediateTimeout = setTimeout(timeslice, 0);
}
};
/**
* Override ui to ensure that the ui functions are initialized.
* Normally this would happen in Mocha.prototype.loadFiles.
*/
mocha.ui = function(ui){
Mocha.prototype.ui.call(this, ui);
this.suite.emit('pre-require', window, null, this);
return this;
};
/**
* Setup mocha with the given setting options.
*/
mocha.setup = function(opts){
if ('string' == typeof opts) opts = { ui: opts };
for (var opt in opts) this[opt](opts[opt]);
return this;
};
/**
* Run mocha, returning the Runner.
*/
mocha.run = function(fn){
var options = mocha.options;
mocha.globals('location');
var query = Mocha.utils.parseQuery(window.location.search || '');
if (query.grep) mocha.grep(query.grep);
if (query.invert) mocha.invert();
return Mocha.prototype.run.call(mocha, function(){
Mocha.utils.highlightTags('code');
if (fn) fn();
});
};
})(); rack-cors-1.1.1/test/unit/ 0000755 0000041 0000041 00000000000 13606604626 015375 5 ustar www-data www-data rack-cors-1.1.1/test/unit/cors_test.rb 0000644 0000041 0000041 00000041316 13606604626 017734 0 ustar www-data www-data require 'minitest/autorun'
require 'rack/test'
require 'mocha/setup'
require 'rack/cors'
require 'ostruct'
Rack::Test::Session.class_eval do
unless defined? :options
def options(uri, params = {}, env = {}, &block)
env = env_for(uri, env.merge(:method => "OPTIONS", :params => params))
process_request(uri, env, &block)
end
end
end
Rack::Test::Methods.class_eval do
def_delegator :current_session, :options
end
module MiniTest::Assertions
def assert_cors_success(response)
assert !response.headers['Access-Control-Allow-Origin'].nil?, "Expected a successful CORS response"
end
def assert_not_cors_success(response)
assert response.headers['Access-Control-Allow-Origin'].nil?, "Expected a failed CORS response"
end
end
class CaptureResult
def initialize(app, options = {})
@app = app
@result_holder = options[:holder]
end
def call(env)
response = @app.call(env)
@result_holder.cors_result = env[Rack::Cors::RACK_CORS]
return response
end
end
class FakeProxy
def initialize(app, options = {})
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers['Vary'] = %w(Origin User-Agent)
[status, headers, body]
end
end
Rack::MockResponse.infect_an_assertion :assert_cors_success, :must_render_cors_success, :only_one_argument
Rack::MockResponse.infect_an_assertion :assert_not_cors_success, :wont_render_cors_success, :only_one_argument
describe Rack::Cors do
include Rack::Test::Methods
attr_accessor :cors_result
def load_app(name, options = {})
test = self
Rack::Builder.new do
use CaptureResult, :holder => test
eval File.read(File.dirname(__FILE__) + "/#{name}.ru")
use FakeProxy if options[:proxy]
map('/') do
run proc { |env|
[200, {'Content-Type' => 'text/html'}, ['success']]
}
end
end
end
let(:app) { load_app('test') }
it 'should support simple CORS request' do
successful_cors_request
cors_result.must_be :hit
end
it "should not return CORS headers if Origin header isn't present" do
get '/'
last_response.wont_render_cors_success
cors_result.wont_be :hit
end
it 'should support OPTIONS CORS request' do
successful_cors_request '/options', :method => :options
end
it 'should support regex origins configuration' do
successful_cors_request :origin => 'http://192.168.0.1:1234'
end
it 'should support subdomain example' do
successful_cors_request :origin => 'http://subdomain.example.com'
end
it 'should support proc origins configuration' do
successful_cors_request '/proc-origin', :origin => 'http://10.10.10.10:3000'
end
it 'should support lambda origin configuration' do
successful_cors_request '/lambda-origin', :origin => 'http://10.10.10.10:3000'
end
it 'should support proc origins configuration (inverse)' do
cors_request '/proc-origin', :origin => 'http://bad.guy'
last_response.wont_render_cors_success
end
it 'should not mix up path rules across origins' do
header 'Origin', 'http://10.10.10.10:3000'
get '/' # / is configured in a separate rule block
last_response.wont_render_cors_success
end
it 'should support alternative X-Origin header' do
header 'X-Origin', 'http://localhost:3000'
get '/'
last_response.must_render_cors_success
end
it 'should support expose header configuration' do
successful_cors_request '/expose_single_header'
last_response.headers['Access-Control-Expose-Headers'].must_equal 'expose-test'
end
it 'should support expose multiple header configuration' do
successful_cors_request '/expose_multiple_headers'
last_response.headers['Access-Control-Expose-Headers'].must_equal 'expose-test-1, expose-test-2'
end
# Explanation: http://www.fastly.com/blog/best-practices-for-using-the-vary-header/
it "should add Vary header if resource matches even if Origin header isn't present" do
get '/'
last_response.wont_render_cors_success
last_response.headers['Vary'].must_equal 'Origin'
end
it "should add Vary header based on :vary option" do
successful_cors_request '/vary_test'
last_response.headers['Vary'].must_equal 'Origin, Host'
end
it "decode URL and resolve paths before resource matching" do
header 'Origin', 'http://localhost:3000'
get '/public/a/..%2F..%2Fprivate/stuff'
last_response.wont_render_cors_success
end
describe 'with array of upstream Vary headers' do
let(:app) { load_app('test', { proxy: true }) }
it 'should add to them' do
successful_cors_request '/vary_test'
last_response.headers['Vary'].must_equal 'Origin, User-Agent, Host'
end
end
it 'should add Vary header if Access-Control-Allow-Origin header was added and if it is specific' do
successful_cors_request '/', :origin => "http://192.168.0.3:8080"
last_response.headers['Access-Control-Allow-Origin'].must_equal 'http://192.168.0.3:8080'
last_response.headers['Vary'].wont_be_nil
end
it 'should add Vary header even if Access-Control-Allow-Origin header was added and it is generic (*)' do
successful_cors_request '/public_without_credentials', :origin => "http://192.168.1.3:8080"
last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
last_response.headers['Vary'].must_equal 'Origin'
end
it 'should support multi allow configurations for the same resource' do
successful_cors_request '/multi-allow-config', :origin => "http://mucho-grande.com"
last_response.headers['Access-Control-Allow-Origin'].must_equal 'http://mucho-grande.com'
last_response.headers['Vary'].must_equal 'Origin'
successful_cors_request '/multi-allow-config', :origin => "http://192.168.1.3:8080"
last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
last_response.headers['Vary'].must_equal 'Origin'
end
it "should not return CORS headers on OPTIONS request if Access-Control-Allow-Origin is not present" do
options '/get-only'
last_response.headers['Access-Control-Allow-Origin'].must_be_nil
end
it "should not apply CORS headers if it does not match conditional on resource" do
header 'Origin', 'http://192.168.0.1:1234'
get '/conditional'
last_response.wont_render_cors_success
end
it "should apply CORS headers if it does match conditional on resource" do
header 'X-OK', '1'
successful_cors_request '/conditional', :origin => 'http://192.168.0.1:1234'
end
it "should not allow everything if Origin is configured as blank string" do
cors_request '/blank-origin', origin: "http://example.net"
last_response.wont_render_cors_success
end
it "should not allow credentials for public resources" do
successful_cors_request '/public'
last_response.headers['Access-Control-Allow-Credentials'].must_be_nil
end
describe 'logging' do
it 'should not log debug messages if debug option is false' do
app = mock
app.stubs(:call).returns(200, {}, [''])
logger = mock
logger.expects(:debug).never
cors = Rack::Cors.new(app, :debug => false, :logger => logger) {}
cors.send(:debug, {}, 'testing')
end
it 'should log debug messages if debug option is true' do
app = mock
app.stubs(:call).returns(200, {}, [''])
logger = mock
logger.expects(:debug)
cors = Rack::Cors.new(app, :debug => true, :logger => logger) {}
cors.send(:debug, {}, 'testing')
end
it 'should use rack.logger if available' do
app = mock
app.stubs(:call).returns([200, {}, ['']])
logger = mock
logger.expects(:debug).at_least_once
cors = Rack::Cors.new(app, :debug => true) {}
cors.call({'rack.logger' => logger, 'HTTP_ORIGIN' => 'test.com'})
end
it 'should use logger proc' do
app = mock
app.stubs(:call).returns([200, {}, ['']])
logger = mock
logger.expects(:debug)
cors = Rack::Cors.new(app, :debug => true, :logger => proc { logger }) {}
cors.call({'HTTP_ORIGIN' => 'test.com'})
end
describe 'with Rails setup' do
after do
::Rails.logger = nil if defined?(::Rails)
end
it 'should use Rails.logger if available' do
app = mock
app.stubs(:call).returns([200, {}, ['']])
logger = mock
logger.expects(:debug)
::Rails = OpenStruct.new(:logger => logger)
cors = Rack::Cors.new(app, :debug => true) {}
cors.call({'HTTP_ORIGIN' => 'test.com'})
end
end
it 'should use Logger if none is set' do
app = mock
app.stubs(:call).returns([200, {}, ['']])
logger = mock
Logger.expects(:new).returns(logger)
logger.expects(:tap).returns(logger)
logger.expects(:debug)
cors = Rack::Cors.new(app, :debug => true) {}
cors.call({'HTTP_ORIGIN' => 'test.com'})
end
end
describe 'preflight requests' do
it 'should fail if origin is invalid' do
preflight_request('http://allyourdataarebelongtous.com', '/')
last_response.wont_render_cors_success
cors_result.wont_be :hit
cors_result.must_be :preflight
end
it 'should fail if Access-Control-Request-Method is not allowed' do
preflight_request('http://localhost:3000', '/get-only', :method => :post)
last_response.wont_render_cors_success
cors_result.miss_reason.must_equal Rack::Cors::Result::MISS_DENY_METHOD
cors_result.wont_be :hit
cors_result.must_be :preflight
end
it 'should fail if header is not allowed' do
preflight_request('http://localhost:3000', '/single_header', :headers => 'Fooey')
last_response.wont_render_cors_success
cors_result.miss_reason.must_equal Rack::Cors::Result::MISS_DENY_HEADER
cors_result.wont_be :hit
cors_result.must_be :preflight
end
it 'should allow any header if headers = :any' do
preflight_request('http://localhost:3000', '/', :headers => 'Fooey')
last_response.must_render_cors_success
end
it 'should allow any method if methods = :any' do
preflight_request('http://localhost:3000', '/', :methods => :any)
last_response.must_render_cors_success
end
it 'allows PATCH method' do
preflight_request('http://localhost:3000', '/', :methods => [ :patch ])
last_response.must_render_cors_success
end
it 'should allow header case insensitive match' do
preflight_request('http://localhost:3000', '/single_header', :headers => 'X-Domain-Token')
last_response.must_render_cors_success
end
it 'should allow multiple headers match' do
# Webkit style
preflight_request('http://localhost:3000', '/two_headers', :headers => 'X-Requested-With, X-Domain-Token')
last_response.must_render_cors_success
# Gecko style
preflight_request('http://localhost:3000', '/two_headers', :headers => 'x-requested-with,x-domain-token')
last_response.must_render_cors_success
end
it "should allow '*' origins to allow any origin" do
preflight_request('http://locohost:3000', '/public')
last_response.must_render_cors_success
last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
end
it "should allow '//' resource if match pattern is //*" do
preflight_request('http://localhost:3000', '/wildcard/')
last_response.must_render_cors_success
last_response.headers['Access-Control-Allow-Origin'].wont_equal nil
end
it "should allow '/' resource if match pattern is //*" do
preflight_request('http://localhost:3000', '/wildcard')
last_response.must_render_cors_success
last_response.headers['Access-Control-Allow-Origin'].wont_equal nil
end
it "should allow '*' origin to allow any origin, and set '*' if no credentials required" do
preflight_request('http://locohost:3000', '/public_without_credentials')
last_response.must_render_cors_success
last_response.headers['Access-Control-Allow-Origin'].must_equal '*'
end
it 'should return "file://" as header with "file://" as origin' do
preflight_request('file://', '/')
last_response.must_render_cors_success
last_response.headers['Access-Control-Allow-Origin'].must_equal 'file://'
end
describe '' do
let(:app) do
test = self
Rack::Builder.new do
use CaptureResult, holder: test
use Rack::Cors, debug: true, logger: Logger.new(StringIO.new) do
allow do
origins '*'
resource '/', :methods => :post
end
end
map('/') do
run ->(env) { [500, {}, ['FAIL!']] }
end
end
end
it "should not send failed preflight requests thru the app" do
preflight_request('http://localhost', '/', :method => :unsupported)
last_response.wont_render_cors_success
last_response.status.must_equal 200
cors_result.must_be :preflight
cors_result.wont_be :hit
cors_result.miss_reason.must_equal Rack::Cors::Result::MISS_DENY_METHOD
end
end
end
describe "with insecure configuration" do
let(:app) { load_app('insecure') }
it "should raise an error" do
proc { cors_request '/public' }.must_raise Rack::Cors::Resource::CorsMisconfigurationError
end
end
describe "with non HTTP config" do
let(:app) { load_app("non_http") }
it 'should support non http/https origins' do
successful_cors_request '/public', origin: 'content://com.company.app'
end
end
describe 'Rack::Lint' do
def app
@app ||= Rack::Builder.new do
use Rack::Cors
use Rack::Lint
run ->(env) { [200, {'Content-Type' => 'text/html'}, ['hello']] }
end
end
it 'is lint-compliant with non-CORS request' do
get '/'
last_response.status.must_equal 200
end
end
describe 'with app overriding CORS header' do
let(:app) do
Rack::Builder.new do
use Rack::Cors, debug: true, logger: Logger.new(StringIO.new) do
allow do
origins '*'
resource '/'
end
end
map('/') do
run ->(env) { [200, {'Access-Control-Allow-Origin' => 'http://foo.net'}, ['success']] }
end
end
end
it "should return app header" do
successful_cors_request origin: "http://example.net"
last_response.headers['Access-Control-Allow-Origin'].must_equal "http://foo.net"
end
it "should return original headers if in debug" do
successful_cors_request origin: "http://example.net"
last_response.headers['X-Rack-CORS-Original-Access-Control-Allow-Origin'].must_equal "*"
end
end
describe 'with headers set to nil' do
let(:app) do
Rack::Builder.new do
use Rack::Cors do
allow do
origins '*'
resource '/', headers: nil
end
end
map('/') do
run ->(env) { [200, {'Content-Type' => 'text/html'}, ['hello']] }
end
end
end
it 'should succeed with CORS simple headers' do
preflight_request('http://localhost:3000', '/', :headers => 'Accept')
last_response.must_render_cors_success
end
end
describe 'with custom allowed headers' do
let(:app) do
Rack::Builder.new do
use Rack::Cors do
allow do
origins '*'
resource '/', headers: []
end
end
map('/') do
run ->(env) { [200, {'Content-Type' => 'text/html'}, ['hello']] }
end
end
end
it 'should succeed with CORS simple headers' do
preflight_request('http://localhost:3000', '/', :headers => 'Accept')
last_response.must_render_cors_success
preflight_request('http://localhost:3000', '/', :headers => 'Accept-Language')
last_response.must_render_cors_success
preflight_request('http://localhost:3000', '/', :headers => 'Content-Type')
last_response.must_render_cors_success
preflight_request('http://localhost:3000', '/', :headers => 'Content-Language')
last_response.must_render_cors_success
end
end
protected
def cors_request(*args)
path = args.first.is_a?(String) ? args.first : '/'
opts = { :method => :get, :origin => 'http://localhost:3000' }
opts.merge! args.last if args.last.is_a?(Hash)
header 'Origin', opts[:origin]
current_session.__send__ opts[:method], path, {}, test: self
end
def successful_cors_request(*args)
cors_request(*args)
last_response.must_render_cors_success
end
def preflight_request(origin, path, opts = {})
header 'Origin', origin
unless opts.key?(:method) && opts[:method].nil?
header 'Access-Control-Request-Method', opts[:method] ? opts[:method].to_s.upcase : 'GET'
end
if opts[:headers]
header 'Access-Control-Request-Headers', opts[:headers]
end
options path
end
end
rack-cors-1.1.1/test/unit/dsl_test.rb 0000644 0000041 0000041 00000004671 13606604626 017553 0 ustar www-data www-data require 'rubygems'
require 'minitest/autorun'
require 'rack/cors'
describe Rack::Cors, 'DSL' do
it 'should support explicit config object dsl mode' do
cors = Rack::Cors.new(Proc.new {}) do |cfg|
cfg.allow do |allow|
allow.origins 'localhost:3000', '127.0.0.1:3000' do |source,env|
source == "http://10.10.10.10:3000" &&
env["USER_AGENT"] == "test-agent"
end
allow.resource '/get-only', :methods => :get
allow.resource '/', :headers => :any
end
end
resources = cors.send :all_resources
resources.length.must_equal 1
resources.first.allow_origin?('http://localhost:3000').must_equal true
resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "test-agent" }).must_equal true
resources.first.allow_origin?('http://10.10.10.10:3001',{"USER_AGENT" => "test-agent" }).wont_equal true
resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "other-agent"}).wont_equal true
end
it 'should support implicit config object dsl mode' do
cors = Rack::Cors.new(Proc.new {}) do
allow do
origins 'localhost:3000', '127.0.0.1:3000' do |source,env|
source == "http://10.10.10.10:3000" &&
env["USER_AGENT"] == "test-agent"
end
resource '/get-only', :methods => :get
resource '/', :headers => :any
end
end
resources = cors.send :all_resources
resources.length.must_equal 1
resources.first.allow_origin?('http://localhost:3000').must_equal true
resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "test-agent" }).must_equal true
resources.first.allow_origin?('http://10.10.10.10:3001',{"USER_AGENT" => "test-agent" }).wont_equal true
resources.first.allow_origin?('http://10.10.10.10:3000',{"USER_AGENT" => "other-agent"}).wont_equal true
end
it 'should support "file://" origin' do
cors = Rack::Cors.new(Proc.new {}) do
allow do
origins 'file://'
resource '/', :headers => :any
end
end
resources = cors.send :all_resources
resources.first.allow_origin?('file://').must_equal true
end
it 'should default credentials option to false' do
cors = Rack::Cors.new(Proc.new {}) do
allow do
origins 'example.net'
resource '/', :headers => :any
end
end
resources = cors.send :all_resources
resources.first.resources.first.credentials.must_equal false
end
end
rack-cors-1.1.1/test/unit/non_http.ru 0000644 0000041 0000041 00000000161 13606604626 017574 0 ustar www-data www-data require 'rack/cors'
use Rack::Cors do
allow do
origins 'com.company.app'
resource '/public'
end
end
rack-cors-1.1.1/test/unit/test.ru 0000644 0000041 0000041 00000003404 13606604626 016725 0 ustar www-data www-data require 'rack/cors'
#use Rack::Cors, :debug => true, :logger => ::Logger.new(STDOUT) do
use Rack::Lint
use Rack::Cors do
allow do
origins 'localhost:3000',
'127.0.0.1:3000',
/http:\/\/192\.168\.0\.\d{1,3}(:\d+)?/,
'file://',
/http:\/\/(.*?)\.example\.com/
resource '/get-only', :methods => :get
resource '/', :headers => :any, :methods => :any
resource '/options', :methods => :options
resource '/single_header', :headers => 'x-domain-token'
resource '/two_headers', :headers => %w{x-domain-token x-requested-with}
resource '/expose_single_header', :expose => 'expose-test'
resource '/expose_multiple_headers', :expose => %w{expose-test-1 expose-test-2}
resource '/conditional', :methods => :get, :if => proc { |env| !!env['HTTP_X_OK'] }
resource '/vary_test', :methods => :get, :vary => %w{ Origin Host }
resource '/patch_test', :methods => :patch
resource '/wildcard/*', :methods => :any
# resource '/file/at/*',
# :methods => [:get, :post, :put, :delete],
# :headers => :any,
# :max_age => 0
end
allow do
origins do |source,env|
source.end_with?("10.10.10.10:3000")
end
resource '/proc-origin'
end
allow do
origins -> (source, env) { source.end_with?("10.10.10.10:3000") }
resource '/lambda-origin'
end
allow do
origins '*'
resource '/public'
resource '/public/*'
resource '/public_without_credentials', :credentials => false
end
allow do
origins 'mucho-grande.com'
resource '/multi-allow-config', :max_age => 600
end
allow do
origins '*'
resource '/multi-allow-config', :max_age => 300, :credentials => false
end
allow do
origins ''
resource '/blank-origin'
end
end
rack-cors-1.1.1/test/unit/insecure.ru 0000644 0000041 0000041 00000000166 13606604626 017565 0 ustar www-data www-data require 'rack/cors'
use Rack::Cors do
allow do
origins '*'
resource '/public', credentials: true
end
end
rack-cors-1.1.1/README.md 0000644 0000041 0000041 00000015075 13606604626 014726 0 ustar www-data www-data # Rack CORS Middleware [](https://travis-ci.org/cyu/rack-cors)
`Rack::Cors` provides support for Cross-Origin Resource Sharing (CORS) for Rack compatible web applications.
The [CORS spec](http://www.w3.org/TR/cors/) allows web applications to make cross domain AJAX calls without using workarounds such as JSONP. See [Cross-domain Ajax with Cross-Origin Resource Sharing](http://www.nczonline.net/blog/2010/05/25/cross-domain-ajax-with-cross-origin-resource-sharing/)
## Installation
Install the gem:
`gem install rack-cors`
Or in your Gemfile:
```ruby
gem 'rack-cors'
```
## Configuration
### Rails Configuration
Put something like the code below in `config/application.rb` of your Rails application. For example, this will allow GET, POST or OPTIONS requests from any origin on any resource.
```ruby
module YourApp
class Application < Rails::Application
# ...
# Rails 5
config.middleware.insert_before 0, Rack::Cors do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :options]
end
end
# Rails 3/4
config.middleware.insert_before 0, "Rack::Cors" do
allow do
origins '*'
resource '*', headers: :any, methods: [:get, :post, :options]
end
end
end
end
```
We use `insert_before` to make sure `Rack::Cors` runs at the beginning of the stack to make sure it isn't interfered with by other middleware (see `Rack::Cache` note in **Common Gotchas** section). Check out the [rails 4 example](https://github.com/cyu/rack-cors/tree/master/examples/rails4) and [rails 3 example](https://github.com/cyu/rack-cors/tree/master/examples/rails3).
See The [Rails Guide to Rack](http://guides.rubyonrails.org/rails_on_rack.html) for more details on rack middlewares or watch the [railscast](http://railscasts.com/episodes/151-rack-middleware).
### Rack Configuration
NOTE: If you're running Rails, updating in `config/application.rb` should be enough. There is no need to update `config.ru` as well.
In `config.ru`, configure `Rack::Cors` by passing a block to the `use` command:
```ruby
use Rack::Cors do
allow do
origins 'localhost:3000', '127.0.0.1:3000',
/\Ahttp:\/\/192\.168\.0\.\d{1,3}(:\d+)?\z/
# regular expressions can be used here
resource '/file/list_all/', :headers => 'x-domain-token'
resource '/file/at/*',
methods: [:get, :post, :delete, :put, :patch, :options, :head],
headers: 'x-domain-token',
expose: ['Some-Custom-Response-Header'],
max_age: 600
# headers to expose
end
allow do
origins '*'
resource '/public/*', headers: :any, methods: :get
# Only allow a request for a specific host
resource '/api/v1/*',
headers: :any,
methods: :get,
if: proc { |env| env['HTTP_HOST'] == 'api.example.com' }
end
end
```
### Configuration Reference
#### Middleware Options
* **debug** (boolean): Enables debug logging and `X-Rack-CORS` HTTP headers for debugging.
* **logger** (Object or Proc): Specify the logger to log to. If a proc is provided, it will be called when a logger is needed. This is helpful in cases where the logger is initialized after `Rack::Cors` is initially configured, like `Rails.logger`.
#### Origin
Origins can be specified as a string, a regular expression, or as '\*' to allow all origins.
**\*SECURITY NOTE:** Be careful when using regular expressions to not accidentally be too inclusive. For example, the expression `/https:\/\/example\.com/` will match the domain *example.com.randomdomainname.co.uk*. It is recommended that any regular expression be enclosed with start & end string anchors (`\A\z`).
Additionally, origins can be specified dynamically via a block of the following form:
```ruby
origins { |source, env| true || false }
```
A Resource path can be specified as exact string match (`/path/to/file.txt`) or with a '\*' wildcard (`/all/files/in/*`). To include all of a directory's files and the files in its subdirectories, use this form: `/assets/**/*`. A resource can take the following options:
* **methods** (string or array or `:any`): The HTTP methods allowed for the resource.
* **headers** (string or array or `:any`): The HTTP headers that will be allowed in the CORS resource request. Use `:any` to allow for any headers in the actual request.
* **expose** (string or array): The HTTP headers in the resource response can be exposed to the client.
* **credentials** (boolean, default: `false`): Sets the `Access-Control-Allow-Credentials` response header. **Note:** If a wildcard (`*`) origin is specified, this option cannot be set to `true`. Read this [security article](http://web-in-security.blogspot.de/2017/07/cors-misconfigurations-on-large-scale.html) for more information.
* **max_age** (number): Sets the `Access-Control-Max-Age` response header.
* **if** (Proc): If the result of the proc is true, will process the request as a valid CORS request.
* **vary** (string or array): A list of HTTP headers to add to the 'Vary' header.
## Common Gotchas
Incorrect positioning of `Rack::Cors` in the middleware stack can produce unexpected results. The Rails example above will put it above all middleware which should cover most issues.
Here are some common cases:
* **Serving static files.** Insert this middleware before `ActionDispatch::Static` so that static files are served with the proper CORS headers (see note below for a caveat). **NOTE:** that this might not work in production environments as static files are usually served from the web server (Nginx, Apache) and not the Rails container.
* **Caching in the middleware.** Insert this middleware before `Rack::Cache` so that the proper CORS headers are written and not cached ones.
* **Authentication via Warden** Warden will return immediately if a resource that requires authentication is accessed without authentication. If `Warden::Manager`is in the stack before `Rack::Cors`, it will return without the correct CORS headers being applied, resulting in a failed CORS request. Be sure to insert this middleware before 'Warden::Manager`.
To determine where to put the CORS middleware in the Rack stack, run the following command:
```bash
bundle exec rake middleware
```
In many cases, the Rack stack will be different running in production environments. For example, the `ActionDispatch::Static` middleware will not be part of the stack if `config.serve_static_assets = false`. You can run the following command to see what your middleware stack looks like in production:
```bash
RAILS_ENV=production bundle exec rake middleware
```
rack-cors-1.1.1/CHANGELOG.md 0000644 0000041 0000041 00000005174 13606604626 015257 0 ustar www-data www-data # Change Log
All notable changes to this project will be documented in this file.
## 1.1.1 - 2019-12-29
### Changed
- Allow //* to match // and / paths
## 1.1.0 - 2019-11-19
### Changed
- Use Rack::Utils.escape_path instead of Rack::Utils.escape
- Require Rack 2.0 for escape_path method
- Don't try to clean path if invalid.
- Return 400 (Bad Request) on preflights with invalid path
## 1.0.6 - 2019-11-14
### Changed
- Use Rack::Utils.escape to make compat with Rack 1.6.0
## 1.0.5 - 2019-11-14
### Changed
- Update Gem spec to require rack >= 1.6.0
## 1.0.4 - 2019-11-13
### Security
- Escape and resolve path before evaluating resource rules (thanks to Colby Morgan)
## 1.0.3 - 2019-03-24
### Changed
- Don't send 'Content-Type' header with pre-flight requests
- Allow ruby array for vary header config
## 1.0.2 - 2017-10-22
### Fixed
- Automatically allow simple headers when headers are set
## 1.0.1 - 2017-07-18
### Fixed
- Allow lambda origin configuration
## 1.0.0 - 2017-07-15
### Security
- Don't implicitly accept 'null' origins when 'file://' is specified
(https://github.com/cyu/rack-cors/pull/134)
- Ignore '' origins (https://github.com/cyu/rack-cors/issues/139)
- Default credentials option on resources to false
(https://github.com/cyu/rack-cors/issues/95)
- Don't allow credentials option to be true if '*' is specified is origin
(https://github.com/cyu/rack-cors/pull/142)
- Don't reflect Origin header when '*' is specified as origin
(https://github.com/cyu/rack-cors/pull/142)
### Fixed
- Don't respond immediately on non-matching preflight requests instead of
sending them through the app (https://github.com/cyu/rack-cors/pull/106)
## 0.4.1 - 2017-02-01
### Fixed
- Return miss result in X-Rack-CORS instead of incorrectly returning
preflight-hit
## 0.4.0 - 2015-04-15
### Changed
- Don't set HTTP_ORIGIN with HTTP_X_ORIGIN if nil
### Added
- Calculate vary headers for non-CORS resources
- Support custom vary headers for resource
- Support :if option for resource
- Support :any as a possible value for :methods option
### Fixed
- Don't symbolize incoming HTTP request methods
## 0.3.1 - 2014-12-27
### Changed
- Changed the env key to rack.cors to avoid Rack::Lint warnings
## 0.3.0 - 2014-10-19
### Added
- Added support for defining a logger with a Proc
- Return a X-Rack-CORS header when in debug mode detailing how Rack::Cors
processed a request
- Added support for non HTTP/HTTPS origins when just a domain is specified
### Changed
- Changed the log level of the fallback logger to DEBUG
- Print warning when attempting to use :any as an allowed method
- Treat incoming `Origin: null` headers as file://
rack-cors-1.1.1/Rakefile 0000644 0000041 0000041 00000000723 13606604626 015106 0 ustar www-data www-data require "bundler/gem_tasks"
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.libs << 'lib' << 'test'
test.pattern = 'test/**/*_test.rb'
test.verbose = true
end
task :default => :test
require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "rack-cors #{version}"
rdoc.rdoc_files.include('README*')
rdoc.rdoc_files.include('lib/**/*.rb')
end
rack-cors-1.1.1/lib/ 0000755 0000041 0000041 00000000000 13606604626 014205 5 ustar www-data www-data rack-cors-1.1.1/lib/rack/ 0000755 0000041 0000041 00000000000 13606604626 015125 5 ustar www-data www-data rack-cors-1.1.1/lib/rack/cors.rb 0000644 0000041 0000041 00000032070 13606604626 016422 0 ustar www-data www-data require 'logger'
module Rack
class Cors
HTTP_ORIGIN = 'HTTP_ORIGIN'.freeze
HTTP_X_ORIGIN = 'HTTP_X_ORIGIN'.freeze
HTTP_ACCESS_CONTROL_REQUEST_METHOD = 'HTTP_ACCESS_CONTROL_REQUEST_METHOD'.freeze
HTTP_ACCESS_CONTROL_REQUEST_HEADERS = 'HTTP_ACCESS_CONTROL_REQUEST_HEADERS'.freeze
PATH_INFO = 'PATH_INFO'.freeze
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
RACK_LOGGER = 'rack.logger'.freeze
RACK_CORS =
# retaining the old key for backwards compatibility
ENV_KEY = 'rack.cors'.freeze
OPTIONS = 'OPTIONS'.freeze
VARY = 'Vary'.freeze
DEFAULT_VARY_HEADERS = ['Origin'].freeze
# All CORS routes need to accept CORS simple headers at all times
# {https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers}
CORS_SIMPLE_HEADERS = ['accept', 'accept-language', 'content-language', 'content-type'].freeze
def initialize(app, opts={}, &block)
@app = app
@debug_mode = !!opts[:debug]
@logger = @logger_proc = nil
if logger = opts[:logger]
if logger.respond_to? :call
@logger_proc = opts[:logger]
else
@logger = logger
end
end
if block_given?
if block.arity == 1
block.call(self)
else
instance_eval(&block)
end
end
end
def debug?
@debug_mode
end
def allow(&block)
all_resources << (resources = Resources.new)
if block.arity == 1
block.call(resources)
else
resources.instance_eval(&block)
end
end
def call(env)
env[HTTP_ORIGIN] ||= env[HTTP_X_ORIGIN] if env[HTTP_X_ORIGIN]
path = evaluate_path(env)
add_headers = nil
if env[HTTP_ORIGIN]
debug(env) do
[ 'Incoming Headers:',
" Origin: #{env[HTTP_ORIGIN]}",
" Path-Info: #{path}",
" Access-Control-Request-Method: #{env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]}",
" Access-Control-Request-Headers: #{env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]}"
].join("\n")
end
if env[REQUEST_METHOD] == OPTIONS && env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
return [400, {}, []] unless Rack::Utils.valid_path?(path)
headers = process_preflight(env, path)
debug(env) do
"Preflight Headers:\n" +
headers.collect{|kv| " #{kv.join(': ')}"}.join("\n")
end
return [200, headers, []]
else
add_headers = process_cors(env, path)
end
else
Result.miss(env, Result::MISS_NO_ORIGIN)
end
# This call must be done BEFORE calling the app because for some reason
# env[PATH_INFO] gets changed after that and it won't match. (At least
# in rails 4.1.6)
vary_resource = resource_for_path(path)
status, headers, body = @app.call env
if add_headers
headers = add_headers.merge(headers)
debug(env) do
add_headers.each_pair do |key, value|
if headers.has_key?(key)
headers["X-Rack-CORS-Original-#{key}"] = value
end
end
end
end
# Vary header should ALWAYS mention Origin if there's ANY chance for the
# response to be different depending on the Origin header value.
# Better explained here: http://www.fastly.com/blog/best-practices-for-using-the-vary-header/
if vary_resource
vary = headers[VARY]
cors_vary_headers = if vary_resource.vary_headers && vary_resource.vary_headers.any?
vary_resource.vary_headers
else
DEFAULT_VARY_HEADERS
end
headers[VARY] = ((vary ? ([vary].flatten.map { |v| v.split(/,\s*/) }.flatten) : []) + cors_vary_headers).uniq.join(', ')
end
if debug? && result = env[RACK_CORS]
result.append_header(headers)
end
[status, headers, body]
end
protected
def debug(env, message = nil, &block)
(@logger || select_logger(env)).debug(message, &block) if debug?
end
def select_logger(env)
@logger = if @logger_proc
logger_proc = @logger_proc
@logger_proc = nil
logger_proc.call
elsif defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
Rails.logger
elsif env[RACK_LOGGER]
env[RACK_LOGGER]
else
::Logger.new(STDOUT).tap { |logger| logger.level = ::Logger::Severity::DEBUG }
end
end
def evaluate_path(env)
path = env[PATH_INFO]
if path
path = Rack::Utils.unescape_path(path)
if Rack::Utils.valid_path?(path)
path = Rack::Utils.clean_path_info(path)
end
end
path
end
def all_resources
@all_resources ||= []
end
def process_preflight(env, path)
result = Result.preflight(env)
resource, error = match_resource(path, env)
unless resource
result.miss(error)
return {}
end
return resource.process_preflight(env, result)
end
def process_cors(env, path)
resource, error = match_resource(path, env)
if resource
Result.hit(env)
cors = resource.to_headers(env)
cors
else
Result.miss(env, error)
nil
end
end
def resource_for_path(path_info)
all_resources.each do |r|
if found = r.resource_for_path(path_info)
return found
end
end
nil
end
def match_resource(path, env)
origin = env[HTTP_ORIGIN]
origin_matched = false
all_resources.each do |r|
if r.allow_origin?(origin, env)
origin_matched = true
if found = r.match_resource(path, env)
return [found, nil]
end
end
end
[nil, origin_matched ? Result::MISS_NO_PATH : Result::MISS_NO_ORIGIN]
end
class Result
HEADER_KEY = 'X-Rack-CORS'.freeze
MISS_NO_ORIGIN = 'no-origin'.freeze
MISS_NO_PATH = 'no-path'.freeze
MISS_NO_METHOD = 'no-method'.freeze
MISS_DENY_METHOD = 'deny-method'.freeze
MISS_DENY_HEADER = 'deny-header'.freeze
attr_accessor :preflight, :hit, :miss_reason
def hit?
!!hit
end
def preflight?
!!preflight
end
def miss(reason)
self.hit = false
self.miss_reason = reason
end
def self.hit(env)
r = Result.new
r.preflight = false
r.hit = true
env[RACK_CORS] = r
end
def self.miss(env, reason)
r = Result.new
r.preflight = false
r.hit = false
r.miss_reason = reason
env[RACK_CORS] = r
end
def self.preflight(env)
r = Result.new
r.preflight = true
env[RACK_CORS] = r
end
def append_header(headers)
headers[HEADER_KEY] = if hit?
preflight? ? 'preflight-hit' : 'hit'
else
[
(preflight? ? 'preflight-miss' : 'miss'),
miss_reason
].join('; ')
end
end
end
class Resources
attr_reader :resources
def initialize
@origins = []
@resources = []
@public_resources = false
end
def origins(*args, &blk)
@origins = args.flatten.reject{ |s| s == '' }.map do |n|
case n
when Proc,
Regexp,
/^https?:\/\//,
'file://' then n
when '*' then @public_resources = true; n
else Regexp.compile("^[a-z][a-z0-9.+-]*:\\\/\\\/#{Regexp.quote(n)}$")
end
end.flatten
@origins.push(blk) if blk
end
def resource(path, opts={})
@resources << Resource.new(public_resources?, path, opts)
end
def public_resources?
@public_resources
end
def allow_origin?(source,env = {})
return true if public_resources?
return !! @origins.detect do |origin|
if origin.is_a?(Proc)
origin.call(source,env)
else
origin === source
end
end
end
def match_resource(path, env)
@resources.detect { |r| r.match?(path, env) }
end
def resource_for_path(path)
@resources.detect { |r| r.matches_path?(path) }
end
end
class Resource
class CorsMisconfigurationError < StandardError
def message
"Allowing credentials for wildcard origins is insecure."\
" Please specify more restrictive origins or set 'credentials' to false in your CORS configuration."
end
end
attr_accessor :path, :methods, :headers, :expose, :max_age, :credentials, :pattern, :if_proc, :vary_headers
def initialize(public_resource, path, opts={})
raise CorsMisconfigurationError if public_resource && opts[:credentials] == true
self.path = path
self.credentials = public_resource ? false : (opts[:credentials] == true)
self.max_age = opts[:max_age] || 7200
self.pattern = compile(path)
self.if_proc = opts[:if]
self.vary_headers = opts[:vary] && [opts[:vary]].flatten
@public_resource = public_resource
self.headers = case opts[:headers]
when :any then :any
when nil then nil
else
[opts[:headers]].flatten.collect{|h| h.downcase}
end
self.methods = case opts[:methods]
when :any then [:get, :head, :post, :put, :patch, :delete, :options]
else
ensure_enum(opts[:methods]) || [:get]
end.map{|e| e.to_s }
self.expose = opts[:expose] ? [opts[:expose]].flatten : nil
end
def matches_path?(path)
pattern =~ path
end
def match?(path, env)
matches_path?(path) && (if_proc.nil? || if_proc.call(env))
end
def process_preflight(env, result)
headers = {}
request_method = env[HTTP_ACCESS_CONTROL_REQUEST_METHOD]
if request_method.nil?
result.miss(Result::MISS_NO_METHOD) and return headers
end
if !methods.include?(request_method.downcase)
result.miss(Result::MISS_DENY_METHOD) and return headers
end
request_headers = env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
if request_headers && !allow_headers?(request_headers)
result.miss(Result::MISS_DENY_HEADER) and return headers
end
result.hit = true
headers.merge(to_preflight_headers(env))
end
def to_headers(env)
h = {
'Access-Control-Allow-Origin' => origin_for_response_header(env[HTTP_ORIGIN]),
'Access-Control-Allow-Methods' => methods.collect{|m| m.to_s.upcase}.join(', '),
'Access-Control-Expose-Headers' => expose.nil? ? '' : expose.join(', '),
'Access-Control-Max-Age' => max_age.to_s }
h['Access-Control-Allow-Credentials'] = 'true' if credentials
h
end
protected
def public_resource?
@public_resource
end
def origin_for_response_header(origin)
return '*' if public_resource?
origin
end
def to_preflight_headers(env)
h = to_headers(env)
if env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS]
h.merge!('Access-Control-Allow-Headers' => env[HTTP_ACCESS_CONTROL_REQUEST_HEADERS])
end
h
end
def allow_headers?(request_headers)
headers = self.headers || []
if headers == :any
return true
end
request_headers = request_headers.split(/,\s*/) if request_headers.kind_of?(String)
request_headers.all? do |header|
header = header.downcase
CORS_SIMPLE_HEADERS.include?(header) || headers.include?(header)
end
end
def ensure_enum(v)
return nil if v.nil?
[v].flatten
end
def compile(path)
if path.respond_to? :to_str
special_chars = %w{. + ( )}
pattern =
path.to_str.gsub(/((:\w+)|\/\*|[\*#{special_chars.join}])/) do |match|
case match
when "/*"
"\\/?(.*?)"
when "*"
"(.*?)"
when *special_chars
Regexp.escape(match)
else
"([^/?]+)"
end
end
/^#{pattern}$/
elsif path.respond_to? :match
path
else
raise TypeError, path
end
end
end
end
end
rack-cors-1.1.1/lib/rack/cors/ 0000755 0000041 0000041 00000000000 13606604626 016073 5 ustar www-data www-data rack-cors-1.1.1/lib/rack/cors/version.rb 0000644 0000041 0000041 00000000071 13606604626 020103 0 ustar www-data www-data module Rack
class Cors
VERSION = "1.1.1"
end
end
rack-cors-1.1.1/Gemfile 0000644 0000041 0000041 00000000174 13606604626 014734 0 ustar www-data www-data source 'https://rubygems.org'
# Specify your gem's dependencies in rack-cors.gemspec
gemspec
gem 'pry-byebug', '~> 3.6.0'
rack-cors-1.1.1/rack-cors.gemspec 0000644 0000041 0000041 00000002367 13606604626 016700 0 ustar www-data www-data # coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'rack/cors/version'
Gem::Specification.new do |spec|
spec.name = "rack-cors"
spec.version = Rack::Cors::VERSION
spec.authors = ["Calvin Yu"]
spec.email = ["me@sourcebender.com"]
spec.description = %q{Middleware that will make Rack-based apps CORS compatible. Fork the project here: https://github.com/cyu/rack-cors}
spec.summary = %q{Middleware for enabling Cross-Origin Resource Sharing in Rack apps}
spec.homepage = "https://github.com/cyu/rack-cors"
spec.license = "MIT"
spec.files = `git ls-files`.split($/).reject { |f| f == '.gitignore' or f =~ /^examples/ }
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
spec.require_paths = ["lib"]
spec.add_dependency "rack", ">= 2.0.0"
spec.add_development_dependency "bundler", ">= 1.16.0", '< 3'
spec.add_development_dependency "rake", "~> 12.3.0"
spec.add_development_dependency "minitest", "~> 5.11.0"
spec.add_development_dependency "mocha", "~> 1.6.0"
spec.add_development_dependency "rack-test", "~> 1.1.0"
end
rack-cors-1.1.1/LICENSE.txt 0000644 0000041 0000041 00000002052 13606604626 015261 0 ustar www-data www-data Copyright (c) 2013 Calvin Yu
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.