diff options
Diffstat (limited to 'remote/test/puppeteer/packages/puppeteer-core/src/util')
4 files changed, 123 insertions, 1 deletions
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/util/Deferred.ts b/remote/test/puppeteer/packages/puppeteer-core/src/util/Deferred.ts index 0dfb013bb3..6699ace36d 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/util/Deferred.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/util/Deferred.ts @@ -1,3 +1,8 @@ +/** + * @license + * Copyright 2024 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ import {TimeoutError} from '../common/Errors.js'; /** diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/util/Mutex.ts b/remote/test/puppeteer/packages/puppeteer-core/src/util/Mutex.ts index 9498bac306..f789837def 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/util/Mutex.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/util/Mutex.ts @@ -1,3 +1,8 @@ +/** + * @license + * Copyright 2024 Google Inc. + * SPDX-License-Identifier: Apache-2.0 + */ import {Deferred} from './Deferred.js'; import {disposeSymbol} from './disposable.js'; diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.test.ts b/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.test.ts index 4cdaf15d5b..bc476b0153 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.test.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.test.ts @@ -9,7 +9,9 @@ import {describe, it} from 'node:test'; import expect from 'expect'; import sinon from 'sinon'; -import {invokeAtMostOnceForArguments} from './decorators.js'; +import {EventEmitter} from '../common/EventEmitter.js'; + +import {bubble, invokeAtMostOnceForArguments} from './decorators.js'; describe('decorators', function () { describe('invokeAtMostOnceForArguments', () => { @@ -76,4 +78,48 @@ describe('decorators', function () { }).toThrow(); }); }); + + describe('bubble', () => { + it('should work', () => { + class Test extends EventEmitter<any> { + @bubble() + accessor field = new EventEmitter(); + } + + const t = new Test(); + let a = false; + t.on('a', (value: boolean) => { + a = value; + }); + + t.field.emit('a', true); + expect(a).toBeTruthy(); + + // Set a new emitter. + t.field = new EventEmitter(); + a = false; + + t.field.emit('a', true); + expect(a).toBeTruthy(); + }); + + it('should not bubble down', () => { + class Test extends EventEmitter<any> { + @bubble() + accessor field = new EventEmitter<any>(); + } + + const t = new Test(); + let a = false; + t.field.on('a', (value: boolean) => { + a = value; + }); + + t.emit('a', true); + expect(a).toBeFalsy(); + + t.field.emit('a', true); + expect(a).toBeTruthy(); + }); + }); }); diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.ts b/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.ts index af21c5fe29..c4dc3b6c0d 100644 --- a/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.ts +++ b/remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.ts @@ -4,6 +4,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +import type {EventType} from '../common/EventEmitter.js'; +import type {EventEmitter} from '../common/EventEmitter.js'; import type {Disposed, Moveable} from '../common/types.js'; import {asyncDisposeSymbol, disposeSymbol} from './disposable.js'; @@ -138,3 +140,67 @@ export function guarded<T extends object>( }; }; } + +const bubbleHandlers = new WeakMap<object, Map<any, any>>(); + +/** + * Event emitter fields marked with `bubble` will have their events bubble up + * the field owner. + */ +// The type is too complicated to type. +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function bubble<T extends EventType[]>(events?: T) { + return <This extends EventEmitter<any>, Value extends EventEmitter<any>>( + {set, get}: ClassAccessorDecoratorTarget<This, Value>, + context: ClassAccessorDecoratorContext<This, Value> + ): ClassAccessorDecoratorResult<This, Value> => { + context.addInitializer(function () { + const handlers = bubbleHandlers.get(this) ?? new Map(); + if (handlers.has(events)) { + return; + } + + const handler = + events !== undefined + ? (type: EventType, event: unknown) => { + if (events.includes(type)) { + this.emit(type, event); + } + } + : (type: EventType, event: unknown) => { + this.emit(type, event); + }; + + handlers.set(events, handler); + bubbleHandlers.set(this, handlers); + }); + return { + set(emitter) { + const handler = bubbleHandlers.get(this)!.get(events)!; + + // In case we are re-setting. + const oldEmitter = get.call(this); + if (oldEmitter !== undefined) { + oldEmitter.off('*', handler); + } + + if (emitter === undefined) { + return; + } + emitter.on('*', handler); + set.call(this, emitter); + }, + // @ts-expect-error -- TypeScript incorrectly types init to require a + // return. + init(emitter) { + if (emitter === undefined) { + return; + } + const handler = bubbleHandlers.get(this)!.get(events)!; + + emitter.on('*', handler); + return emitter; + }, + }; + }; +} |