From 53824a16e5bc626572f7352f7c9d8aab5292bd5f Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Thu, 21 Mar 2024 17:23:07 +0100 Subject: [PATCH 1/2] fix: Mock EventEmitter --- src/vscode-mock.ts | 3 +- src/vscode/EventEmitter.test.ts | 28 +++++++++++++++++ src/vscode/EventEmitter.ts | 53 +++++++++++++++++++++++++++++++++ src/vscode/index.ts | 1 + 4 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/vscode/EventEmitter.test.ts create mode 100644 src/vscode/EventEmitter.ts diff --git a/src/vscode-mock.ts b/src/vscode-mock.ts index c0ca4b2..3b67f91 100644 --- a/src/vscode-mock.ts +++ b/src/vscode-mock.ts @@ -36,6 +36,7 @@ import { EndOfLine, EnvironmentVariableMutatorType, EvaluatableExpression, + EventEmitter, ExtensionKind, ExtensionMode, FileChangeType, @@ -94,7 +95,6 @@ type NotImplemented = | 'CancellationError' | 'CancellationTokenSource' | 'CodeActionTriggerKind' - | 'EventEmitter' | 'Hover' | 'InlineValueText' | 'InlineValueVariableLookup' @@ -245,6 +245,7 @@ export function createVSCodeMock(jest: TestFramework): VSCodeMock { EndOfLine, EnvironmentVariableMutatorType, EvaluatableExpression, + EventEmitter, ExtensionKind, ExtensionMode, FileChangeType, diff --git a/src/vscode/EventEmitter.test.ts b/src/vscode/EventEmitter.test.ts new file mode 100644 index 0000000..b3aaa1d --- /dev/null +++ b/src/vscode/EventEmitter.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, jest, test } from '@jest/globals'; +// eslint-disable-next-line node/no-missing-import +import type * as vscode from 'vscode'; + +import { EventEmitter } from './EventEmitter'; + +describe('EventEmitter', () => { + test('fire', () => { + const emitter = new EventEmitter(); + const listener = jest.fn(); + const listener2 = jest.fn(); + emitter.event(listener); + emitter.event(listener2); + emitter.fire('foo'); + expect(listener).toBeCalledWith('foo'); + expect(listener2).toBeCalledWith('foo'); + }); + + test('dispose', () => { + const emitter = new EventEmitter(); + const listener = jest.fn(); + const disposable = emitter.event(listener); + emitter.fire('foo'); + disposable.dispose(); + emitter.fire('bar'); + expect(listener).toBeCalledWith('foo'); + }); +}); diff --git a/src/vscode/EventEmitter.ts b/src/vscode/EventEmitter.ts new file mode 100644 index 0000000..4cd858c --- /dev/null +++ b/src/vscode/EventEmitter.ts @@ -0,0 +1,53 @@ +// eslint-disable-next-line node/no-missing-import +import type * as vscode from 'vscode'; +import type { Disposable } from 'vscode'; + +type Listener = (e: T) => any; + +export class EventEmitter implements vscode.EventEmitter { + readonly #listeners = new Set>(); + + /** + * The event listeners can subscribe to. + */ + event(listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable { + const fn = thisArgs ? listener.bind(thisArgs) : listener; + this.#listeners.add(fn); + const disposable = { + dispose: () => { + this.#listeners.delete(fn); + }, + }; + + if (disposables) { + disposables.push(disposable); + } + + return disposable; + } + + /** + * Notify all subscribers of the {@link EventEmitter.event event}. Failure + * of one or more listener will not fail this function call. + * + * @param data The event object. + */ + fire(data: T): void { + for (const listener of this.#listeners) { + try { + listener(data); + } catch { + // ignore + } + } + } + + /** + * Dispose this object and free resources. + */ + dispose(): void { + this.#listeners.clear(); + } + + constructor() {} +} diff --git a/src/vscode/index.ts b/src/vscode/index.ts index a0f00c5..67a5064 100644 --- a/src/vscode/index.ts +++ b/src/vscode/index.ts @@ -21,3 +21,4 @@ export * from './uri'; export { createWindow } from './window'; export type { Window } from './window'; export { MockWorkspace, Workspace, createWorkspace } from './workspace'; +export { EventEmitter } from './EventEmitter'; From a3883c36d4413b1236bc53ac806b8ea4ce7d4f4b Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Thu, 21 Mar 2024 17:28:00 +0100 Subject: [PATCH 2/2] lint --- src/vscode/EventEmitter.test.ts | 8 +++----- src/vscode/EventEmitter.ts | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vscode/EventEmitter.test.ts b/src/vscode/EventEmitter.test.ts index b3aaa1d..ab9052e 100644 --- a/src/vscode/EventEmitter.test.ts +++ b/src/vscode/EventEmitter.test.ts @@ -1,6 +1,4 @@ import { describe, expect, jest, test } from '@jest/globals'; -// eslint-disable-next-line node/no-missing-import -import type * as vscode from 'vscode'; import { EventEmitter } from './EventEmitter'; @@ -12,8 +10,8 @@ describe('EventEmitter', () => { emitter.event(listener); emitter.event(listener2); emitter.fire('foo'); - expect(listener).toBeCalledWith('foo'); - expect(listener2).toBeCalledWith('foo'); + expect(listener).toHaveBeenCalledWith('foo'); + expect(listener2).toHaveBeenCalledWith('foo'); }); test('dispose', () => { @@ -23,6 +21,6 @@ describe('EventEmitter', () => { emitter.fire('foo'); disposable.dispose(); emitter.fire('bar'); - expect(listener).toBeCalledWith('foo'); + expect(listener).toHaveBeenCalledWith('foo'); }); }); diff --git a/src/vscode/EventEmitter.ts b/src/vscode/EventEmitter.ts index 4cd858c..92dfda7 100644 --- a/src/vscode/EventEmitter.ts +++ b/src/vscode/EventEmitter.ts @@ -1,6 +1,8 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ // eslint-disable-next-line node/no-missing-import import type * as vscode from 'vscode'; -import type { Disposable } from 'vscode'; + +type Disposable = vscode.Disposable; type Listener = (e: T) => any;