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 assert.throws(app[method].bind(app, '/', 3), /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){
639 var cb = after(2, done)
641 app.get('/api*', function(req, res){
642 res.send(req.params[0]);
651 .expect(200, '/hey', cb)
654 it('should allow naming', function(done){
657 app.get('/api/:resource(*)', function(req, res){
658 var resource = req.params.resource;
663 .get('/api/users/0.json')
664 .expect('users/0.json', done);
667 it('should not be greedy immediately after param', function(done){
670 app.get('/user/:user*', function(req, res){
671 res.end(req.params.user);
676 .expect('122', done);
679 it('should eat everything after /', function(done){
682 app.get('/user/:user*', function(req, res){
683 res.end(req.params.user);
687 .get('/user/122/aaa')
688 .expect('122', done);
691 it('should span multiple segments', function(done){
694 app.get('/file/*', function(req, res){
695 res.end(req.params[0]);
699 .get('/file/javascripts/jquery.js')
700 .expect('javascripts/jquery.js', done);
703 it('should be optional', function(done){
706 app.get('/file/*', function(req, res){
707 res.end(req.params[0]);
715 it('should require a preceding /', function(done){
718 app.get('/file/*', function(req, res){
719 res.end(req.params[0]);
727 it('should keep correct parameter indexes', function(done){
730 app.get('/*/user/:id', function (req, res) {
731 res.send(req.params);
736 .expect(200, '{"0":"1","id":"2"}', done);
739 it('should work within arrays', function(done){
742 app.get(['/user/:id', '/foo/*', '/:bar'], function (req, res) {
743 res.send(req.params.bar);
748 .expect(200, 'test', done);
752 describe(':name', function(){
753 it('should denote a capture group', function(done){
756 app.get('/user/:user', function(req, res){
757 res.end(req.params.user);
765 it('should match a single segment only', function(done){
768 app.get('/user/:user', function(req, res){
769 res.end(req.params.user);
773 .get('/user/tj/edit')
777 it('should allow several capture groups', function(done){
780 app.get('/user/:user/:op', function(req, res){
781 res.end(req.params.op + 'ing ' + req.params.user);
785 .get('/user/tj/edit')
786 .expect('editing tj', done);
789 it('should work following a partial capture group', function(done){
791 var cb = after(2, done);
793 app.get('/user(s)?/:user/:op', function(req, res){
794 res.end(req.params.op + 'ing ' + req.params.user + (req.params[0] ? ' (old)' : ''));
798 .get('/user/tj/edit')
799 .expect('editing tj', cb);
802 .get('/users/tj/edit')
803 .expect('editing tj (old)', cb);
806 it('should work inside literal parenthesis', function(done){
809 app.get('/:user\\(:op\\)', function(req, res){
810 res.end(req.params.op + 'ing ' + req.params.user);
815 .expect('editing tj', done);
818 it('should work in array of paths', function(done){
820 var cb = after(2, done);
822 app.get(['/user/:user/poke', '/user/:user/pokes'], function(req, res){
823 res.end('poking ' + req.params.user);
827 .get('/user/tj/poke')
828 .expect('poking tj', cb);
831 .get('/user/tj/pokes')
832 .expect('poking tj', cb);
836 describe(':name?', function(){
837 it('should denote an optional capture group', function(done){
840 app.get('/user/:user/:op?', function(req, res){
841 var op = req.params.op || 'view';
842 res.end(op + 'ing ' + req.params.user);
847 .expect('viewing tj', done);
850 it('should populate the capture group', function(done){
853 app.get('/user/:user/:op?', function(req, res){
854 var op = req.params.op || 'view';
855 res.end(op + 'ing ' + req.params.user);
859 .get('/user/tj/edit')
860 .expect('editing tj', done);
864 describe('.:name', function(){
865 it('should denote a format', function(done){
867 var cb = after(2, done)
869 app.get('/:name.:format', function(req, res){
870 res.end(req.params.name + ' as ' + req.params.format);
875 .expect(200, 'foo as json', cb)
883 describe('.:name?', function(){
884 it('should denote an optional format', function(done){
886 var cb = after(2, done)
888 app.get('/:name.:format?', function(req, res){
889 res.end(req.params.name + ' as ' + (req.params.format || 'html'));
894 .expect(200, 'foo as html', cb)
898 .expect(200, 'foo as json', done)
902 describe('when next() is called', function(){
903 it('should continue lookup', function(done){
907 app.get('/foo/:bar?', function(req, res, next){
908 calls.push('/foo/:bar?');
912 app.get('/bar', function(req, res){
916 app.get('/foo', function(req, res, next){
921 app.get('/foo', function(req, res, next){
922 calls.push('/foo 2');
928 .expect(200, ['/foo/:bar?', '/foo', '/foo 2'], done)
932 describe('when next("route") is called', function(){
933 it('should jump to next route', function(done){
936 function fn(req, res, next){
937 res.set('X-Hit', '1')
941 app.get('/foo', fn, function(req, res, next){
945 app.get('/foo', function(req, res){
951 .expect('X-Hit', '1')
952 .expect(200, 'success', done)
956 describe('when next("router") is called', function () {
957 it('should jump out of router', function (done) {
959 var router = express.Router()
961 function fn (req, res, next) {
962 res.set('X-Hit', '1')
966 router.get('/foo', fn, function (req, res, next) {
970 router.get('/foo', function (req, res, next) {
976 app.get('/foo', function (req, res) {
982 .expect('X-Hit', '1')
983 .expect(200, 'success', done)
987 describe('when next(err) is called', function(){
988 it('should break out of app.router', function(done){
992 app.get('/foo/:bar?', function(req, res, next){
993 calls.push('/foo/:bar?');
997 app.get('/bar', function(req, res){
1001 app.get('/foo', function(req, res, next){
1003 next(new Error('fail'));
1006 app.get('/foo', function(req, res, next){
1010 app.use(function(err, req, res, next){
1019 .expect(200, { calls: ['/foo/:bar?', '/foo'], error: 'fail' }, done)
1022 it('should call handler in same route, if exists', function(done){
1023 var app = express();
1025 function fn1(req, res, next) {
1026 next(new Error('boom!'));
1029 function fn2(req, res, next) {
1030 res.send('foo here');
1033 function fn3(err, req, res, next) {
1034 res.send('route go ' + err.message);
1037 app.get('/foo', fn1, fn2, fn3);
1039 app.use(function (err, req, res, next) {
1045 .expect('route go boom!', done)
1049 it('should allow rewriting of the url', function(done){
1050 var app = express();
1052 app.get('/account/edit', function(req, res, next){
1053 req.user = { id: 12 }; // faux authenticated user
1054 req.url = '/user/' + req.user.id + '/edit';
1058 app.get('/user/:id/edit', function(req, res){
1059 res.send('editing user ' + req.params.id);
1063 .get('/account/edit')
1064 .expect('editing user 12', done);
1067 it('should run in order added', function(done){
1068 var app = express();
1071 app.get('*', function(req, res, next){
1076 app.get('/user/:id', function(req, res, next){
1081 app.use(function(req, res, next){
1086 app.all('/user/:id', function(req, res, next){
1091 app.get('*', function(req, res, next){
1096 app.use(function(req, res, next){
1098 res.end(path.join(','))
1103 .expect(200, '0,1,2,3,4,5', done);
1106 it('should be chainable', function(){
1107 var app = express();
1108 assert.strictEqual(app.get('/', function () {}), app)