From da992a527c6f0c6998799ea1616cce5bac28be98 Mon Sep 17 00:00:00 2001 From: Eric Hwang Date: Wed, 5 Jun 2024 15:12:51 -0700 Subject: [PATCH] Add new App event 'page', emitted when a new Page instance is created --- package.json | 1 + src/App.ts | 16 ++++++++-------- src/AppForServer.ts | 1 + test/all/App.mocha.js | 42 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 5febdbfb..540c59f7 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "mocha": "^10.0.0", "prettier": "^3.0.1", "racer": "^v2.0.0-beta.11", + "sinon": "^18.0.0", "ts-node": "^10.9.2", "typescript": "~5.1.3" }, diff --git a/src/App.ts b/src/App.ts index 98b5504b..51ea96b7 100644 --- a/src/App.ts +++ b/src/App.ts @@ -51,14 +51,13 @@ type Routes = [string, string, any][]; * APP EVENTS * 'error', Error - 'pageRendered', Page - 'destroy' - 'model', Model - 'route', Page - 'routeDone', Page, transition: boolean - 'ready', Page - 'load', Page - 'destroyPage', Page + 'model', Model - a new root model was created by the app, server and client + 'page', Page - a new Page was created by the app, server and client + 'ready', Page - app is ready to attach to server-rendered HTML, client only + 'load', Page - app is fully loaded, client only + 'destroyPage', Page - current Page is about to be destroyed, client only + 'route', Page - app is about to invoke a route handler, client only + 'routeDone', Page, routeType - app has finished running a route handler, client only */ export abstract class App extends EventEmitter { @@ -427,6 +426,7 @@ export class AppForClient extends App { const ClientPage = this.Page as unknown as typeof PageForClient; const page = new ClientPage(this, this.model); this.page = page; + this.emit('page', page); return page; } diff --git a/src/AppForServer.ts b/src/AppForServer.ts index 91bdc284..e591ee7e 100644 --- a/src/AppForServer.ts +++ b/src/AppForServer.ts @@ -159,6 +159,7 @@ export class AppForServer extends App { }); page.on('error', next); } + this.emit('page', page); return page; } diff --git a/test/all/App.mocha.js b/test/all/App.mocha.js index 9c877a8f..6c9a5c79 100644 --- a/test/all/App.mocha.js +++ b/test/all/App.mocha.js @@ -1,14 +1,48 @@ -var expect = require('chai').expect; -var AppForClient = require('../../src/App').AppForClient; +const expect = require('chai').expect; +const racer = require('racer'); +const sinon = require('sinon'); +const AppForClient = require('../../src/App').AppForClient; +const { DerbyForClient } = require('../../src/Derby'); +const { DerbyForServer } = require('../../src/DerbyForServer'); + +describe('App', () => { + afterEach(() => { + sinon.restore(); + }); + + [DerbyForClient, DerbyForServer].forEach((DerbyClass) => { + describe(`from ${DerbyClass.name}`, () => { + it('createPage emits \'page\' event with newly created page', () => { + const derby = new DerbyClass(); + // A properly working _init() requires a more complicated setup, + // especially for AppForClient, so stub it out since createPage() + // doesn't depend on anything in _init(). + sinon.stub(derby.App.prototype, '_init'); + + const app = derby.createApp(); + app.model = racer.createModel(); + + let pageFromEvent = null; + app.on('page', (page) => { + pageFromEvent = page; + }); + const page1 = app.createPage({}); + expect(pageFromEvent).to.equal(page1); + const page2 = app.createPage({}); + expect(pageFromEvent).to.equal(page2); + }); + }); + }); +}); describe('App._parseInitialData', () => { it('parses simple json', () => { - var actual = AppForClient._parseInitialData('{"foo": "bar"}'); + const actual = AppForClient._parseInitialData('{"foo": "bar"}'); expect(actual).to.deep.equal({ foo: 'bar' }); }); it('parses escaped json', () => { - var actual = AppForClient._parseInitialData('{"foo": "<\\u0021bar><\\/bar>"}'); + const actual = AppForClient._parseInitialData('{"foo": "<\\u0021bar><\\/bar>"}'); expect(actual).to.deep.equal({ foo: '' }); });