Skip to content

Commit

Permalink
fix: Mock EventEmitter (#269)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S committed Mar 21, 2024
1 parent a033d8c commit afdbacb
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/vscode-mock.ts
Expand Up @@ -36,6 +36,7 @@ import {
EndOfLine,
EnvironmentVariableMutatorType,
EvaluatableExpression,
EventEmitter,
ExtensionKind,
ExtensionMode,
FileChangeType,
Expand Down Expand Up @@ -94,7 +95,6 @@ type NotImplemented =
| 'CancellationError'
| 'CancellationTokenSource'
| 'CodeActionTriggerKind'
| 'EventEmitter'
| 'Hover'
| 'InlineValueText'
| 'InlineValueVariableLookup'
Expand Down Expand Up @@ -245,6 +245,7 @@ export function createVSCodeMock(jest: TestFramework): VSCodeMock {
EndOfLine,
EnvironmentVariableMutatorType,
EvaluatableExpression,
EventEmitter,
ExtensionKind,
ExtensionMode,
FileChangeType,
Expand Down
26 changes: 26 additions & 0 deletions src/vscode/EventEmitter.test.ts
@@ -0,0 +1,26 @@
import { describe, expect, jest, test } from '@jest/globals';

import { EventEmitter } from './EventEmitter';

describe('EventEmitter', () => {
test('fire', () => {
const emitter = new EventEmitter<string>();
const listener = jest.fn();
const listener2 = jest.fn();
emitter.event(listener);
emitter.event(listener2);
emitter.fire('foo');
expect(listener).toHaveBeenCalledWith('foo');
expect(listener2).toHaveBeenCalledWith('foo');
});

test('dispose', () => {
const emitter = new EventEmitter<string>();
const listener = jest.fn();
const disposable = emitter.event(listener);
emitter.fire('foo');
disposable.dispose();
emitter.fire('bar');
expect(listener).toHaveBeenCalledWith('foo');
});
});
55 changes: 55 additions & 0 deletions src/vscode/EventEmitter.ts
@@ -0,0 +1,55 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
// eslint-disable-next-line node/no-missing-import
import type * as vscode from 'vscode';

type Disposable = vscode.Disposable;

type Listener<T> = (e: T) => any;

export class EventEmitter<T> implements vscode.EventEmitter<T> {
readonly #listeners = new Set<Listener<T>>();

/**
* 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() {}
}
1 change: 1 addition & 0 deletions src/vscode/index.ts
Expand Up @@ -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';

0 comments on commit afdbacb

Please sign in to comment.