2 var after = require('after');
3 var express = require('../')
4 , request = require('supertest')
5 , assert = require('assert')
6 , methods = require('methods');
8 describe('app.router', function(){
9 it('should restore req.params after leaving router', function(done){
11 var router = new express.Router();
13 function handler1(req, res, next){
14 res.setHeader('x-user-id', String(req.params.id));
18 function handler2(req, res){
19 res.send(req.params.id);
22 router.use(function(req, res, next){
23 res.setHeader('x-router', String(req.params.id));
27 app.get('/user/:id', handler1, router, handler2);
31 .expect('x-router', 'undefined')
32 .expect('x-user-id', '1')
33 .expect(200, '1', done);
36 describe('methods', function(){
37 methods.concat('del').forEach(function(method){
38 if (method === 'connect') return;
40 it('should include ' + method.toUpperCase(), function(done){
43 app[method]('/foo', function(req, res){
52 it('should reject numbers for app.' + method, function(){
54 app[method].bind(app, '/', 3).should.throw(/Number/);
58 it('should re-route when method is altered', function (done) {
60 var cb = after(3, done);
62 app.use(function (req, res, next) {
63 if (req.method !== 'POST') return next();
64 req.method = 'DELETE';
65 res.setHeader('X-Method-Altered', '1');
69 app.delete('/', function (req, res) {
70 res.end('deleted everything');
79 .expect(200, 'deleted everything', cb);
83 .expect('X-Method-Altered', '1')
84 .expect(200, 'deleted everything', cb);
88 describe('decode params', function () {
89 it('should decode correct params', function(done){
92 app.get('/:name', function(req, res, next){
93 res.send(req.params.name);
98 .expect('foo/bar', done);
101 it('should not accept params in malformed paths', function(done) {
104 app.get('/:name', function(req, res, next){
105 res.send(req.params.name);
113 it('should not decode spaces', function(done) {
116 app.get('/:name', function(req, res, next){
117 res.send(req.params.name);
122 .expect('foo+bar', done);
125 it('should work with unicode', function(done) {
128 app.get('/:name', function(req, res, next){
129 res.send(req.params.name);
134 .expect('\u03b1', done);
138 it('should be .use()able', function(done){
143 app.use(function(req, res, next){
144 calls.push('before');
148 app.get('/', function(req, res, next){
153 app.use(function(req, res, next){
160 .expect(200, ['before', 'GET /', 'after'], done)
163 describe('when given a regexp', function(){
164 it('should match the pathname only', function(done){
167 app.get(/^\/user\/[0-9]+$/, function(req, res){
172 .get('/user/12?foo=bar')
173 .expect('user', done);
176 it('should populate req.params with the captures', function(done){
179 app.get(/^\/user\/([0-9]+)\/(view|edit)?$/, function(req, res){
180 var id = req.params[0]
181 , op = req.params[1];
182 res.end(op + 'ing user ' + id);
186 .get('/user/10/edit')
187 .expect('editing user 10', done);
190 it('should ensure regexp matches path prefix', function (done) {
194 app.use(/\/api.*/, function (req, res, next) {
198 app.use(/api/, function (req, res, next) {
202 app.use(/\/test/, function (req, res, next) {
206 app.use(function (req, res) {
211 .get('/test/api/1234')
212 .expect(200, function (err) {
213 if (err) return done(err)
214 assert.deepEqual(p, ['c'])
220 describe('case sensitivity', function(){
221 it('should be disabled by default', function(done){
224 app.get('/user', function(req, res){
233 describe('when "case sensitive routing" is enabled', function(){
234 it('should match identical casing', function(done){
237 app.enable('case sensitive routing');
239 app.get('/uSer', function(req, res){
248 it('should not match otherwise', function(done){
251 app.enable('case sensitive routing');
253 app.get('/uSer', function(req, res){
264 describe('params', function(){
265 it('should overwrite existing req.params by default', function(done){
267 var router = new express.Router();
269 router.get('/:action', function(req, res){
270 res.send(req.params);
273 app.use('/user/:user', router);
277 .expect(200, '{"action":"get"}', done);
280 it('should allow merging existing req.params', function(done){
282 var router = new express.Router({ mergeParams: true });
284 router.get('/:action', function(req, res){
285 var keys = Object.keys(req.params).sort();
286 res.send(keys.map(function(k){ return [k, req.params[k]] }));
289 app.use('/user/:user', router);
293 .expect(200, '[["action","get"],["user","tj"]]', done);
296 it('should use params from router', function(done){
298 var router = new express.Router({ mergeParams: true });
300 router.get('/:thing', function(req, res){
301 var keys = Object.keys(req.params).sort();
302 res.send(keys.map(function(k){ return [k, req.params[k]] }));
305 app.use('/user/:thing', router);
309 .expect(200, '[["thing","get"]]', done);
312 it('should merge numeric indices req.params', function(done){
314 var router = new express.Router({ mergeParams: true });
316 router.get('/*.*', function(req, res){
317 var keys = Object.keys(req.params).sort();
318 res.send(keys.map(function(k){ return [k, req.params[k]] }));
321 app.use('/user/id:(\\d+)', router);
324 .get('/user/id:10/profile.json')
325 .expect(200, '[["0","10"],["1","profile"],["2","json"]]', done);
328 it('should merge numeric indices req.params when more in parent', function(done){
330 var router = new express.Router({ mergeParams: true });
332 router.get('/*', function(req, res){
333 var keys = Object.keys(req.params).sort();
334 res.send(keys.map(function(k){ return [k, req.params[k]] }));
337 app.use('/user/id:(\\d+)/name:(\\w+)', router);
340 .get('/user/id:10/name:tj/profile')
341 .expect(200, '[["0","10"],["1","tj"],["2","profile"]]', done);
344 it('should merge numeric indices req.params when parent has same number', function(done){
346 var router = new express.Router({ mergeParams: true });
348 router.get('/name:(\\w+)', function(req, res){
349 var keys = Object.keys(req.params).sort();
350 res.send(keys.map(function(k){ return [k, req.params[k]] }));
353 app.use('/user/id:(\\d+)', router);
356 .get('/user/id:10/name:tj')
357 .expect(200, '[["0","10"],["1","tj"]]', done);
360 it('should ignore invalid incoming req.params', function(done){
362 var router = new express.Router({ mergeParams: true });
364 router.get('/:name', function(req, res){
365 var keys = Object.keys(req.params).sort();
366 res.send(keys.map(function(k){ return [k, req.params[k]] }));
369 app.use('/user/', function (req, res, next) {
370 req.params = 3; // wat?
371 router(req, res, next);
376 .expect(200, '[["name","tj"]]', done);
379 it('should restore req.params', function(done){
381 var router = new express.Router({ mergeParams: true });
383 router.get('/user:(\\w+)/*', function (req, res, next) {
387 app.use('/user/id:(\\d+)', function (req, res, next) {
388 router(req, res, function (err) {
389 var keys = Object.keys(req.params).sort();
390 res.send(keys.map(function(k){ return [k, req.params[k]] }));
395 .get('/user/id:42/user:tj/profile')
396 .expect(200, '[["0","42"]]', done);
400 describe('trailing slashes', function(){
401 it('should be optional by default', function(done){
404 app.get('/user', function(req, res){
413 describe('when "strict routing" is enabled', function(){
414 it('should match trailing slashes', function(done){
417 app.enable('strict routing');
419 app.get('/user/', function(req, res){
428 it('should pass-though middleware', function(done){
431 app.enable('strict routing');
433 app.use(function (req, res, next) {
434 res.setHeader('x-middleware', 'true');
438 app.get('/user/', function(req, res){
444 .expect('x-middleware', 'true')
445 .expect(200, 'tj', done);
448 it('should pass-though mounted middleware', function(done){
451 app.enable('strict routing');
453 app.use('/user/', function (req, res, next) {
454 res.setHeader('x-middleware', 'true');
458 app.get('/user/test/', function(req, res){
464 .expect('x-middleware', 'true')
465 .expect(200, 'tj', done);
468 it('should match no slashes', function(done){
471 app.enable('strict routing');
473 app.get('/user', function(req, res){
482 it('should match middleware when omitting the trailing slash', function(done){
485 app.enable('strict routing');
487 app.use('/user/', function(req, res){
493 .expect(200, 'tj', done);
496 it('should match middleware', function(done){
499 app.enable('strict routing');
501 app.use('/user', function(req, res){
507 .expect(200, 'tj', done);
510 it('should match middleware when adding the trailing slash', function(done){
513 app.enable('strict routing');
515 app.use('/user', function(req, res){
521 .expect(200, 'tj', done);
524 it('should fail when omitting the trailing slash', function(done){
527 app.enable('strict routing');
529 app.get('/user/', function(req, res){
538 it('should fail when adding the trailing slash', function(done){
541 app.enable('strict routing');
543 app.get('/user', function(req, res){
554 it('should allow escaped regexp', function(done){
557 app.get('/user/\\d+', function(req, res){
563 .expect(200, function (err) {
564 if (err) return done(err)
571 it('should allow literal "."', function(done){
574 app.get('/api/users/:from..:to', function(req, res){
575 var from = req.params.from
576 , to = req.params.to;
578 res.end('users from ' + from + ' to ' + to);
582 .get('/api/users/1..50')
583 .expect('users from 1 to 50', done);
586 describe('*', function(){
587 it('should capture everything', function (done) {
590 app.get('*', function (req, res) {
591 res.end(req.params[0])
595 .get('/user/tobi.json')
596 .expect('/user/tobi.json', done)
599 it('should decode the capture', function (done) {
602 app.get('*', function (req, res) {
603 res.end(req.params[0])
607 .get('/user/tobi%20and%20loki.json')
608 .expect('/user/tobi and loki.json', done)
611 it('should denote a greedy capture group', function(done){
614 app.get('/user/*.json', function(req, res){
615 res.end(req.params[0]);
619 .get('/user/tj.json')
623 it('should work with several', function(done){
626 app.get('/api/*.*', function(req, res){
627 var resource = req.params[0]
628 , format = req.params[1];
629 res.end(resource + ' as ' + format);
633 .get('/api/users/foo.bar.json')
634 .expect('users/foo.bar as json', done);
637 it('should work cross-segment', function(done){
640 app.get('/api*', function(req, res){
641 res.send(req.params[0]);
646 .expect('', function(){
649 .expect('/hey', done);
653 it('should allow naming', function(done){
656 app.get('/api/:resource(*)', function(req, res){
657 var resource = req.params.resource;
662 .get('/api/users/0.json')
663 .expect('users/0.json', done);
666 it('should not be greedy immediately after param', function(done){
669 app.get('/user/:user*', function(req, res){
670 res.end(req.params.user);
675 .expect('122', done);
678 it('should eat everything after /', function(done){
681 app.get('/user/:user*', function(req, res){
682 res.end(req.params.user);
686 .get('/user/122/aaa')
687 .expect('122', done);
690 it('should span multiple segments', function(done){
693 app.get('/file/*', function(req, res){
694 res.end(req.params[0]);
698 .get('/file/javascripts/jquery.js')
699 .expect('javascripts/jquery.js', done);
702 it('should be optional', function(done){
705 app.get('/file/*', function(req, res){
706 res.end(req.params[0]);
714 it('should require a preceding /', function(done){
717 app.get('/file/*', function(req, res){
718 res.end(req.params[0]);
726 it('should keep correct parameter indexes', function(done){
729 app.get('/*/user/:id', function (req, res) {
730 res.send(req.params);
735 .expect(200, '{"0":"1","id":"2"}', done);
738 it('should work within arrays', function(done){
741 app.get(['/user/:id', '/foo/*', '/:bar'], function (req, res) {
742 res.send(req.params.bar);
747 .expect(200, 'test', done);
751 describe(':name', function(){
752 it('should denote a capture group', function(done){
755 app.get('/user/:user', function(req, res){
756 res.end(req.params.user);
764 it('should match a single segment only', function(done){
767 app.get('/user/:user', function(req, res){
768 res.end(req.params.user);
772 .get('/user/tj/edit')
776 it('should allow several capture groups', function(done){
779 app.get('/user/:user/:op', function(req, res){
780 res.end(req.params.op + 'ing ' + req.params.user);
784 .get('/user/tj/edit')
785 .expect('editing tj', done);
788 it('should work following a partial capture group', function(done){
790 var cb = after(2, done);
792 app.get('/user(s)?/:user/:op', function(req, res){
793 res.end(req.params.op + 'ing ' + req.params.user + (req.params[0] ? ' (old)' : ''));
797 .get('/user/tj/edit')
798 .expect('editing tj', cb);
801 .get('/users/tj/edit')
802 .expect('editing tj (old)', cb);
805 it('should work inside literal parenthesis', function(done){
808 app.get('/:user\\(:op\\)', function(req, res){
809 res.end(req.params.op + 'ing ' + req.params.user);
814 .expect('editing tj', done);
817 it('should work in array of paths', function(done){
819 var cb = after(2, done);
821 app.get(['/user/:user/poke', '/user/:user/pokes'], function(req, res){
822 res.end('poking ' + req.params.user);
826 .get('/user/tj/poke')
827 .expect('poking tj', cb);
830 .get('/user/tj/pokes')
831 .expect('poking tj', cb);
835 describe(':name?', function(){
836 it('should denote an optional capture group', function(done){
839 app.get('/user/:user/:op?', function(req, res){
840 var op = req.params.op || 'view';
841 res.end(op + 'ing ' + req.params.user);
846 .expect('viewing tj', done);
849 it('should populate the capture group', function(done){
852 app.get('/user/:user/:op?', function(req, res){
853 var op = req.params.op || 'view';
854 res.end(op + 'ing ' + req.params.user);
858 .get('/user/tj/edit')
859 .expect('editing tj', done);
863 describe('.:name', function(){
864 it('should denote a format', function(done){
867 app.get('/:name.:format', function(req, res){
868 res.end(req.params.name + ' as ' + req.params.format);
873 .expect('foo as json', function(){
881 describe('.:name?', function(){
882 it('should denote an optional format', function(done){
885 app.get('/:name.:format?', function(req, res){
886 res.end(req.params.name + ' as ' + (req.params.format || 'html'));
891 .expect('foo as html', function(){
894 .expect('foo as json', done);
899 describe('when next() is called', function(){
900 it('should continue lookup', function(done){
904 app.get('/foo/:bar?', function(req, res, next){
905 calls.push('/foo/:bar?');
909 app.get('/bar', function(req, res){
913 app.get('/foo', function(req, res, next){
918 app.get('/foo', function(req, res, next){
919 calls.push('/foo 2');
925 .expect(200, ['/foo/:bar?', '/foo', '/foo 2'], done)
929 describe('when next("route") is called', function(){
930 it('should jump to next route', function(done){
933 function fn(req, res, next){
934 res.set('X-Hit', '1')
938 app.get('/foo', fn, function(req, res, next){
942 app.get('/foo', function(req, res){
948 .expect('X-Hit', '1')
949 .expect(200, 'success', done)
953 describe('when next("router") is called', function () {
954 it('should jump out of router', function (done) {
956 var router = express.Router()
958 function fn (req, res, next) {
959 res.set('X-Hit', '1')
963 router.get('/foo', fn, function (req, res, next) {
967 router.get('/foo', function (req, res, next) {
973 app.get('/foo', function (req, res) {
979 .expect('X-Hit', '1')
980 .expect(200, 'success', done)
984 describe('when next(err) is called', function(){
985 it('should break out of app.router', function(done){
989 app.get('/foo/:bar?', function(req, res, next){
990 calls.push('/foo/:bar?');
994 app.get('/bar', function(req, res){
998 app.get('/foo', function(req, res, next){
1000 next(new Error('fail'));
1003 app.get('/foo', function(req, res, next){
1007 app.use(function(err, req, res, next){
1016 .expect(200, { calls: ['/foo/:bar?', '/foo'], error: 'fail' }, done)
1019 it('should call handler in same route, if exists', function(done){
1020 var app = express();
1022 function fn1(req, res, next) {
1023 next(new Error('boom!'));
1026 function fn2(req, res, next) {
1027 res.send('foo here');
1030 function fn3(err, req, res, next) {
1031 res.send('route go ' + err.message);
1034 app.get('/foo', fn1, fn2, fn3);
1036 app.use(function (err, req, res, next) {
1042 .expect('route go boom!', done)
1046 it('should allow rewriting of the url', function(done){
1047 var app = express();
1049 app.get('/account/edit', function(req, res, next){
1050 req.user = { id: 12 }; // faux authenticated user
1051 req.url = '/user/' + req.user.id + '/edit';
1055 app.get('/user/:id/edit', function(req, res){
1056 res.send('editing user ' + req.params.id);
1060 .get('/account/edit')
1061 .expect('editing user 12', done);
1064 it('should run in order added', function(done){
1065 var app = express();
1068 app.get('*', function(req, res, next){
1073 app.get('/user/:id', function(req, res, next){
1078 app.use(function(req, res, next){
1083 app.all('/user/:id', function(req, res, next){
1088 app.get('*', function(req, res, next){
1093 app.use(function(req, res, next){
1095 res.end(path.join(','))
1100 .expect(200, '0,1,2,3,4,5', done);
1103 it('should be chainable', function(){
1104 var app = express();
1105 app.get('/', function(){}).should.equal(app);