From 72ba3a8cf13ae6174f004fd0283e2281551c163c Mon Sep 17 00:00:00 2001 From: fengmk2 Date: Wed, 22 Jan 2025 23:42:57 +0800 Subject: [PATCH] feat: support custom pathToRegexpModule on app.options (#290) deps on - https://github.com/eggjs/router/pull/18 - https://github.com/eggjs/egg-path-matching/pull/11 --- .../workflows/{nodejs.yml => nodejs-5.x.yml} | 4 +- .github/workflows/release-5.x.yml | 12 + .gitignore | 2 + README.md | 19 +- lib/egg.js | 17 +- lib/loader/mixin/middleware.js | 11 +- package.json | 13 +- .../app/controller/async.js | 9 + .../app/controller/comments.js | 15 + .../app/controller/locals.js | 5 + .../app/controller/members.js | 19 + .../app/controller/middleware.js | 5 + .../app/controller/package.js | 5 + .../app/controller/posts.js | 29 ++ .../app/middleware/async.js | 8 + .../app/middleware/common.js | 9 + .../app/middleware/generator.js | 8 + .../app/middleware/generator_both.js | 10 + .../app/router.js | 34 ++ .../app/views/locals/router.html | 1 + .../config/config.default.js | 9 + .../package.json | 3 + .../router-with-pathToRegexpModule.test.js | 346 ++++++++++++++++++ 23 files changed, 568 insertions(+), 25 deletions(-) rename .github/workflows/{nodejs.yml => nodejs-5.x.yml} (74%) create mode 100644 .github/workflows/release-5.x.yml create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/controller/async.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/controller/comments.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/controller/locals.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/controller/members.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/controller/middleware.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/controller/package.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/controller/posts.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/middleware/async.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/middleware/common.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator_both.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/router.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/app/views/locals/router.html create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/config/config.default.js create mode 100644 test/fixtures/router-app-with-pathToRegexpModule/package.json create mode 100644 test/utils/router-with-pathToRegexpModule.test.js diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs-5.x.yml similarity index 74% rename from .github/workflows/nodejs.yml rename to .github/workflows/nodejs-5.x.yml index b0f6bd0f..8f275748 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs-5.x.yml @@ -1,4 +1,4 @@ -name: CI +name: CI for 5.x on: push: @@ -13,3 +13,5 @@ jobs: uses: node-modules/github-actions/.github/workflows/node-test.yml@master with: version: '14.19.0, 14, 16, 18, 20' + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/release-5.x.yml b/.github/workflows/release-5.x.yml new file mode 100644 index 00000000..79331a82 --- /dev/null +++ b/.github/workflows/release-5.x.yml @@ -0,0 +1,12 @@ +name: Release for 5.x +on: + push: + branches: [ 5.x ] + +jobs: + release: + name: Node.js + uses: eggjs/github-actions/.github/workflows/node-release.yml@master + secrets: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + GIT_TOKEN: ${{ secrets.GIT_TOKEN }} diff --git a/.gitignore b/.gitignore index 537c68f9..7c7b3882 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ test/fixtures/egg/node_modules/egg-core package-lock.json run test/fixtures/*/timing.json +.tshy* +.package-lock.json diff --git a/README.md b/README.md index 6301c108..d1874c1d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # egg-core [![NPM version][npm-image]][npm-url] -[![Node.js CI](https://github.com/eggjs/egg-core/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/egg-core/actions/workflows/nodejs.yml) +[![Node.js CI](https://github.com/eggjs/egg-core/actions/workflows/nodejs-5.x.yml/badge.svg)](https://github.com/eggjs/egg-core/actions/workflows/nodejs-5.x.yml) [![Test coverage][codecov-image]][codecov-url] [![Known Vulnerabilities][snyk-image]][snyk-url] [![npm download][download-image]][download-url] @@ -17,7 +17,7 @@ A core Pluggable framework based on [koa](https://github.com/koajs/koa). -**Don't use it directly, see [egg].** +**Don't use it directly, see [egg](https://github.com/eggjs/egg).** ## Usage @@ -236,6 +236,7 @@ filter | `Function` | a function that filter the exports which can b ## Timing EggCore record boot progress with `Timing`, include: + - Process start time - Script start time(node don't implement an interface like `process.uptime` to record the script start running time, framework can implement a prestart file used with node `--require` options to set `process.scriptTime`) - Application start time @@ -274,18 +275,8 @@ Please open an issue [here](https://github.com/eggjs/egg/issues). [MIT](LICENSE) -[egg]: https://github.com/eggjs/egg - - ## Contributors -|[
popomore](https://github.com/popomore)
|[
dead-horse](https://github.com/dead-horse)
|[
fengmk2](https://github.com/fengmk2)
|[
atian25](https://github.com/atian25)
|[
whxaxes](https://github.com/whxaxes)
|[
gxcsoccer](https://github.com/gxcsoccer)
| -| :---: | :---: | :---: | :---: | :---: | :---: | -|[
killagu](https://github.com/killagu)
|[
iyuq](https://github.com/iyuq)
|[
ngot](https://github.com/ngot)
|[
initial-wu](https://github.com/initial-wu)
|[
waitingsong](https://github.com/waitingsong)
|[
AnzerWall](https://github.com/AnzerWall)
| -|[
army8735](https://github.com/army8735)
|[
njugray](https://github.com/njugray)
|[
JacksonTian](https://github.com/JacksonTian)
|[
JimmyDaddy](https://github.com/JimmyDaddy)
|[
XadillaX](https://github.com/XadillaX)
|[
monkindey](https://github.com/monkindey)
| -|[
mattma](https://github.com/mattma)
|[
shaoshuai0102](https://github.com/shaoshuai0102)
|[
zhang740](https://github.com/zhang740)
|[
dsonet](https://github.com/dsonet)
|[
chenbin92](https://github.com/chenbin92)
|[
hyj1991](https://github.com/hyj1991)
| -[
maxming2333](https://github.com/maxming2333)
|[
supperchong](https://github.com/supperchong)
|[
ZhangDianPeng](https://github.com/ZhangDianPeng)
|[
mosaic101](https://github.com/mosaic101)
- -This project follows the git-contributor [spec](https://github.com/xudafeng/git-contributor), auto updated at `Wed Nov 24 2021 22:24:39 GMT+0800`. +[![Contributors](https://contrib.rocks/image?repo=eggjs/core)](https://github.com/eggjs/core/graphs/contributors) - +Made with [contributors-img](https://contrib.rocks). diff --git a/lib/egg.js b/lib/egg.js index 9db5022d..d0f5b52c 100644 --- a/lib/egg.js +++ b/lib/egg.js @@ -25,11 +25,23 @@ class EggCore extends KoaApplication { * @param {String} [options.baseDir=process.cwd()] - the directory of application * @param {String} [options.type=application|agent] - whether it's running in app worker or agent worker * @param {Object} [options.plugins] - custom plugins + * @param {Object | String} [options.pathToRegexpModule] - custom `path-to-regexp` module, default is `path-to-regexp@1` * @since 1.0.0 */ constructor(options = {}) { options.baseDir = options.baseDir || process.cwd(); options.type = options.type || 'application'; + if (typeof options.pathToRegexpModule === 'string') { + /** + * Usage: + * ```js + * const app = new Application({ + * pathToRegexpModule: '/path/to/path-to-regexp-v8', + * }); + * ``` + */ + options.pathToRegexpModule = require(options.pathToRegexpModule); + } assert(typeof options.baseDir === 'string', 'options.baseDir required, and must be a string'); assert(fs.existsSync(options.baseDir), `Directory ${options.baseDir} not exists`); @@ -301,7 +313,10 @@ class EggCore extends KoaApplication { if (this[ROUTER]) { return this[ROUTER]; } - const router = this[ROUTER] = new Router({ sensitive: true }, this); + const router = this[ROUTER] = new Router({ + sensitive: true, + pathToRegexpModule: this.options.pathToRegexpModule, + }, this); // register router middleware this.beforeStart(() => { this.use(router.middleware()); diff --git a/lib/loader/mixin/middleware.js b/lib/loader/mixin/middleware.js index 361aeae5..247b39fc 100644 --- a/lib/loader/mixin/middleware.js +++ b/lib/loader/mixin/middleware.js @@ -76,7 +76,10 @@ module.exports = { assert(is.function(mw), `Middleware ${name} must be a function, but actual is ${inspect(mw)}`); mw._name = name; // middlewares support options.enable, options.ignore and options.match - mw = wrapMiddleware(mw, options); + mw = wrapMiddleware(mw, { + ...options, + pathToRegexpModule: app.options.pathToRegexpModule, + }); if (mw) { if (debug.enabled) { // show mw debug log on every request @@ -104,9 +107,11 @@ function wrapMiddleware(mw, options) { mw = utils.middleware(mw); // support options.match and options.ignore - if (!options.match && !options.ignore) return mw; - const match = pathMatching(options); + if (!options.match && !options.ignore) { + return mw; + } + const match = pathMatching(options); const fn = (ctx, next) => { if (!match(ctx)) return next(); return mw(ctx, next); diff --git a/package.json b/package.json index 7cdba82d..13f35f37 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "scripts": { "lint": "eslint .", "test": "npm run lint -- --fix && npm run test-local", - "test-local": "egg-bin test -p", + "test-local": "egg-bin test -p --full-trace", "test-single": "egg-bin test", "cov": "egg-bin cov -p", "ci": "npm run lint && npm run cov", @@ -23,7 +23,7 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/eggjs/egg-core.git" + "url": "git+https://github.com/eggjs/core.git" }, "keywords": [ "egg", @@ -34,7 +34,7 @@ "bugs": { "url": "https://github.com/eggjs/egg/issues" }, - "homepage": "https://github.com/eggjs/egg-core#readme", + "homepage": "https://github.com/eggjs/core#readme", "engines": { "node": ">= 14.19.0" }, @@ -53,17 +53,18 @@ "supertest": "^4.0.2", "ts-node": "^10.9.1", "typescript": "^4.9.4", - "urllib": "^3.10.0" + "urllib": "^3.10.0", + "path-to-regexp-v8": "npm:path-to-regexp@8" }, "dependencies": { - "@eggjs/router": "^2.0.0", + "@eggjs/router": "^2.2.0", "@types/depd": "^1.1.32", "@types/koa": "^2.13.5", "co": "^4.6.0", "debug": "^4.1.1", "depd": "^2.0.0", "egg-logger": "^3.1.0", - "egg-path-matching": "^1.0.1", + "egg-path-matching": "^1.2.0", "extend2": "^1.0.0", "gals": "^1.0.1", "get-ready": "^2.0.1", diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/controller/async.js b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/async.js new file mode 100644 index 00000000..68692fa7 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/async.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = app => { + return class AsyncController extends app.Controller { + async index() { + this.ctx.body.push('async'); + } + } +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/controller/comments.js b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/comments.js new file mode 100644 index 00000000..8b8d928b --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/comments.js @@ -0,0 +1,15 @@ +'use strict'; + +// 测试 app.resources 遇到 controller 没有足够的 action 的场景 + +exports.index = function* () { + this.body = 'index'; +}; + +exports.new = function* () { + this.body = 'new'; +}; + +exports.show = function* () { + this.body = 'show - ' + this.params.id; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/controller/locals.js b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/locals.js new file mode 100644 index 00000000..cced36fe --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/locals.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.router = function* () { + yield this.render('locals/router.html'); +}; \ No newline at end of file diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/controller/members.js b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/members.js new file mode 100644 index 00000000..013ceb8d --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/members.js @@ -0,0 +1,19 @@ +'use strict'; + +// 测试 app.resources 遇到 controller 没有足够的 action 的场景 + +exports.index = function* () { + this.body = 'index'; +}; + +exports.new = function* () { + this.body = 'new'; +}; + +exports.show = function* () { + this.body = 'show - ' + this.params.id; +}; + +exports.delete = function* () { + this.body = `delete - ${this.params.id}`; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/controller/middleware.js b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/middleware.js new file mode 100644 index 00000000..d96c1c5b --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/middleware.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function* () { + this.body = []; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/controller/package.js b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/package.js new file mode 100644 index 00000000..b5b4fd64 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/package.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.get = function* () { + this.body = this.params[0]; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/controller/posts.js b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/posts.js new file mode 100644 index 00000000..6e140350 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/controller/posts.js @@ -0,0 +1,29 @@ +'use strict'; + +exports.index = function* () { + this.body = 'index'; +}; + +exports.new = function* () { + this.body = 'new'; +}; + +exports.create = function* () { + this.body = 'create'; +}; + +exports.show = function* () { + this.body = 'show - ' + this.params.id; +}; + +exports.edit = function* () { + this.body = 'edit - ' + this.params.id; +}; + +exports.update = function* () { + this.body = 'update - ' + this.params.id; +}; + +exports.destroy = function* () { + this.body = 'destroy - ' + this.params.id; +}; \ No newline at end of file diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/async.js b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/async.js new file mode 100644 index 00000000..fe2af8e5 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/async.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function() { + return async (ctx, next) => { + await next(); + ctx.body.push('async'); + }; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/common.js b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/common.js new file mode 100644 index 00000000..0a53e7d0 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/common.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = function() { + return function(ctx, next) { + return next().then(() => { + ctx.body.push('common'); + }); + }; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator.js b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator.js new file mode 100644 index 00000000..957e26f0 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function() { + return function*(next) { + yield next; + this.body.push('generator'); + }; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator_both.js b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator_both.js new file mode 100644 index 00000000..3264b22b --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/middleware/generator_both.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = function() { + return function*(next) { + this.body = []; + this.body.push('generator before'); + yield next; + this.body.push('generator after'); + }; +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/router.js b/test/fixtures/router-app-with-pathToRegexpModule/app/router.js new file mode 100644 index 00000000..49130821 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/router.js @@ -0,0 +1,34 @@ +module.exports = function (app) { + const common = app.middlewares.common(); + const asyncMw = app.middlewares.async(); + const generator = app.middlewares.generator(); + const generatorBoth = app.middlewares.generatorBoth(); + + app + .get('/locals/router', app.controller.locals.router) + .get('/members/index', 'members.index') + .delete('/members/delete/:id', 'members.delete') + .del('/members/del/:id', 'members.delete') + .resources('posts', '/posts', 'posts') + .resources('members', '/members', app.controller.members) + .resources('/comments', app.controller.comments) + // not work on path-to-regexp@8 + // .get('comment_index', '/comments/:id?filter=', app.controller.comments.index) + .get('params', '/params/:a/:b', app.controller.locals.router) + .get('/middleware', common, asyncMw, generator, 'middleware') + .get('middleware', '/named_middleware', common, asyncMw, generator, 'middleware') + .get('/mix', generatorBoth , 'async.index') + .register('/comments', [ 'post' ] , app.controller.comments.new) + .register('/register_middleware', [ 'get' ], [ common, asyncMw, generator, 'middleware' ]) + .redirect('/redirect', '/middleware', 302); + + app.router + .get('/router_middleware', common, asyncMw, generator, 'middleware') + .redirect('/router_redirect', '/middleware'); + + // not work on path-to-regexp@8 + // app.get('packages', /^\/packages\/(.*)/, 'package.get'); + + app.get([ '/url1', '/url2' ], 'members.index') + app.get([ '/urlm1', '/urlm2' ], common, asyncMw, generator, 'middleware') +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/app/views/locals/router.html b/test/fixtures/router-app-with-pathToRegexpModule/app/views/locals/router.html new file mode 100644 index 00000000..0006527d --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/app/views/locals/router.html @@ -0,0 +1 @@ +posts: /posts \ No newline at end of file diff --git a/test/fixtures/router-app-with-pathToRegexpModule/config/config.default.js b/test/fixtures/router-app-with-pathToRegexpModule/config/config.default.js new file mode 100644 index 00000000..166a3213 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/config/config.default.js @@ -0,0 +1,9 @@ +'use strict'; + +exports.keys = 'my'; + +exports.security = { + csrf: { + enable: false, + }, +}; diff --git a/test/fixtures/router-app-with-pathToRegexpModule/package.json b/test/fixtures/router-app-with-pathToRegexpModule/package.json new file mode 100644 index 00000000..85a85027 --- /dev/null +++ b/test/fixtures/router-app-with-pathToRegexpModule/package.json @@ -0,0 +1,3 @@ +{ + "name": "router-app-with-pathToRegexpModule" +} diff --git a/test/utils/router-with-pathToRegexpModule.test.js b/test/utils/router-with-pathToRegexpModule.test.js new file mode 100644 index 00000000..79d302f1 --- /dev/null +++ b/test/utils/router-with-pathToRegexpModule.test.js @@ -0,0 +1,346 @@ +const assert = require('assert'); +const request = require('supertest'); +const utils = require('../utils'); + +describe('test/utils/router-with-pathToRegexpModule.test.js', () => { + let app; + before(() => { + app = utils.createApp('router-app-with-pathToRegexpModule', { + pathToRegexpModule: require.resolve('path-to-regexp-v8'), + }); + app.loader.loadAll(); + return app.ready(); + }); + after(() => app.close()); + + describe('router.resources', () => { + describe('normal', () => { + it('should GET /posts', () => { + return request(app.callback()) + .get('/posts') + .expect(200) + .expect('index'); + }); + + it('should GET /posts/new', () => { + return request(app.callback()) + .get('/posts/new') + .expect(200) + .expect('new'); + }); + + it('should POST /posts', () => { + return request(app.callback()) + .post('/posts') + .expect(200) + .expect('create'); + }); + + it('should GET /posts/:id', () => { + return request(app.callback()) + .get('/posts/123') + .expect(200) + .expect('show - 123'); + }); + + it('should GET /posts/:id/edit', () => { + return request(app.callback()) + .get('/posts/123/edit') + .expect(200) + .expect('edit - 123'); + }); + + it('should PATCH /posts/:id', () => { + return request(app.callback()) + .patch('/posts/123') + .expect(200) + .expect('update - 123'); + }); + + it('should PUT /posts/:id', () => { + return request(app.callback()) + .put('/posts/123') + .expect(200) + .expect('update - 123'); + }); + + it('should DELETE /posts/:id', () => { + return request(app.callback()) + .delete('/posts/123') + .expect(200) + .expect('destroy - 123'); + }); + }); + + describe('controller url', () => { + it('should GET /members', () => { + return request(app.callback()) + .get('/members') + .expect(200) + .expect('index'); + }); + + it('should GET /members/index', () => { + return request(app.callback()) + .get('/members/index') + .expect(200) + .expect('index'); + }); + + it('should GET /members/new', () => { + return request(app.callback()) + .get('/members/new') + .expect(200) + .expect('new'); + }); + + it('should GET /members/:id', () => { + return request(app.callback()) + .get('/members/1231') + .expect(200) + .expect('show - 1231'); + }); + + it('should POST /members', () => { + return request(app.callback()) + .post('/members') + .expect(404); + }); + + it('should PUT /members/:id', () => { + return request(app.callback()) + .put('/members/1231') + .expect(404); + }); + + // not work on path-to-regexp@8 + it.skip('should GET /POSTS', () => { + return request(app.callback()) + .get('/POSTS') + .expect(404); + }); + + it('should GET /members/delete/:id', () => { + return request(app.callback()) + .delete('/members/delete/1') + .expect(200) + .expect('delete - 1'); + }); + + it('should GET /members/del/:id', () => { + return request(app.callback()) + .del('/members/del/1') + .expect(200) + .expect('delete - 1'); + }); + + // not work on path-to-regexp@8 + it.skip('should GET /packages/(.*)', () => { + return request(app.callback()) + .get('/packages/urllib') + .expect('urllib'); + }); + }); + + describe('no name', () => { + it('should GET /comments', () => { + return request(app.callback()) + .get('/comments') + .expect('index') + .expect(200); + }); + + it('should POST /comments', () => { + return request(app.callback()) + .post('/comments') + .expect('new') + .expect(200); + }); + }); + + describe('async controller', () => { + it('should execute by the correct order', () => { + return request(app.callback()) + .get('/mix') + .expect([ 'generator before', 'async', 'generator after' ]) + .expect(200); + }); + }); + }); + + describe('router.url', () => { + it('should work', () => { + assert(app.url('posts') === '/posts'); + + assert(app.router.url('posts') === '/posts'); + assert(app.router.url('members') === '/members'); + assert(app.router.url('post', { id: 1 }) === '/posts/1'); + assert(app.router.url('member', { id: 1 }) === '/members/1'); + assert(app.router.url('new_post') === '/posts/new'); + assert(app.router.url('new_member') === '/members/new'); + assert(app.router.url('edit_post', { id: 1 }) === '/posts/1/edit'); + assert(app.router.url('params', { a: 1, b: 2 }) === '/params/1/2'); + // no match params + assert(app.router.url('edit_post', {}) === '/posts/:id/edit'); + assert(app.router.url('noname') === ''); + // assert(app.router.url('comment_index', { id: 1, a: 1 }) === '/comments/1?filter=&a=1'); + }); + + it('should work with unknow params', () => { + assert(app.router.url('posts', { name: 'foo', page: 2 }) === '/posts?name=foo&page=2'); + assert(app.router.url('posts', { name: 'foo&?', page: 2 }) === '/posts?name=foo%26%3F&page=2'); + assert(app.router.url('edit_post', { id: 10, page: 2 }) === '/posts/10/edit?page=2'); + assert(app.router.url('edit_post', { i: 2, id: 10 }) === '/posts/10/edit?i=2'); + assert(app.router.url('edit_post', { id: 10, page: 2, tags: [ 'chair', 'develop' ] }) === + '/posts/10/edit?page=2&tags=chair&tags=develop'); + assert(app.router.url('edit_post', { id: [ 10 ], page: [ 2 ], tags: [ 'chair', 'develop' ] }) === + '/posts/10/edit?page=2&tags=chair&tags=develop'); + assert(app.router.url('edit_post', { id: [ 10, 11 ], page: [ 2 ], tags: [ 'chair', 'develop' ] }) === + '/posts/10/edit?page=2&tags=chair&tags=develop'); + }); + + it.skip('should not support regular url', () => { + assert.throws(() => { + app.router.url('packages', [ 'urllib' ]); + }, 'Can\'t get the url for regExp /^\/packages\/(.*)/ for by name \'posts\''); + }); + }); + + describe('router.pathFor', () => { + it('should work', () => { + assert(app.router.pathFor('posts') === '/posts'); + }); + }); + + describe('router.method', () => { + it('router method include HEAD', () => { + assert(app.router.methods.includes('HEAD')); + }); + }); + + describe('router middleware', () => { + it('should support all kinds of middlewares', () => { + return request(app.callback()) + .get('/middleware') + .expect(200) + .expect([ 'generator', 'async', 'common' ]); + }); + + it('should support all kinds of middlewares with name', () => { + return request(app.callback()) + .get('/named_middleware') + .expect(200) + .expect([ 'generator', 'async', 'common' ]); + }); + + it('should support all kinds of middlewares with register', () => { + return request(app.callback()) + .get('/register_middleware') + .expect(200) + .expect([ 'generator', 'async', 'common' ]); + }); + + it('should app.router support all kinds of middlewares', () => { + return request(app.callback()) + .get('/router_middleware') + .expect(200) + .expect([ 'generator', 'async', 'common' ]); + }); + }); + + describe('redirect', () => { + it('should app.redirect to target', () => { + return request(app.callback()) + .get('/redirect') + .expect(302) + .expect('location', '/middleware'); + }); + + it('should app.router.redirect to target', () => { + return request(app.callback()) + .get('/router_redirect') + .expect(301) + .expect('location', '/middleware'); + }); + }); + + describe('controller mutli url', () => { + it('should GET /url1', () => { + return request(app.callback()) + .get('/url1') + .expect(200) + .expect('index'); + }); + it('should GET /url2', () => { + return request(app.callback()) + .get('/url2') + .expect(200) + .expect('index'); + }); + it('use middlewares /urlm1', () => { + return request(app.callback()) + .get('/urlm1') + .expect(200) + .expect([ 'generator', 'async', 'common' ]); + }); + it('use middlewares /urlm2', () => { + return request(app.callback()) + .get('/urlm2') + .expect(200) + .expect([ 'generator', 'async', 'common' ]); + }); + }); + + describe('controller not exist', () => { + it('should check when app.router.VERB', () => { + try { + app.router.get('/test', app.controller.not_exist); + throw new Error('should not run here'); + } catch (err) { + assert(err.message.includes('controller not exists')); + } + }); + + it('should check when app.router.VERB with controller string', () => { + try { + app.get('/hello', 'not.exist.controller'); + throw new Error('should not run here'); + } catch (err) { + assert(err.message.includes('controller \'not.exist.controller\' not exists')); + } + }); + + it('should check when app.router.resources', () => { + try { + app.router.resources('/test', app.controller.not_exist); + throw new Error('should not run here'); + } catch (err) { + assert(err.message.includes('controller not exists')); + } + }); + + it('should check when app.router.resources with controller string', () => { + try { + app.router.resources('/test', 'not.exist.controller'); + throw new Error('should not run here'); + } catch (err) { + assert(err.message.includes('controller \'not.exist.controller\' not exists')); + } + }); + }); + + describe('router middleware', () => { + before(() => { + app = utils.createApp('router-in-app'); + app.loader.loadAll(); + return app.ready(); + }); + + it('should always load router middleware at last', () => { + return request(app.callback()) + .get('/') + .expect(200) + .expect('foo'); + }); + }); +});