Fixing iron:router issue with latest Chrome version

This commit is contained in:
Erik Ziegler 2016-06-07 19:32:30 +02:00
parent 3734b5f31a
commit 6f23c7163a
20 changed files with 1074 additions and 4 deletions

View File

@ -26,8 +26,8 @@ clinical:hipaa-audit-log@2.4.2
clinical:hipaa-logger@1.0.1
clinical:router@2.0.17
clinical:router-location@2.0.14
clinical:router-middleware-stack@2.0.13
clinical:router-url@2.0.15
clinical:router-middleware-stack@2.1.2
clinical:router-url@2.1.0
clinical:theming@0.4.10
coffeescript@1.0.11
cornerstone@0.0.1

View File

@ -14,8 +14,8 @@ callback-hook@1.0.4
check@1.1.0
clinical:router@2.0.17
clinical:router-location@2.0.14
clinical:router-middleware-stack@2.0.13
clinical:router-url@2.0.15
clinical:router-middleware-stack@2.1.2
clinical:router-url@2.1.0
coffeescript@1.0.11
cornerstone@0.0.1
ddp@1.2.2

View File

@ -0,0 +1,6 @@
.DS_Store
.npm
.build*
smart.lock
versions.json
.versions

View File

@ -0,0 +1,3 @@
v1.0.11 / 2015-10-09
==================
* Support Meteor 1.2

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2014 EventedMind <chris@eventedmind.com>
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.

View File

@ -0,0 +1,18 @@
Clinical Router MiddlewareStack
==============================================================================
Client and server middleware support inspired by Connect.
===============================
#### Installation
This package is a dependency of ``clinical:router``. Just install the router package, and you'll get this included.
````bash
meteor add clinical:router
````
===============================
#### Licensing
![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)

View File

@ -0,0 +1,57 @@
## Customize the test machine
machine:
# Timezone
timezone:
America/Los_Angeles # Set the timezone
# Add some environment variables
environment:
CIRCLE_ENV: test
CXX: g++-4.8
DISPLAY: :99.0
NPM_PREFIX: /home/ubuntu/nvm/v0.10.33
#general:
# build_dir: helloworld
## Customize dependencies
dependencies:
cache_directories:
- ~/.meteor # relative to the user's home directory
- ~/nvm/v0.10.33/lib/node_modules/starrynight
- ~/nvm/v0.10.33/bin/starrynight
pre:
# Install Starrynight unless it is cached
- if [ ! -e ~/nvm/v0.10.33/bin/starrynight ]; then npm install -g starrynight; else echo "Starrynight seems to be cached"; fi;
# Install Meteor
- mkdir -p ${HOME}/.meteor
# If Meteor is already cached, do not need to build it again.
- if [ ! -e ${HOME}/.meteor/meteor ]; then curl https://install.meteor.com | /bin/sh; else echo "Meteor seems to be cached"; fi;
# Link the meteor executable into /usr/bin
- sudo ln -s $HOME/.meteor/meteor /usr/bin/meteor
# Check if the helloworld directory already exists, if it doesn't, create the helloworld app
- if [ ! -e ${HOME}/helloworld ]; then meteor create --release METEOR@1.1.0.3 helloworld; else echo "helloworld app seems to be cached"; fi;
override:
- cd helloworld
- cd helloworld && ls -la
- cd helloworld && rm helloworld.*
- cd helloworld && meteor add anti:gagarin@0.4.11 session meteor-platform clinical:theming clinical:default-theme clinical:router clinical:router-default-config clinical:active-layout clinical:active-layout-reset clinical:active-layout-pagescreen-config grove:less
- cd helloworld && git clone http://github.com/clinical-meteor/router-middleware-stack packages/router-middleware-stack
- cd helloworld && meteor add clinical:router clinical:router-middleware-stack
- cd helloworld/packages && ls -la
- cd helloworld/packages/router-middleware-stack && ls -la
- cd helloworld/packages/router-middleware-stack/tests && ls -la
- cd helloworld/packages/router-middleware-stack/tests/gagarin && ls -la
- cd helloworld && starrynight autoconfig
## Customize test commands
test:
pre:
- cd helloworld && meteor:
background: true
- sleep 80
override:
- cd helloworld && starrynight run-tests --type package-verification

View File

@ -0,0 +1 @@
local

View File

@ -0,0 +1 @@
ofqtruq93rux231sdq

View File

@ -0,0 +1,10 @@
# Meteor packages used by this project, one per line.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
standard-app-packages
autopublish
insecure
iron:middleware-stack

View File

@ -0,0 +1 @@
none

View File

@ -0,0 +1,44 @@
application-configuration@1.0.0
autopublish@1.0.0
autoupdate@1.0.0
binary-heap@1.0.0
blaze-tools@1.0.0
blaze@1.0.0
callback-hook@1.0.0
check@1.0.0
ctl-helper@1.0.0
ctl@1.0.0
deps@1.0.0
ejson@1.0.0
follower-livedata@1.0.0
geojson-utils@1.0.0
html-tools@1.0.0
htmljs@1.0.0
id-map@1.0.0
insecure@1.0.0
iron:core@0.1.0
iron:middleware-stack@0.1.0
iron:url@0.1.0
jquery@1.0.0
json@1.0.0
livedata@1.0.0
logging@1.0.0
meteor@1.0.0
minifiers@1.0.0
minimongo@1.0.0
mongo-livedata@1.0.1
observe-sequence@1.0.0
ordered-dict@1.0.0
random@1.0.0
reactive-dict@1.0.0
reload@1.0.0
retry@1.0.0
routepolicy@1.0.0
session@1.0.0
spacebars-compiler@1.0.0
spacebars@1.0.0
standard-app-packages@1.0.0
templating@1.0.0
ui@1.0.0
underscore@1.0.0
webapp@1.0.0

View File

@ -0,0 +1,29 @@
stack = new Iron.MiddlewareStack;
printUrls = function (name, thisArg) {
console.log('%c***********************', 'color: orange;');
console.log("<" + name + "> this.originalUrl: " + JSON.stringify(thisArg.originalUrl));
console.log("<" + name + "> this.url: " + JSON.stringify(thisArg.url));
console.log('%c***********************', 'color: orange;');
};
stack.push(function m1 () {
printUrls('m1', this);
this.next();
});
stack.push(function m2 () {
printUrls('m2', this);
this.next();
});
stack.push('/admin', function adminApp () {
printUrls('adminApp', this);
}, {mount: true});
console.log('%cDispatch "/"', 'color: blue;');
stack.dispatch('/', {});
console.log('');
console.log('%cDispatch "/admin"', 'color: blue;');
stack.dispatch('/admin', {});

View File

@ -0,0 +1,118 @@
var Url = Iron.Url;
Handler = function (path, fn, options) {
if (_.isFunction(path)) {
options = options || fn || {};
fn = path;
path = '/';
// probably need a better approach here to differentiate between
// Router.use(function () {}) and Router.use(MyAdminApp). In the first
// case we don't want to count it as a viable server handler when we're
// on the client and need to decide whether to go to the server. in the
// latter case, we DO want to go to the server, potentially.
this.middleware = true;
if (typeof options.mount === 'undefined')
options.mount = true;
}
// if fn is a function then typeof fn => 'function'
// but note we can't use _.isObject here because that will return true if the
// fn is a function OR an object.
if (typeof fn === 'object') {
options = fn;
fn = options.action || 'action';
}
options = options || {};
this.options = options;
this.mount = options.mount;
this.method = (options.method && options.method.toLowerCase()) || false;
// should the handler be on the 'client', 'server' or 'both'?
// XXX can't we default this to undefined in which case it's run in all
// environments?
this.where = options.where || 'client';
// if we're mounting at path '/foo' then this handler should also handle
// '/foo/bar' and '/foo/bar/baz'
if (this.mount)
options.end = false;
// set the name
if (options.name)
this.name = options.name;
else if (typeof path === 'string' && path.charAt(0) !== '/')
this.name = path;
else if (typeof path === 'string' && path !== '/')
this.name = path.split('/').slice(1).join('.');
// if the path is explicitly set on the options (e.g. legacy router support)
// then use that
// otherwise use the path argument which could also be a name
path = options.path || path;
if (typeof path === 'string' && path.charAt(0) !== '/')
path = '/' + path;
this.path = path;
this.compiledUrl = new Url(path, options);
if (_.isString(fn)) {
this.handle = function handle () {
// try to find a method on the current thisArg which might be a Controller
// for example.
var func = this[fn];
if (typeof func !== 'function')
throw new Error("No method named " + JSON.stringify(fn) + " found on handler.");
return func.apply(this, arguments);
};
} else if (_.isFunction(fn)) {
// or just a regular old function
this.handle = fn;
}
};
/**
* Returns true if the path matches the handler's compiled url, method
* and environment (e.g. client/server). If no options.method or options.where
* is provided, then only the path will be used to test.
*/
Handler.prototype.test = function (path, options) {
options = options || {};
var isUrlMatch = this.compiledUrl.test(path);
var isMethodMatch = true;
var isEnvMatch = true;
if (this.method && options.method)
isMethodMatch = this.method == options.method.toLowerCase();
if (options.where)
isEnvMatch = this.where == options.where;
return isUrlMatch && isMethodMatch && isEnvMatch;
};
Handler.prototype.params = function (path) {
return this.compiledUrl.params(path);
};
Handler.prototype.resolve = function (params, options) {
return this.compiledUrl.resolve(params, options);
};
/**
* Returns a new cloned Handler.
* XXX problem is here because we're not storing the original path.
*/
Handler.prototype.clone = function () {
var clone = new Handler(this.path, this.handle, this.options);
// in case the original function had a name
clone.name = this.name;
return clone;
};

View File

@ -0,0 +1,282 @@
var Url = Iron.Url;
var assert = Iron.utils.assert;
var defaultValue = Iron.utils.defaultValue;
/**
* Connect inspired middleware stack that works on the client and the server.
*
* You can add handlers to the stack for various paths. Those handlers can run
* on the client or server. Then you can dispatch into the stack with a
* given path by calling the dispatch method. This goes down the stack looking
* for matching handlers given the url and environment (client/server). If we're
* on the client and we should make a trip to the server, the onServerDispatch
* callback is called.
*
* The middleware stack supports the Connect API. But it also allows you to
* specify a context so we can have one context object (like a Controller) that
* is a consistent context for each handler function called on a dispatch.
*
*/
MiddlewareStack = function () {
this._stack = [];
this.length = 0;
};
MiddlewareStack.prototype._create = function (path, fn, options) {
var handler = new Handler(path, fn, options);
var name = handler.name;
if (name) {
if (_.has(this._stack, name)) {
throw new Error("Handler with name '" + name + "' already exists.");
}
this._stack[name] = handler;
}
return handler;
};
MiddlewareStack.prototype.findByName = function (name) {
return this._stack[name];
};
/**
* Push a new handler onto the stack.
*/
MiddlewareStack.prototype.push = function (path, fn, options) {
var handler = this._create(path, fn, options);
this._stack.push(handler);
this.length++;
return handler;
};
MiddlewareStack.prototype.append = function (/* fn1, fn2, [f3, f4]... */) {
var self = this;
var args = _.toArray(arguments);
var options = {};
if (typeof args[args.length-1] === 'object')
options = args.pop();
_.each(args, function (fnOrArray) {
if (typeof fnOrArray === 'undefined')
return;
else if (typeof fnOrArray === 'function')
self.push(fnOrArray, options);
else if (_.isArray(fnOrArray))
self.append.apply(self, fnOrArray.concat([options]));
else
throw new Error("Can only append functions or arrays to the MiddlewareStack");
});
return this;
};
/**
* Insert a handler at a specific index in the stack.
*
* The index behavior is the same as Array.prototype.splice. If the index is
* greater than the stack length the handler will be appended at the end of the
* stack. If the index is negative, the item will be inserted "index" elements
* from the end.
*/
MiddlewareStack.prototype.insertAt = function (index, path, fn, options) {
var handler = this._create(path, fn, options);
this._stack.splice(index, 0, handler);
this.length = this._stack.length;
return this;
};
/**
* Insert a handler before another named handler.
*/
MiddlewareStack.prototype.insertBefore = function (name, path, fn, options) {
var beforeHandler;
var index;
if (!(beforeHandler = this._stack[name]))
throw new Error("Couldn't find a handler named '" + name + "' on the path stack");
index = _.indexOf(this._stack, beforeHandler);
this.insertAt(index, path, fn, options);
return this;
};
/**
* Insert a handler after another named handler.
*
*/
MiddlewareStack.prototype.insertAfter = function (name, path, fn, options) {
var handler;
var index;
if (!(handler = this._stack[name]))
throw new Error("Couldn't find a handler named '" + name + "' on the path stack");
index = _.indexOf(this._stack, handler);
this.insertAt(index + 1, path, fn, options);
return this;
};
/**
* Return a new MiddlewareStack comprised of this stack joined with other
* stacks. Note the new stack will not have named handlers anymore. Only the
* handlers are cloned but not the name=>handler mapping.
*/
MiddlewareStack.prototype.concat = function (/* stack1, stack2, */) {
var ret = new MiddlewareStack;
var concat = Array.prototype.concat;
var clonedThisStack = EJSON.clone(this._stack);
var clonedOtherStacks = _.map(_.toArray(arguments), function (s) { return EJSON.clone(s._stack); });
ret._stack = concat.apply(clonedThisStack, clonedOtherStacks);
ret.length = ret._stack.length;
return ret;
};
/**
* Dispatch into the middleware stack, allowing the handlers to control the
* iteration by calling this.next();
*/
MiddlewareStack.prototype.dispatch = function dispatch (url, context, done) {
var self = this;
var originalUrl = url;
assert(typeof url === 'string', "Requires url");
assert(typeof context === 'object', "Requires context object");
url = Url.normalize(url || '/');
defaultValue(context, 'request', {});
defaultValue(context, 'response', {});
defaultValue(context, 'originalUrl', url);
//defaultValue(context, 'location', Url.parse(originalUrl));
defaultValue(context, '_method', context.method);
defaultValue(context, '_handlersForEnv', {client: false, server: false});
defaultValue(context, '_handled', false);
defaultValue(context, 'isHandled', function () {
return context._handled;
});
defaultValue(context, 'willBeHandledOnClient', function () {
return context._handlersForEnv.client;
});
defaultValue(context, 'willBeHandledOnServer', function () {
return context._handlersForEnv.server;
});
var wrappedDone = function () {
if (done) {
try {
done.apply(this, arguments);
} catch (err) {
// if we catch an error at this point in the stack we don't want it
// handled in the next() iterator below. So we'll mark the error to tell
// the next iterator to ignore it.
err._punt = true;
// now rethrow it!
throw err;
}
}
};
var index = 0;
var next = Meteor.bindEnvironment(function boundNext (err) {
var handler = self._stack[index++];
// reset the url
context.url = context.request.url = context.originalUrl;
if (!handler)
return wrappedDone.call(context, err);
if (!handler.test(url, {method: context._method}))
return next(err);
// okay if we've gotten this far the handler matches our url but we still
// don't know if this is a client or server handler. Let's track that.
// XXX couldn't the environment be something else like cordova?
var where = Meteor.isClient ? 'client' : 'server';
// track that we have a handler for the given environment so long as it's
// not middleware created like this Router.use(function () {}). We'll assume
// that if the handler is of that form we don't want to make a trip to
// the client or the server for it.
if (!handler.middleware)
context._handlersForEnv[handler.where] = true;
// but if we're not actually on that env, skip to the next handler.
if (handler.where !== where)
return next(err);
// get the parameters for this url from the handler's compiled path
// XXX removing for now
//var params = handler.params(context.location.href);
//context.request.params = defaultValue(context, 'params', {});
//_.extend(context.params, params);
// so we can call this.next()
// XXX this breaks with things like request.body which require that the
// iterator be saved for the given function call.
context.next = next;
if (handler.mount) {
var mountpath = Url.normalize(handler.compiledUrl.pathname);
var newUrl = url.substr(mountpath.length, url.length);
newUrl = Url.normalize(newUrl);
context.url = context.request.url = newUrl;
}
try {
//
// The connect api says a handler signature (arity) can look like any of:
//
// 1) function (req, res, next)
// 2) function (err, req, res, next)
// 3) function (err)
var arity = handler.handle.length
var req = context.request;
var res = context.response;
// function (err, req, res, next)
if (err && arity === 4)
return handler.handle.call(context, err, req, res, next);
// function (req, res, next)
if (!err && arity < 4)
return handler.handle.call(context, req, res, next);
// default is function (err) so punt the error down the stack
// until we either find a handler who likes to deal with errors or we call
// out
return next(err);
} catch (err) {
if (err._punt)
// ignore this error and throw it down the stack
throw err;
else
// see if the next handler wants to deal with the error
next(err);
} finally {
// we'll put this at the end because some middleware
// might want to decide what to do based on whether we've
// been handled "yet". If we set this to true before the handler
// is called, there's no way for the handler to say, if we haven't been
// handled yet go to the server, for example.
context._handled = true;
context.next = null;
}
});
next();
context.next = null;
return context;
};
Iron = Iron || {};
Iron.MiddlewareStack = MiddlewareStack;

View File

@ -0,0 +1,32 @@
Package.describe({
name: 'clinical:router-middleware-stack',
summary: 'Client and server middleware support inspired by Connect.',
version: '2.1.2',
git: 'https://github.com/clinical-meteor/clinical-router-middleware-stack'
});
Package.on_use(function (api) {
api.versionsFrom('1.1.0.3');
api.use('underscore');
api.use('ejson');
api.use('iron:core@1.0.11');
api.imply('iron:core');
api.use('clinical:router-url@2.1.0');
api.add_files('lib/handler.js');
api.add_files('lib/middleware_stack.js');
api.export('MiddlewareStack');
api.export('Handler', {testOnly: true});
});
Package.on_test(function (api) {
api.use('clinical:router-middleware-stack');
api.use('tinytest');
api.use('test-helpers');
api.add_files('tests/tinytests/handler_test.js');
api.add_files('tests/tinytests/middleware_stack_test.js');
});

View File

@ -0,0 +1,17 @@
describe('clinical:router-middleware-stack', function () {
var server = meteor();
var client = browser(server);
it('MiddlewareStack should exist on the client', function () {
return client.execute(function () {
expect(MiddlewareStack).to.exist;
});
});
it('MiddlewareStack should exist on the client', function () {
return server.execute(function () {
expect(MiddlewareStack).to.exist;
});
});
});

View File

@ -0,0 +1,54 @@
Tinytest.add('MiddlewareStack - handler basics', function (test) {
var handler;
var fn = function myName () {};
var opts = {};
// constructor options
// middleware handler
handler = new Handler(fn);
test.equal(handler.handle, fn);
test.equal(handler.name, 'myName', 'name is "myName"');
test.equal(handler.where, 'client');
test.isTrue(handler.test('/'));
test.isTrue(handler.test('/match/everything'));
opts.name = 'newName';
handler = new Handler('/items/:id', fn, opts);
test.equal(handler.handle, fn);
test.equal(handler.name, 'newName', 'name is "newName"');
test.equal(handler.options, opts);
test.isTrue(handler.test('/items/1'));
test.isTrue(handler.test('/items/2'));
handler = new Handler('/items/:id?', fn, opts);
test.isTrue(handler.test('/items/1'));
test.isTrue(handler.test('/items'));
handler = new Handler(/.*/, fn, opts);
test.isTrue(handler.test('/'));
test.isTrue(handler.test('/foo'));
var called = false;
var thisArg = {
methodName: function () {called = true;}
};
handler = new Handler('/items/:id', 'methodName');
handler.handle.call(thisArg);
test.isTrue(called);
handler = new Handler('/items/:id', fn);
var params = handler.params('/items/5');
test.equal(params.id, "5");
});
Tinytest.add('Handler - test', function (test) {
var handler = new Handler('/testme', function () {}, {
where: 'server',
method: 'GET'
});
test.isTrue(handler.test('/testme', {where: 'server', method: 'GET'}));
test.isFalse(handler.test('/testme', {where: 'client', method: 'GET'}));
test.isFalse(handler.test('/testme', {where: 'server', method: 'POST'}));
});

View File

@ -0,0 +1,182 @@
Tinytest.add('MiddlewareStack - handler names and paths', function (test) {
var handler;
// path is a name
handler = new Handler('home', {});
test.equal(handler.name, 'home', 'name is "home"');
test.equal(handler.path, '/home', 'path is "/home"');
// path is an option
handler = new Handler('home', {path: '/foo'});
test.equal(handler.name, 'home', 'name is "home"');
test.equal(handler.path, '/foo', 'path is "/foo"');
handler = new Handler('/home', {path: '/bar'});
test.equal(handler.path, '/bar', 'path is "/bar"');
handler = new Handler('/home', {path: '/bar', name: 'foo'});
test.equal(handler.path, '/bar', 'path is "/bar"');
test.equal(handler.name, 'foo', 'name is "foo"');
});
Tinytest.add('MiddlewareStack - create and find by name', function (test) {
// basically just test that a handler gets created and keyed by name if thee's
// a name. Also test duplicate named handlers throws an error.
var stack = new Iron.MiddlewareStack;
stack._create('/items', function () {}, {name: 'items'});
test.isTrue(stack.findByName('items'));
test.throws(function () {
// same name
stack._create('/items', function () {}, {name: 'items'});
});
});
Tinytest.add('MiddlewareStack - push', function (test) {
var stack = new Iron.MiddlewareStack;
var fns = [function () {}, function () {}];
stack.push(fns[0]);
test.equal(stack._stack[0].handle, fns[0]);
stack.push(fns[1]);
test.equal(stack._stack[1].handle, fns[1]);
});
Tinytest.add('MiddlewareStack - insertAt', function (test) {
var stack = new Iron.MiddlewareStack;
var fns = [function () {}, function () {}, function () {}];
stack.push(fns[0]);
stack.push(fns[2]);
stack.insertAt(1, fns[1]);
test.equal(stack._stack[1].handle, fns[1]);
});
Tinytest.add('MiddlewareStack - insertBefore', function (test) {
var stack = new Iron.MiddlewareStack;
var fns = [function one() {}, function two() {}, function three() {}];
stack.push(fns[0]);
stack.push(fns[2]);
stack.insertBefore('three', fns[1]);
test.equal(stack._stack[1].handle, fns[1]);
});
Tinytest.add('MiddlewareStack - insertAfter ', function (test) {
var stack = new Iron.MiddlewareStack;
var fns = [function one() {}, function two() {}, function three() {}];
stack.push(fns[0]);
stack.push(fns[2]);
stack.insertAfter('one', fns[1]);
test.equal(stack._stack[1].handle, fns[1]);
});
Tinytest.add('MiddlewareStack - dispatch iteration with this.next', function (test) {
var stack = new Iron.MiddlewareStack;
var calls = [];
if (Meteor.isClient) {
stack.push(function m1 () {
calls.push('m1');
this.next();
});
stack.push(function m2 () {
calls.push('m2');
// no call to next
});
stack.push(function m3 () {
calls.push('m3');
});
stack.dispatch('/', {});
test.equal(calls.length, 2, "call length is two");
test.equal(calls[0], 'm1', "m1 called");
test.equal(calls[1], 'm2', "m2 called");
}
if (Meteor.isServer) {
stack.push(function m1 () {
calls.push('m1');
this.next();
}, {where: 'server'});
stack.push(function m2 () {
calls.push('m2');
// no call to next
}, {where: 'server'});
stack.push(function m3 () {
calls.push('m3');
}, {where: 'server'});
stack.dispatch('/', {});
test.equal(calls.length, 2, "call length is two");
test.equal(calls[0], 'm1', "m1 called");
test.equal(calls[1], 'm2', "m2 called");
}
});
Tinytest.add('MiddlewareStack - dispatch callback', function (test) {
var stack = new Iron.MiddlewareStack;
var calls = [];
if (Meteor.isClient) {
stack.push(function m1 () {
calls.push('m1');
this.next();
});
stack.dispatch('/', {}, function () {
calls.push('done');
});
test.equal(calls.length, 2, "call length is two");
test.equal(calls[0], 'm1', "m1 called");
test.equal(calls[1], 'done', "done called");
}
if (Meteor.isServer) {
stack.push(function m1 () {
calls.push('m1');
this.next();
}, {where: 'server'});
stack.dispatch('/', {}, function () {
calls.push('done');
});
test.equal(calls.length, 2, "call length is two");
test.equal(calls[0], 'm1', "m1 called");
test.equal(calls[1], 'done', "done called");
}
});
if (Meteor.isServer) {
var Fiber = Npm.require('fibers');
Tinytest.addAsync('MiddlewareStack - async next maintains fibers', function (test, done) {
var envVar = new Meteor.EnvironmentVariable;
envVar.withValue(true, function () {
var stack = new Iron.MiddlewareStack;
test.isTrue(envVar.getOrNullIfOutsideFiber());
stack.push(function(req, res, next) {
// break out of the current fiber
setTimeout(function() {
next();
}, 0);
}, {where: 'server'});
stack.push(function(req, res, next) {
test.isTrue(envVar.getOrNullIfOutsideFiber());
this.next();
}, {where: 'server'});
stack.dispatch('/', {}, function () {
test.isTrue(envVar.getOrNullIfOutsideFiber());
done();
});
});
});
}

View File

@ -0,0 +1,194 @@
Tinytest.add('MiddlewareStack - dispatch', function (test) {
var stack = new Iron.MiddlewareStack;
var calls = {};
var call = function (name) {
calls[name] = calls[name] || 0;
calls[name]++;
};
// client middleware
stack.push(function m1 (req, res, next) {
call('m1');
next();
});
stack.push(function m2 (req, res, next) {
call('m2');
this.next();
});
// client handlers
var params;
stack.push('/items/:id', function item (req, res, next) {
call('item');
params = this.params;
next();
});
// same path is okay as long as name is different
stack.push('/items/:id', function itemTwo (req, res, next) {
call('item2');
});
// server handler
stack.push('/server', function server (req, res, next) {
call('server');
}, { where: 'server' });
var thisArg = {};
if (Meteor.isClient) {
stack.dispatch('/', {});
test.equal(calls.m1, 1, 'm1 not called');
test.equal(calls.m2, 1, 'm2 not called');
test.isFalse(calls.item);
test.isFalse(calls.item2);
test.isFalse(calls.server);
stack.dispatch('/items/1', thisArg);
test.equal(calls.m1, 2, 'm1 not called');
test.equal(calls.m2, 2, 'm2 not called');
test.equal(calls.item, 1, 'item not called');
test.equal(calls.item2, 1, 'item2 not called');
test.isFalse(calls.server);
var params = thisArg.params;
test.equal(params.id, "1");
stack.onServerDispatch(function () {
call('serverDispatch');
});
stack.dispatch('/server', {});
test.equal(calls.serverDispatch, 1);
}
if (Meteor.isServer) {
stack.dispatch('/server', {});
test.isFalse(calls.m1);
test.isFalse(calls.m2);
test.equal(calls.server, 1);
}
});
Tinytest.add('MiddlewareStack - dispatch error handling', function (test) {
if (Meteor.isClient) {
var stack = new Iron.MiddlewareStack;
var calls = [];
stack.push(function (req, res, next) {
calls.push(1);
throw new Error('test');
});
stack.push(function (req, res, next) {
calls.push(2);
next();
});
stack.dispatch('/', {
next: function (err) {
calls.push(3);
test.equal(err.message, 'test');
}
});
test.equal(calls.length, 2);
// first fn throws an error
test.equal(calls[0], 1);
// rest of middleware skipped
test.equal(calls[1], 3);
}
});
Tinytest.add('MiddlewareStack - mounting paths', function (test) {
var stack = new Iron.MiddlewareStack;
var calls = [];
if (Meteor.isClient) {
// add a mounted handler at mountpath /foo
stack.push('/foo', function (req, res, next) {
calls.push({args: EJSON.clone(arguments), thisArg: EJSON.clone(this)});
next();
}, {mount: true});
// add a regular handler at /foo/bar/baz
stack.push('/foo/bar/baz', function (req, res, next) {
calls.push({args: EJSON.clone(arguments), thisArg: EJSON.clone(this)});
});
stack.dispatch('/foo/bar/baz', {});
// make sure it got called
test.equal(calls.length, 2);
// this.url
test.equal(calls[0].thisArg.url, '/bar/baz', 'url wrong on controller');
test.equal(calls[0].thisArg.originalUrl, '/foo/bar/baz', 'originalUrl wrong on controller');
// fn (req, res, next)
// req.url
test.equal(calls[0].args[0].url, '/bar/baz', 'url wrong on req');
test.equal(calls[0].args[0].originalUrl, '/foo/bar/baz', 'originalUrl wrong on req');
// now make sure the url is set back to the originalUrl for non mounted
// handlers.
test.equal(calls[1].thisArg.url, '/foo/bar/baz');
test.equal(calls[1].thisArg.originalUrl, '/foo/bar/baz');
// fn (req, res, next)
// req.url
test.equal(calls[1].args[0].url, '/foo/bar/baz');
test.equal(calls[1].args[0].originalUrl, '/foo/bar/baz');
}
if (Meteor.isServer) {
stack.push('/foo', function (req, res, next) {
calls.push({args: EJSON.clone(arguments), thisArg: EJSON.clone(this)});
next();
}, {mount: true, where: 'server'});
// add a regular handler at /foo/bar/baz
stack.push('/foo/bar/baz', function (req, res, next) {
calls.push({args: EJSON.clone(arguments), thisArg: EJSON.clone(this)});
}, {where: 'server'});
stack.dispatch('/foo/bar/baz', {});
// make sure it got called
test.equal(calls.length, 2);
// this.url
test.equal(calls[0].thisArg.url, '/bar/baz');
test.equal(calls[0].thisArg.originalUrl, '/foo/bar/baz');
// fn (req, res, next)
// req.url
test.equal(calls[0].args[0].url, '/bar/baz');
test.equal(calls[0].args[0].originalUrl, '/foo/bar/baz');
// now make sure the url is set back to the originalUrl for non mounted
// handlers.
test.equal(calls[1].thisArg.url, '/foo/bar/baz');
test.equal(calls[1].thisArg.originalUrl, '/foo/bar/baz');
// fn (req, res, next)
// req.url
test.equal(calls[1].args[0].url, '/foo/bar/baz');
test.equal(calls[1].args[0].originalUrl, '/foo/bar/baz');
}
});
Tinytest.add('MiddlewareStack - append with options', function (test) {
var fn1 = function () {};
var fn2 = function () {};
var stack = new Iron.MiddlewareStack;
stack.append(fn1, fn2, {where: 'server'});
test.equal(stack._stack[0].where, 'server');
test.equal(stack._stack[1].where, 'server');
});
//TODO concat
//TODO append