Skip to content

Commit 2a9d906

Browse files
committed
refactor: add TargetEventEmitter abstraction for Browser/BrowserContext
Extract a TargetEventEmitter interface and asTargetEmitter() helper to unify Browser and BrowserContext target event handling. This enables PageCollector and UniverseManager to work with both types without type-specific branching.
1 parent 0c4f211 commit 2a9d906

5 files changed

Lines changed: 94 additions & 48 deletions

File tree

package-lock.json

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/DevtoolsUtils.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import {PuppeteerDevToolsConnection} from './DevToolsConnectionAdapter.js';
88
import {Mutex} from './Mutex.js';
9+
import type {TargetEventEmitter} from './PageCollector.js';
910
import {DevTools} from './third_party/index.js';
1011
import type {
1112
Browser,
@@ -108,15 +109,15 @@ export interface TargetUniverse {
108109
export type TargetUniverseFactoryFn = (page: Page) => Promise<TargetUniverse>;
109110

110111
export class UniverseManager {
111-
readonly #browser: Browser;
112+
readonly #browser: TargetEventEmitter;
112113
readonly #createUniverseFor: TargetUniverseFactoryFn;
113114
readonly #universes = new WeakMap<Page, TargetUniverse>();
114115

115116
/** Guard access to #universes so we don't create unnecessary universes */
116117
readonly #mutex = new Mutex();
117118

118119
constructor(
119-
browser: Browser,
120+
browser: TargetEventEmitter,
120121
factory: TargetUniverseFactoryFn = DEFAULT_FACTORY,
121122
) {
122123
this.#browser = browser;

src/PageCollector.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,32 @@ import type {
1515
import {DevTools} from './third_party/index.js';
1616
import {
1717
type Browser,
18+
type BrowserContext,
1819
type Frame,
1920
type Handler,
2021
type HTTPRequest,
2122
type Page,
2223
type PageEvents as PuppeteerPageEvents,
2324
} from './third_party/index.js';
2425

26+
/**
27+
* Common interface for Browser and BrowserContext target events.
28+
* Both Browser and BrowserContext structurally satisfy this interface,
29+
* enabling PageCollector and UniverseManager to work with either.
30+
*/
31+
export interface TargetEventEmitter {
32+
on(type: 'targetcreated', handler: (target: Target) => void): unknown;
33+
on(type: 'targetdestroyed', handler: (target: Target) => void): unknown;
34+
off(type: 'targetcreated', handler: (target: Target) => void): unknown;
35+
off(type: 'targetdestroyed', handler: (target: Target) => void): unknown;
36+
}
37+
38+
export function asTargetEmitter(
39+
source: Browser | BrowserContext | TargetEventEmitter,
40+
): TargetEventEmitter {
41+
return source as unknown as TargetEventEmitter;
42+
}
43+
2544
export class UncaughtError {
2645
readonly details: Protocol.Runtime.ExceptionDetails;
2746
readonly targetId: string;
@@ -57,7 +76,7 @@ type WithSymbolId<T> = T & {
5776
};
5877

5978
export class PageCollector<T> {
60-
#browser: Browser;
79+
#browser: TargetEventEmitter;
6180
#listenersInitializer: (
6281
collector: (item: T) => void,
6382
) => ListenerMap<PageEvents>;
@@ -72,7 +91,7 @@ export class PageCollector<T> {
7291
protected storage = new WeakMap<Page, Array<Array<WithSymbolId<T>>>>();
7392

7493
constructor(
75-
browser: Browser,
94+
browser: TargetEventEmitter,
7695
listeners: (collector: (item: T) => void) => ListenerMap<PageEvents>,
7796
) {
7897
this.#browser = browser;
@@ -373,7 +392,7 @@ class PageEventSubscriber {
373392

374393
export class NetworkCollector extends PageCollector<HTTPRequest> {
375394
constructor(
376-
browser: Browser,
395+
browser: TargetEventEmitter,
377396
listeners: (
378397
collector: (item: HTTPRequest) => void,
379398
) => ListenerMap<PageEvents> = collect => {

tests/DevtoolsUtils.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
urlsEqual,
1515
UniverseManager,
1616
} from '../src/DevtoolsUtils.js';
17+
import {asTargetEmitter} from '../src/PageCollector.js';
1718
import {DevTools} from '../src/third_party/index.js';
1819
import type {Browser, Target} from '../src/third_party/index.js';
1920

@@ -102,7 +103,7 @@ describe('UniverseManager', () => {
102103
it('calls the factory for existing pages', async () => {
103104
const browser = getMockBrowser();
104105
const factory = sinon.stub().resolves({});
105-
const manager = new UniverseManager(browser, factory);
106+
const manager = new UniverseManager(asTargetEmitter(browser), factory);
106107
await manager.init(await browser.pages());
107108

108109
const page = (await browser.pages())[0];
@@ -115,7 +116,7 @@ describe('UniverseManager', () => {
115116
} as unknown as Browser;
116117
// eslint-disable-next-line @typescript-eslint/no-empty-function
117118
const factory = sinon.stub().returns(new Promise(() => {})); // Don't resolve.
118-
const manager = new UniverseManager(browser, factory);
119+
const manager = new UniverseManager(asTargetEmitter(browser), factory);
119120
await manager.init([]);
120121

121122
sinon.assert.notCalled(factory);
@@ -135,7 +136,7 @@ describe('UniverseManager', () => {
135136

136137
it('works with a real browser', async () => {
137138
await withBrowser(async (browser, page) => {
138-
const manager = new UniverseManager(browser);
139+
const manager = new UniverseManager(asTargetEmitter(browser));
139140
await manager.init([page]);
140141

141142
assert.notStrictEqual(manager.get(page), null);
@@ -144,7 +145,7 @@ describe('UniverseManager', () => {
144145

145146
it('ignores pauses', async () => {
146147
await withBrowser(async (browser, page) => {
147-
const manager = new UniverseManager(browser);
148+
const manager = new UniverseManager(asTargetEmitter(browser));
148149
await manager.init([page]);
149150
const targetUniverse = manager.get(page);
150151
assert.ok(targetUniverse);

0 commit comments

Comments
 (0)