From b7d902cf990ee90181f24e9722f42560858118eb Mon Sep 17 00:00:00 2001 From: killa Date: Sat, 28 Jan 2023 11:07:30 +0800 Subject: [PATCH] feat: impl setGetAppCallback (#152) --- README.md | 22 +++++++++++++++++++ bootstrap.js | 2 +- index.d.ts | 9 ++++++++ index.js | 2 ++ lib/app.js | 1 + lib/app_handler.js | 15 ++++++++++++- lib/inject_context.js | 15 ++++++++----- register.js | 10 ++++----- .../setup-app/config/config.default.js | 5 +++++ test/fixtures/setup-app/package.json | 3 +++ test/fixtures/setup-app/test/.setup.js | 12 ++++++++++ test/fixtures/setup-app/test/index.test.js | 8 +++++++ test/inject_ctx.test.js | 16 ++++++++++++++ 13 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/setup-app/config/config.default.js create mode 100644 test/fixtures/setup-app/package.json create mode 100644 test/fixtures/setup-app/test/.setup.js create mode 100644 test/fixtures/setup-app/test/index.test.js diff --git a/README.md b/README.md index a4c14f1..6e259a3 100644 --- a/README.md +++ b/README.md @@ -500,6 +500,28 @@ describe('test ctx', () => { }); ``` +And if you use mm.app to bootstrap app, you can manually call setGetAppCallback, +then egg-mock will inject ctx for each test case. +```js +// test/.setup.js +const mm = require('egg-mock'); +const path = require('path'); +before(async function() { + const app = this.app = mm.app(); + mm.setGetAppCallback(() => { + return app; + }); + await app.ready(); +}); + + +// test/index.test.js +it('should work', function() { + // eslint-disable-next-line no-undef + assert(this.app.currentContext); +}); +``` + ### env for custom bootstrap EGG_BASE_DIR: the base dir of egg app EGG_FRAMEWORK: the framework of egg app diff --git a/bootstrap.js b/bootstrap.js index 371cf2d..82bfd3a 100644 --- a/bootstrap.js +++ b/bootstrap.js @@ -16,7 +16,7 @@ appHandler.setupApp(); module.exports = { assert, get app() { - return appHandler.getApp(); + return appHandler.getBootstrapApp(); }, mock, mm: mock, diff --git a/index.d.ts b/index.d.ts index 359ecce..7f47f92 100644 --- a/index.d.ts +++ b/index.d.ts @@ -2,6 +2,7 @@ import { Application, Context, EggLogger } from 'egg'; import { MockMate } from 'mm'; import { Test } from 'supertest'; import { MockAgent } from 'urllib'; +import { Suite } from 'mocha'; export { MockAgent }; interface EggTest extends Test { @@ -155,6 +156,14 @@ export interface EggMock extends MockMate { * restore mock */ restore: () => any; + + /** + * If you use mm.app instead of egg-mock/bootstrap to bootstrap app. + * Should manually call setGetAppCallback, + * then egg-mock will inject ctx for each test case + * @param cb + */ + setGetAppCallback: (cb: (suite: Suite) => Promise ) => void; } declare const mm: EggMock; diff --git a/index.js b/index.js index 79baf42..a79b4b8 100644 --- a/index.js +++ b/index.js @@ -75,6 +75,8 @@ Object.assign(mock, mm, { mm(process.env, 'EGG_HOME', homePath); } }, + + setGetAppCallback: require('./lib/app_handler').setGetAppCallback, }); process.setMaxListeners(100); diff --git a/lib/app.js b/lib/app.js index ae10ea3..d3fc40d 100644 --- a/lib/app.js +++ b/lib/app.js @@ -29,6 +29,7 @@ const MOCK_APP_METHOD = [ '_app', 'on', 'once', + 'then', ]; class MockApplication extends EventEmitter { diff --git a/lib/app_handler.js b/lib/app_handler.js index 2323f63..96cb09b 100644 --- a/lib/app_handler.js +++ b/lib/app_handler.js @@ -37,6 +37,19 @@ exports.setupApp = () => { } }; -exports.getApp = () => { +let getAppCallback; + +exports.setGetAppCallback = cb => { + getAppCallback = cb; +}; + +exports.getApp = async suite => { + if (getAppCallback) { + return getAppCallback(suite); + } + return app; +}; + +exports.getBootstrapApp = () => { return app; }; diff --git a/lib/inject_context.js b/lib/inject_context.js index a1bca4a..642d1b8 100644 --- a/lib/inject_context.js +++ b/lib/inject_context.js @@ -5,7 +5,7 @@ const EGG_CONTEXT = Symbol.for('mocha#suite#ctx#eggCtx'); /** * Monkey patch the mocha instance with egg context. * - * @param {Function} mocha + * @param {Function} mocha - */ function injectContext(mocha) { if (!mocha || mocha._injectContextLoaded) { @@ -19,7 +19,7 @@ function injectContext(mocha) { // Inject ctx for before/after. Runner.prototype.runSuite = async function(suite, fn) { debug('run suite: %s %b', suite.title, !!(suite.ctx && suite[EGG_CONTEXT])); - const app = appHandler.getApp(); + const app = await appHandler.getApp(suite); const self = this; if (!app) { return runSuite.call(self, suite, fn); @@ -46,8 +46,8 @@ function injectContext(mocha) { // Inject ctx for beforeEach/it/afterEach. // And ctx with before/after is not same as beforeEach/it/afterEach. - Runner.prototype.runTests = function(suite, fn) { - const app = appHandler.getApp(); + Runner.prototype.runTests = async function(suite, fn) { + const app = await appHandler.getApp(suite); const self = this; if (!app) { return runTests.call(self, suite, fn); @@ -67,6 +67,7 @@ function injectContext(mocha) { } suite.tests = [ test ]; + const app = await appHandler.getApp(suite); await app.ready(); const mockContextFun = app.mockModuleContextScope || app.mockContextScope; try { @@ -80,8 +81,10 @@ function injectContext(mocha) { }); }); }); - } catch (errSuite) { - return done(errSuite); + } catch (err) { + err.message = '[egg-mock] run mock context error: ' + err.message; + console.error(err); + return done(suite); } return next(i + 1); } diff --git a/register.js b/register.js index 6c6dc22..024ecf9 100644 --- a/register.js +++ b/register.js @@ -18,7 +18,7 @@ exports.mochaGlobalTeardown = async () => { exports.mochaHooks = { async beforeAll() { - const app = appHandler.getApp(); + const app = await appHandler.getApp(); debug('mochaHooks.beforeAll call, _app: %s', app); if (app) { @@ -26,7 +26,7 @@ exports.mochaHooks = { } }, async afterEach() { - const app = appHandler.getApp(); + const app = await appHandler.getApp(); debug('mochaHooks.afterEach call, _app: %s', app); if (app) { await app.backgroundTasksFinished(); @@ -40,15 +40,15 @@ exports.mochaHooks = { * * @return {Array} */ -function findNodeJSMocha () { +function findNodeJSMocha() { const children = require.cache || {}; return Object.keys(children) - .filter(function (child) { + .filter(function(child) { const val = children[child].exports; return typeof val === 'function' && val.name === 'Mocha'; }) - .map(function (child) { + .map(function(child) { return children[child].exports; }); } diff --git a/test/fixtures/setup-app/config/config.default.js b/test/fixtures/setup-app/config/config.default.js new file mode 100644 index 0000000..13f1f27 --- /dev/null +++ b/test/fixtures/setup-app/config/config.default.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = { + keys: '123', +}; diff --git a/test/fixtures/setup-app/package.json b/test/fixtures/setup-app/package.json new file mode 100644 index 0000000..fd1f8f6 --- /dev/null +++ b/test/fixtures/setup-app/package.json @@ -0,0 +1,3 @@ +{ + "name": "app" +} diff --git a/test/fixtures/setup-app/test/.setup.js b/test/fixtures/setup-app/test/.setup.js new file mode 100644 index 0000000..abd8acf --- /dev/null +++ b/test/fixtures/setup-app/test/.setup.js @@ -0,0 +1,12 @@ +const mm = require('../../../../index'); +const path = require('path'); +before(async () => { + global.app = mm.app({ + baseDir: path.join(__dirname, '../'), + framework: require.resolve('egg'), + }); + mm.setGetAppCallback(() => { + return global.app; + }); + await global.app.ready(); +}); diff --git a/test/fixtures/setup-app/test/index.test.js b/test/fixtures/setup-app/test/index.test.js new file mode 100644 index 0000000..97e0ff2 --- /dev/null +++ b/test/fixtures/setup-app/test/index.test.js @@ -0,0 +1,8 @@ +const assert = require('assert'); + +describe('test/index.test.ts', () => { + it('should work', () => { + // eslint-disable-next-line no-undef + assert(app.currentContext); + }); +}); diff --git a/test/inject_ctx.test.js b/test/inject_ctx.test.js index 39c22c2..e399fc2 100644 --- a/test/inject_ctx.test.js +++ b/test/inject_ctx.test.js @@ -20,4 +20,20 @@ describe('test/inject_ctx.test.js', () => { .expect('stdout', /9 passing/) .end(); }); + + it('should inject ctx to runner with setGetAppCallback', async () => { + const fixture = path.join(__dirname, 'fixtures/setup-app'); + + await coffee.fork(require.resolve('egg-bin/bin/egg-bin'), [ + 'test', + '-r', require.resolve('../register'), + '--full-trace', + ], { + cwd: fixture, + }) + .debug() + .expect('code', 0) + // .expect('stdout', /9 passing/) + .end(); + }); });