route.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. /**
  2. * Route tests
  3. */
  4. var koa = require('koa')
  5. , http = require('http')
  6. , request = require('supertest')
  7. , Router = require('../../lib/router')
  8. , should = require('should')
  9. , Route = require('../../lib/route');
  10. describe('Route', function() {
  11. it('supports regular expression route paths', function(done) {
  12. var app = koa();
  13. app.use(Router(app));
  14. app.get(/^\/blog\/\d{4}-\d{2}-\d{2}\/?$/i, function *(next) {
  15. this.status = 204;
  16. });
  17. request(http.createServer(app.callback()))
  18. .get('/blog/2013-04-20')
  19. .expect(204)
  20. .end(function(err) {
  21. if (err) return done(err);
  22. done();
  23. });
  24. });
  25. it('supports named regular express routes', function(done) {
  26. var app = koa();
  27. app.use(Router(app));
  28. app.get('test', /^\/test\/?/i, function *(next) {
  29. this.status = 204;
  30. yield next;
  31. });
  32. request(http.createServer(app.callback()))
  33. .get('/test')
  34. .expect(204)
  35. .end(function(err) {
  36. if (err) return done(err);
  37. done();
  38. });
  39. });
  40. it('composes multiple callbacks/middlware', function(done) {
  41. var app = koa();
  42. app.use(Router(app));
  43. app.get(
  44. '/:category/:title',
  45. function *(next) {
  46. this.status = 500;
  47. yield next;
  48. },
  49. function *(next) {
  50. this.status = 204;
  51. yield next;
  52. }
  53. );
  54. request(http.createServer(app.callback()))
  55. .get('/programming/how-to-node')
  56. .expect(204)
  57. .end(function(err) {
  58. if (err) return done(err);
  59. done();
  60. });
  61. });
  62. describe('Route#match()', function() {
  63. it('captures URL path parameters', function(done) {
  64. var app = koa();
  65. app.use(Router(app));
  66. app.get('/:category/:title', function *(next) {
  67. this.should.have.property('params');
  68. this.params.should.be.type('object');
  69. this.params.should.have.property('category', 'match');
  70. this.params.should.have.property('title', 'this');
  71. this.status = 204;
  72. done();
  73. });
  74. request(http.createServer(app.callback()))
  75. .get('/match/this')
  76. .expect(204)
  77. .end(function(err) {
  78. if (err) return done(err);
  79. });
  80. });
  81. it('return orginal path parameters when decodeURIComponent throw error', function(done) {
  82. var app = koa();
  83. app.use(Router(app));
  84. app.get('/:category/:title', function *(next) {
  85. this.should.have.property('params');
  86. this.params.should.be.type('object');
  87. this.params.should.have.property('category', '100%');
  88. this.params.should.have.property('title', '101%');
  89. this.status = 204;
  90. });
  91. request(http.createServer(app.callback()))
  92. .get('/100%/101%')
  93. .expect(204)
  94. .end(done);
  95. });
  96. it('populates ctx.params with regexp captures', function(done) {
  97. var app = koa();
  98. app.use(Router(app));
  99. app.get(/^\/api\/([^\/]+)\/?/i, function *(next) {
  100. this.should.have.property('params');
  101. this.params.should.be.type('object');
  102. this.params.should.have.property(0, '1');
  103. yield next;
  104. }, function *(next) {
  105. this.should.have.property('params');
  106. this.params.should.be.type('object');
  107. this.params.should.have.property(0, '1');
  108. this.status = 204;
  109. });
  110. request(http.createServer(app.callback()))
  111. .get('/api/1')
  112. .expect(204)
  113. .end(function(err) {
  114. if (err) return done(err);
  115. done();
  116. });
  117. });
  118. it('return orginal ctx.params when decodeURIComponent throw error', function(done) {
  119. var app = koa();
  120. app.use(Router(app));
  121. app.get(/^\/api\/([^\/]+)\/?/i, function *(next) {
  122. this.should.have.property('params');
  123. this.params.should.be.type('object');
  124. this.params.should.have.property(0, '101%');
  125. yield next;
  126. }, function *(next) {
  127. this.should.have.property('params');
  128. this.params.should.be.type('object');
  129. this.params.should.have.property(0, '101%');
  130. this.status = 204;
  131. });
  132. request(http.createServer(app.callback()))
  133. .get('/api/101%')
  134. .expect(204)
  135. .end(function(err) {
  136. if (err) return done(err);
  137. done();
  138. });
  139. });
  140. it('populates ctx.params with regexp captures include undefiend', function(done) {
  141. var app = koa();
  142. app.use(Router(app));
  143. app.get(/^\/api(\/.+)?/i, function *(next) {
  144. this.should.have.property('params');
  145. this.params.should.be.type('object');
  146. this.params.should.have.property(0, undefined);
  147. yield next;
  148. }, function *(next) {
  149. this.should.have.property('params');
  150. this.params.should.be.type('object');
  151. this.params.should.have.property(0, undefined);
  152. this.status = 204;
  153. });
  154. request(http.createServer(app.callback()))
  155. .get('/api')
  156. .expect(204)
  157. .end(function(err) {
  158. if (err) return done(err);
  159. done();
  160. });
  161. });
  162. it('should throw friendly error message when handle not exists', function() {
  163. var app = koa();
  164. app.use(Router(app));
  165. var notexistHandle = undefined;
  166. (function () {
  167. app.get('/foo', notexistHandle);
  168. }).should.throw('get `/foo`: `middleware` must be a function, not `undefined`');
  169. (function () {
  170. app.get('foo router', '/foo', notexistHandle);
  171. }).should.throw('get `foo router`: `middleware` must be a function, not `undefined`');
  172. (function () {
  173. app.post('/foo', function() {}, notexistHandle);
  174. }).should.throw('post `/foo`: `middleware` must be a function, not `undefined`');
  175. });
  176. });
  177. describe('Route#param()', function() {
  178. it('composes middleware for param fn', function(done) {
  179. var app = koa();
  180. var router = new Router();
  181. var route = new Route('/users/:user', ['GET'], [function *(next) {
  182. this.body = this.user;
  183. }]);
  184. route.param('user', function *(id, next) {
  185. this.user = { name: 'alex' };
  186. if (!id) return this.status = 404;
  187. yield next;
  188. });
  189. router.routes.push(route);
  190. app.use(router.middleware());
  191. request(http.createServer(app.callback()))
  192. .get('/users/3')
  193. .expect(200)
  194. .end(function(err, res) {
  195. if (err) return done(err);
  196. res.should.have.property('body');
  197. res.body.should.have.property('name', 'alex');
  198. done();
  199. });
  200. });
  201. it('ignores params which are not matched', function(done) {
  202. var app = koa();
  203. var router = new Router();
  204. var route = new Route('/users/:user', ['GET'], [function *(next) {
  205. this.body = this.user;
  206. }]);
  207. route.param('user', function *(id, next) {
  208. this.user = { name: 'alex' };
  209. if (!id) return this.status = 404;
  210. yield next;
  211. });
  212. route.param('title', function *(id, next) {
  213. this.user = { name: 'mark' };
  214. if (!id) return this.status = 404;
  215. yield next;
  216. });
  217. router.routes.push(route);
  218. app.use(router.middleware());
  219. request(http.createServer(app.callback()))
  220. .get('/users/3')
  221. .expect(200)
  222. .end(function(err, res) {
  223. if (err) return done(err);
  224. res.should.have.property('body');
  225. res.body.should.have.property('name', 'alex');
  226. done();
  227. });
  228. });
  229. });
  230. describe('Route#url()', function() {
  231. it('generates route URL', function() {
  232. var route = new Route('/:category/:title', ['get'], [function* () {}], 'books');
  233. var url = route.url({ category: 'programming', title: 'how-to-node' });
  234. url.should.equal('/programming/how-to-node');
  235. url = route.url('programming', 'how-to-node');
  236. url.should.equal('/programming/how-to-node');
  237. });
  238. it('escapes using encodeURIComponent()', function() {
  239. var route = new Route('/:category/:title', ['get'], [function *() {}], 'books');
  240. var url = route.url({ category: 'programming', title: 'how to node' });
  241. url.should.equal('/programming/how%20to%20node');
  242. });
  243. });
  244. });