Skip to content

Commit 24128a3

Browse files
nyapatalmeidxkodiakhq[bot]
authored
test: replace jest with vitest (#10472)
* chore: vitest config * feat: vitest * fix: do not actually create ws * chore: config * chore: lockfile * chore: revert downgrade, up node * chore: package - 'git add -A' * chore: delete mock-socket * chore: delete mock-socket * fix: lockfile --------- Co-authored-by: almeidx <[email protected]> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent bb04e09 commit 24128a3

21 files changed

+267
-219
lines changed

packages/voice/__mocks__/ws.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/voice/__tests__/AudioPlayer.test.ts

Lines changed: 56 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Buffer } from 'node:buffer';
55
import { once } from 'node:events';
66
import process from 'node:process';
77
import { Readable } from 'node:stream';
8+
import { describe, test, expect, vitest, type Mock, beforeEach, afterEach } from 'vitest';
89
import { addAudioPlayer, deleteAudioPlayer } from '../src/DataStore';
910
import { VoiceConnection, VoiceConnectionStatus } from '../src/VoiceConnection';
1011
import type { AudioPlayer } from '../src/audio/AudioPlayer';
@@ -13,14 +14,31 @@ import { AudioPlayerError } from '../src/audio/AudioPlayerError';
1314
import { AudioResource } from '../src/audio/AudioResource';
1415
import { NoSubscriberBehavior } from '../src/index';
1516

16-
jest.mock('../src/DataStore');
17-
jest.mock('../src/VoiceConnection');
18-
jest.mock('../src/audio/AudioPlayerError');
17+
vitest.mock('../src/DataStore', () => {
18+
return {
19+
addAudioPlayer: vitest.fn(),
20+
deleteAudioPlayer: vitest.fn(),
21+
};
22+
});
1923

20-
const addAudioPlayerMock = addAudioPlayer as unknown as jest.Mock<typeof addAudioPlayer>;
21-
const deleteAudioPlayerMock = deleteAudioPlayer as unknown as jest.Mock<typeof deleteAudioPlayer>;
22-
const AudioPlayerErrorMock = AudioPlayerError as unknown as jest.Mock<typeof AudioPlayerError>;
23-
const VoiceConnectionMock = VoiceConnection as unknown as jest.Mock<VoiceConnection>;
24+
vitest.mock('../src/VoiceConnection', async (importOriginal) => {
25+
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
26+
const actual = await importOriginal<typeof import('../src/VoiceConnection')>();
27+
const VoiceConnection = vitest.fn();
28+
VoiceConnection.prototype.setSpeaking = vitest.fn();
29+
VoiceConnection.prototype.dispatchAudio = vitest.fn();
30+
VoiceConnection.prototype.prepareAudioPacket = vitest.fn();
31+
return {
32+
...actual,
33+
VoiceConnection,
34+
};
35+
});
36+
37+
vitest.mock('../src/audio/AudioPlayerError', () => {
38+
return {
39+
AudioPlayerError: vitest.fn(),
40+
};
41+
});
2442

2543
function* silence() {
2644
while (true) {
@@ -29,15 +47,15 @@ function* silence() {
2947
}
3048

3149
function createVoiceConnectionMock() {
32-
const connection = new VoiceConnectionMock();
50+
const connection = new VoiceConnection({} as any, {} as any);
3351
connection.state = {
3452
status: VoiceConnectionStatus.Signalling,
3553
adapter: {
36-
sendPayload: jest.fn(),
37-
destroy: jest.fn(),
54+
sendPayload: vitest.fn(),
55+
destroy: vitest.fn(),
3856
},
3957
};
40-
connection.subscribe = jest.fn((player) => player['subscribe'](connection));
58+
connection.subscribe = vitest.fn((player) => player['subscribe'](connection));
4159
return connection;
4260
}
4361

@@ -57,10 +75,7 @@ async function started(resource: AudioResource) {
5775
let player: AudioPlayer | undefined;
5876

5977
beforeEach(() => {
60-
AudioPlayerErrorMock.mockReset();
61-
VoiceConnectionMock.mockReset();
62-
addAudioPlayerMock.mockReset();
63-
deleteAudioPlayerMock.mockReset();
78+
vitest.resetAllMocks();
6479
});
6580

6681
afterEach(() => {
@@ -71,8 +86,8 @@ describe('State transitions', () => {
7186
test('Starts in Idle state', () => {
7287
player = createAudioPlayer();
7388
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
74-
expect(addAudioPlayerMock).toBeCalledTimes(0);
75-
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
89+
expect(addAudioPlayer).toBeCalledTimes(0);
90+
expect(deleteAudioPlayer).toBeCalledTimes(0);
7691
});
7792

7893
test('Playing resource with pausing and resuming', async () => {
@@ -86,11 +101,11 @@ describe('State transitions', () => {
86101
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
87102
expect(player.unpause()).toEqual(false);
88103
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
89-
expect(addAudioPlayerMock).toBeCalledTimes(0);
104+
expect(addAudioPlayer).toBeCalledTimes(0);
90105

91106
player.play(resource);
92107
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
93-
expect(addAudioPlayerMock).toBeCalledTimes(1);
108+
expect(addAudioPlayer).toBeCalledTimes(1);
94109

95110
// Expect pause() to return true and transition to paused state
96111
expect(player.pause()).toEqual(true);
@@ -109,7 +124,7 @@ describe('State transitions', () => {
109124
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
110125

111126
// The audio player should not have been deleted throughout these changes
112-
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
127+
expect(deleteAudioPlayer).toBeCalledTimes(0);
113128
});
114129

115130
test('Playing to Stopping', async () => {
@@ -122,13 +137,13 @@ describe('State transitions', () => {
122137

123138
player.play(resource);
124139
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
125-
expect(addAudioPlayerMock).toBeCalledTimes(1);
126-
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
140+
expect(addAudioPlayer).toBeCalledTimes(1);
141+
expect(deleteAudioPlayer).toBeCalledTimes(0);
127142

128143
expect(player.stop()).toEqual(true);
129144
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
130-
expect(addAudioPlayerMock).toBeCalledTimes(1);
131-
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
145+
expect(addAudioPlayer).toBeCalledTimes(1);
146+
expect(deleteAudioPlayer).toBeCalledTimes(0);
132147
expect(resource.silenceRemaining).toEqual(5);
133148
});
134149

@@ -142,8 +157,8 @@ describe('State transitions', () => {
142157
await started(resource);
143158

144159
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
145-
expect(addAudioPlayerMock).toHaveBeenCalled();
146-
expect(deleteAudioPlayerMock).not.toHaveBeenCalled();
160+
expect(addAudioPlayer).toHaveBeenCalled();
161+
expect(deleteAudioPlayer).not.toHaveBeenCalled();
147162
});
148163

149164
describe('NoSubscriberBehavior transitions', () => {
@@ -188,11 +203,11 @@ describe('State transitions', () => {
188203
player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Stop } });
189204

190205
player.play(resource);
191-
expect(addAudioPlayerMock).toBeCalledTimes(1);
206+
expect(addAudioPlayer).toBeCalledTimes(1);
192207
expect(player.checkPlayable()).toEqual(true);
193208
player['_stepPrepare']();
194209
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
195-
expect(deleteAudioPlayerMock).toBeCalledTimes(1);
210+
expect(deleteAudioPlayer).toBeCalledTimes(1);
196211
});
197212
});
198213

@@ -217,7 +232,7 @@ describe('State transitions', () => {
217232

218233
player.play(resource);
219234
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
220-
expect(addAudioPlayerMock).toBeCalledTimes(1);
235+
expect(addAudioPlayer).toBeCalledTimes(1);
221236
expect(player.checkPlayable()).toEqual(true);
222237

223238
// Run through a few packet cycles
@@ -241,7 +256,8 @@ describe('State transitions', () => {
241256
expect(connection.dispatchAudio).toHaveBeenCalledTimes(6);
242257
await wait();
243258
player['_stepPrepare']();
244-
const prepareAudioPacket = connection.prepareAudioPacket as unknown as jest.Mock<
259+
const prepareAudioPacket = connection.prepareAudioPacket as unknown as Mock<
260+
[Buffer],
245261
typeof connection.prepareAudioPacket
246262
>;
247263
expect(prepareAudioPacket).toHaveBeenCalledTimes(6);
@@ -251,7 +267,7 @@ describe('State transitions', () => {
251267
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
252268
expect(connection.setSpeaking).toBeCalledTimes(1);
253269
expect(connection.setSpeaking).toHaveBeenLastCalledWith(false);
254-
expect(deleteAudioPlayerMock).toHaveBeenCalledTimes(1);
270+
expect(deleteAudioPlayer).toHaveBeenCalledTimes(1);
255271
});
256272

257273
test('stop() causes resource to use silence padding frames', async () => {
@@ -275,7 +291,7 @@ describe('State transitions', () => {
275291

276292
player.play(resource);
277293
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
278-
expect(addAudioPlayerMock).toBeCalledTimes(1);
294+
expect(addAudioPlayer).toBeCalledTimes(1);
279295
expect(player.checkPlayable()).toEqual(true);
280296

281297
player.stop();
@@ -298,15 +314,16 @@ describe('State transitions', () => {
298314

299315
await wait();
300316
expect(player.checkPlayable()).toEqual(false);
301-
const prepareAudioPacket = connection.prepareAudioPacket as unknown as jest.Mock<
317+
const prepareAudioPacket = connection.prepareAudioPacket as unknown as Mock<
318+
[Buffer],
302319
typeof connection.prepareAudioPacket
303320
>;
304321
expect(prepareAudioPacket).toHaveBeenCalledTimes(5);
305322

306323
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
307324
expect(connection.setSpeaking).toBeCalledTimes(1);
308325
expect(connection.setSpeaking).toHaveBeenLastCalledWith(false);
309-
expect(deleteAudioPlayerMock).toHaveBeenCalledTimes(1);
326+
expect(deleteAudioPlayer).toHaveBeenCalledTimes(1);
310327
});
311328

312329
test('Plays silence 5 times for unreadable stream before quitting', async () => {
@@ -328,10 +345,11 @@ describe('State transitions', () => {
328345

329346
player.play(resource);
330347
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
331-
expect(addAudioPlayerMock).toBeCalledTimes(1);
348+
expect(addAudioPlayer).toBeCalledTimes(1);
332349
expect(player.checkPlayable()).toEqual(true);
333350

334-
const prepareAudioPacket = connection.prepareAudioPacket as unknown as jest.Mock<
351+
const prepareAudioPacket = connection.prepareAudioPacket as unknown as Mock<
352+
[Buffer],
335353
typeof connection.prepareAudioPacket
336354
>;
337355

@@ -351,7 +369,7 @@ describe('State transitions', () => {
351369
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
352370
expect(connection.setSpeaking).toBeCalledTimes(1);
353371
expect(connection.setSpeaking).toHaveBeenLastCalledWith(false);
354-
expect(deleteAudioPlayerMock).toHaveBeenCalledTimes(1);
372+
expect(deleteAudioPlayer).toHaveBeenCalledTimes(1);
355373
});
356374

357375
test('checkPlayable() transitions to Idle for unreadable stream', async () => {
@@ -397,6 +415,6 @@ test('Propagates errors from streams', async () => {
397415
const res = await once(player, 'error');
398416
const playerError = res[0] as AudioPlayerError;
399417
expect(playerError).toBeInstanceOf(AudioPlayerError);
400-
expect(AudioPlayerErrorMock).toHaveBeenCalledWith(error, resource);
418+
expect(AudioPlayerError).toHaveBeenCalledWith(error, resource);
401419
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
402420
});

packages/voice/__tests__/AudioReceiveStream.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable no-promise-executor-return */
22
import { Buffer } from 'node:buffer';
3+
import { describe, test, expect } from 'vitest';
34
import { SILENCE_FRAME } from '../src/audio/AudioPlayer';
45
import { AudioReceiveStream, EndBehaviorType } from '../src/receive/AudioReceiveStream';
56

packages/voice/__tests__/AudioResource.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ import { Buffer } from 'node:buffer';
22
import process from 'node:process';
33
import { PassThrough, Readable } from 'node:stream';
44
import { opus, VolumeTransformer } from 'prism-media';
5+
import { describe, test, expect, vitest, type MockedFunction, beforeAll, beforeEach } from 'vitest';
56
import { SILENCE_FRAME } from '../src/audio/AudioPlayer';
67
import { AudioResource, createAudioResource, NO_CONSTRAINT, VOLUME_CONSTRAINT } from '../src/audio/AudioResource';
78
import { findPipeline as _findPipeline, StreamType, TransformerType, type Edge } from '../src/audio/TransformerGraph';
89

9-
jest.mock('prism-media');
10-
jest.mock('../src/audio/TransformerGraph');
10+
vitest.mock('../src/audio/TransformerGraph');
1111

1212
async function wait() {
1313
// eslint-disable-next-line no-promise-executor-return
@@ -22,7 +22,7 @@ async function started(resource: AudioResource) {
2222
return resource;
2323
}
2424

25-
const findPipeline = _findPipeline as unknown as jest.MockedFunction<typeof _findPipeline>;
25+
const findPipeline = _findPipeline as unknown as MockedFunction<typeof _findPipeline>;
2626

2727
beforeAll(() => {
2828
// @ts-expect-error: No type
@@ -37,7 +37,8 @@ beforeAll(() => {
3737
if (constraint === VOLUME_CONSTRAINT) {
3838
base.push({
3939
cost: 1,
40-
transformer: () => new VolumeTransformer({} as any),
40+
// Transformer type shouldn't matter: we are not testing prism-media, but rather the expectation that the stream is VolumeTransformer
41+
transformer: () => new VolumeTransformer({ type: 's16le' } as any),
4142
type: TransformerType.InlineVolume,
4243
});
4344
}
@@ -96,7 +97,8 @@ describe('createAudioResource', () => {
9697
});
9798

9899
test('Infers from VolumeTransformer', () => {
99-
const stream = new VolumeTransformer({} as any);
100+
// Transformer type shouldn't matter: we are not testing prism-media, but rather the expectation that the stream is VolumeTransformer
101+
const stream = new VolumeTransformer({ type: 's16le' } as any);
100102
const resource = createAudioResource(stream, { inlineVolume: true });
101103
expect(findPipeline).toHaveBeenCalledWith(StreamType.Raw, NO_CONSTRAINT);
102104
expect(resource.volume).toEqual(stream);

packages/voice/__tests__/DataStore.test.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
/* eslint-disable @typescript-eslint/dot-notation */
22
import { GatewayOpcodes } from 'discord-api-types/v10';
3+
import { describe, test, expect, vitest, type Mocked, beforeEach } from 'vitest';
34
import * as DataStore from '../src/DataStore';
45
import type { VoiceConnection } from '../src/VoiceConnection';
56
import * as _AudioPlayer from '../src/audio/AudioPlayer';
67

7-
jest.mock('../src/VoiceConnection');
8-
jest.mock('../src/audio/AudioPlayer');
8+
vitest.mock('../src/VoiceConnection');
9+
vitest.mock('../src/audio/AudioPlayer');
910

10-
const AudioPlayer = _AudioPlayer as unknown as jest.Mocked<typeof _AudioPlayer>;
11+
const AudioPlayer = _AudioPlayer as unknown as Mocked<typeof _AudioPlayer>;
1112

1213
function createVoiceConnection(joinConfig: Pick<DataStore.JoinConfig, 'group' | 'guildId'>): VoiceConnection {
1314
return {
@@ -71,8 +72,8 @@ describe('DataStore', () => {
7172
});
7273
test('Managing Audio Players', async () => {
7374
const player = DataStore.addAudioPlayer(new AudioPlayer.AudioPlayer());
74-
const dispatchSpy = jest.spyOn(player as any, '_stepDispatch');
75-
const prepareSpy = jest.spyOn(player as any, '_stepPrepare');
75+
const dispatchSpy = vitest.spyOn(player as any, '_stepDispatch');
76+
const prepareSpy = vitest.spyOn(player as any, '_stepPrepare');
7677
expect(DataStore.hasAudioPlayer(player)).toEqual(true);
7778
expect(DataStore.addAudioPlayer(player)).toEqual(player);
7879
DataStore.deleteAudioPlayer(player);
@@ -87,12 +88,12 @@ describe('DataStore', () => {
8788
test('Preparing Audio Frames', async () => {
8889
// Test functional player
8990
const player2 = DataStore.addAudioPlayer(new AudioPlayer.AudioPlayer());
90-
player2['checkPlayable'] = jest.fn(() => true);
91+
player2['checkPlayable'] = vitest.fn(() => true);
9192
const player3 = DataStore.addAudioPlayer(new AudioPlayer.AudioPlayer());
92-
const dispatchSpy2 = jest.spyOn(player2 as any, '_stepDispatch');
93-
const prepareSpy2 = jest.spyOn(player2 as any, '_stepPrepare');
94-
const dispatchSpy3 = jest.spyOn(player3 as any, '_stepDispatch');
95-
const prepareSpy3 = jest.spyOn(player3 as any, '_stepPrepare');
93+
const dispatchSpy2 = vitest.spyOn(player2 as any, '_stepDispatch');
94+
const prepareSpy2 = vitest.spyOn(player2 as any, '_stepPrepare');
95+
const dispatchSpy3 = vitest.spyOn(player3 as any, '_stepDispatch');
96+
const prepareSpy3 = vitest.spyOn(player3 as any, '_stepPrepare');
9697
await waitForEventLoop();
9798
DataStore.deleteAudioPlayer(player2);
9899
await waitForEventLoop();

packages/voice/__tests__/SSRCMap.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { type EventEmitter, once } from 'node:events';
22
import process from 'node:process';
3+
import { describe, test, expect } from 'vitest';
34
import { SSRCMap, type VoiceUserData } from '../src/receive/SSRCMap';
45

56
async function onceOrThrow<Emitter extends EventEmitter>(target: Emitter, event: string, after: number) {
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import { test, expect, vitest } from 'vitest';
12
import { methods } from '../src/util/Secretbox';
23

3-
jest.mock('tweetnacl');
4+
vitest.mock('tweetnacl');
45

56
test('Does not throw error with a package installed', () => {
6-
// @ts-expect-error: Unknown type
7-
expect(() => methods.open()).not.toThrowError();
7+
// @ts-expect-error We are testing
8+
expect(() => methods.open()).toThrow(TypeError);
89
});

packages/voice/__tests__/SpeakingMap.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { describe, test, expect, vitest } from 'vitest';
12
import { SpeakingMap } from '../src/receive/SpeakingMap';
23
import { noop } from '../src/util/util';
34

4-
jest.useFakeTimers();
5+
vitest.useFakeTimers();
56

67
describe('SpeakingMap', () => {
78
test('Emits start and end', () => {
@@ -17,17 +18,17 @@ describe('SpeakingMap', () => {
1718
for (let index = 0; index < 10; index++) {
1819
speaking.onPacket(userId);
1920
setTimeout(noop, SpeakingMap.DELAY / 2);
20-
jest.advanceTimersToNextTimer();
21+
vitest.advanceTimersToNextTimer();
2122

2223
expect(starts).toEqual([userId]);
2324
expect(ends).toEqual([]);
2425
}
2526

26-
jest.advanceTimersToNextTimer();
27+
vitest.advanceTimersToNextTimer();
2728
expect(ends).toEqual([userId]);
2829

2930
speaking.onPacket(userId);
30-
jest.advanceTimersToNextTimer();
31+
vitest.advanceTimersToNextTimer();
3132
expect(starts).toEqual([userId, userId]);
3233
});
3334
});

packages/voice/__tests__/TransformerGraph.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// @ts-nocheck
2+
import { describe, test, expect } from 'vitest';
23
import { findPipeline, StreamType, TransformerType, type Edge } from '../src/audio/TransformerGraph';
34

45
const noConstraint = () => true;

0 commit comments

Comments
 (0)