summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/puppeteer-core/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/packages/puppeteer-core/src/util')
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/util/Deferred.ts5
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/util/Mutex.ts5
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.test.ts48
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/util/decorators.ts66
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;
+ },
+ };
+ };
+}