mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-09 16:13:31 +01:00
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 <github@almeidx.dev> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
@@ -1 +0,0 @@
|
||||
export { WebSocket as default } from 'mock-socket';
|
||||
@@ -5,6 +5,7 @@ import { Buffer } from 'node:buffer';
|
||||
import { once } from 'node:events';
|
||||
import process from 'node:process';
|
||||
import { Readable } from 'node:stream';
|
||||
import { describe, test, expect, vitest, type Mock, beforeEach, afterEach } from 'vitest';
|
||||
import { addAudioPlayer, deleteAudioPlayer } from '../src/DataStore';
|
||||
import { VoiceConnection, VoiceConnectionStatus } from '../src/VoiceConnection';
|
||||
import type { AudioPlayer } from '../src/audio/AudioPlayer';
|
||||
@@ -13,14 +14,31 @@ import { AudioPlayerError } from '../src/audio/AudioPlayerError';
|
||||
import { AudioResource } from '../src/audio/AudioResource';
|
||||
import { NoSubscriberBehavior } from '../src/index';
|
||||
|
||||
jest.mock('../src/DataStore');
|
||||
jest.mock('../src/VoiceConnection');
|
||||
jest.mock('../src/audio/AudioPlayerError');
|
||||
vitest.mock('../src/DataStore', () => {
|
||||
return {
|
||||
addAudioPlayer: vitest.fn(),
|
||||
deleteAudioPlayer: vitest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const addAudioPlayerMock = addAudioPlayer as unknown as jest.Mock<typeof addAudioPlayer>;
|
||||
const deleteAudioPlayerMock = deleteAudioPlayer as unknown as jest.Mock<typeof deleteAudioPlayer>;
|
||||
const AudioPlayerErrorMock = AudioPlayerError as unknown as jest.Mock<typeof AudioPlayerError>;
|
||||
const VoiceConnectionMock = VoiceConnection as unknown as jest.Mock<VoiceConnection>;
|
||||
vitest.mock('../src/VoiceConnection', async (importOriginal) => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
const actual = await importOriginal<typeof import('../src/VoiceConnection')>();
|
||||
const VoiceConnection = vitest.fn();
|
||||
VoiceConnection.prototype.setSpeaking = vitest.fn();
|
||||
VoiceConnection.prototype.dispatchAudio = vitest.fn();
|
||||
VoiceConnection.prototype.prepareAudioPacket = vitest.fn();
|
||||
return {
|
||||
...actual,
|
||||
VoiceConnection,
|
||||
};
|
||||
});
|
||||
|
||||
vitest.mock('../src/audio/AudioPlayerError', () => {
|
||||
return {
|
||||
AudioPlayerError: vitest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
function* silence() {
|
||||
while (true) {
|
||||
@@ -29,15 +47,15 @@ function* silence() {
|
||||
}
|
||||
|
||||
function createVoiceConnectionMock() {
|
||||
const connection = new VoiceConnectionMock();
|
||||
const connection = new VoiceConnection({} as any, {} as any);
|
||||
connection.state = {
|
||||
status: VoiceConnectionStatus.Signalling,
|
||||
adapter: {
|
||||
sendPayload: jest.fn(),
|
||||
destroy: jest.fn(),
|
||||
sendPayload: vitest.fn(),
|
||||
destroy: vitest.fn(),
|
||||
},
|
||||
};
|
||||
connection.subscribe = jest.fn((player) => player['subscribe'](connection));
|
||||
connection.subscribe = vitest.fn((player) => player['subscribe'](connection));
|
||||
return connection;
|
||||
}
|
||||
|
||||
@@ -57,10 +75,7 @@ async function started(resource: AudioResource) {
|
||||
let player: AudioPlayer | undefined;
|
||||
|
||||
beforeEach(() => {
|
||||
AudioPlayerErrorMock.mockReset();
|
||||
VoiceConnectionMock.mockReset();
|
||||
addAudioPlayerMock.mockReset();
|
||||
deleteAudioPlayerMock.mockReset();
|
||||
vitest.resetAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -71,8 +86,8 @@ describe('State transitions', () => {
|
||||
test('Starts in Idle state', () => {
|
||||
player = createAudioPlayer();
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(0);
|
||||
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
|
||||
expect(addAudioPlayer).toBeCalledTimes(0);
|
||||
expect(deleteAudioPlayer).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('Playing resource with pausing and resuming', async () => {
|
||||
@@ -86,11 +101,11 @@ describe('State transitions', () => {
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
expect(player.unpause()).toEqual(false);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(0);
|
||||
expect(addAudioPlayer).toBeCalledTimes(0);
|
||||
|
||||
player.play(resource);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(addAudioPlayer).toBeCalledTimes(1);
|
||||
|
||||
// Expect pause() to return true and transition to paused state
|
||||
expect(player.pause()).toEqual(true);
|
||||
@@ -109,7 +124,7 @@ describe('State transitions', () => {
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
|
||||
// The audio player should not have been deleted throughout these changes
|
||||
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
|
||||
expect(deleteAudioPlayer).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test('Playing to Stopping', async () => {
|
||||
@@ -122,13 +137,13 @@ describe('State transitions', () => {
|
||||
|
||||
player.play(resource);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
|
||||
expect(addAudioPlayer).toBeCalledTimes(1);
|
||||
expect(deleteAudioPlayer).toBeCalledTimes(0);
|
||||
|
||||
expect(player.stop()).toEqual(true);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(deleteAudioPlayerMock).toBeCalledTimes(0);
|
||||
expect(addAudioPlayer).toBeCalledTimes(1);
|
||||
expect(deleteAudioPlayer).toBeCalledTimes(0);
|
||||
expect(resource.silenceRemaining).toEqual(5);
|
||||
});
|
||||
|
||||
@@ -142,8 +157,8 @@ describe('State transitions', () => {
|
||||
await started(resource);
|
||||
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
expect(addAudioPlayerMock).toHaveBeenCalled();
|
||||
expect(deleteAudioPlayerMock).not.toHaveBeenCalled();
|
||||
expect(addAudioPlayer).toHaveBeenCalled();
|
||||
expect(deleteAudioPlayer).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('NoSubscriberBehavior transitions', () => {
|
||||
@@ -188,11 +203,11 @@ describe('State transitions', () => {
|
||||
player = createAudioPlayer({ behaviors: { noSubscriber: NoSubscriberBehavior.Stop } });
|
||||
|
||||
player.play(resource);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(addAudioPlayer).toBeCalledTimes(1);
|
||||
expect(player.checkPlayable()).toEqual(true);
|
||||
player['_stepPrepare']();
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
expect(deleteAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(deleteAudioPlayer).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -217,7 +232,7 @@ describe('State transitions', () => {
|
||||
|
||||
player.play(resource);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(addAudioPlayer).toBeCalledTimes(1);
|
||||
expect(player.checkPlayable()).toEqual(true);
|
||||
|
||||
// Run through a few packet cycles
|
||||
@@ -241,7 +256,8 @@ describe('State transitions', () => {
|
||||
expect(connection.dispatchAudio).toHaveBeenCalledTimes(6);
|
||||
await wait();
|
||||
player['_stepPrepare']();
|
||||
const prepareAudioPacket = connection.prepareAudioPacket as unknown as jest.Mock<
|
||||
const prepareAudioPacket = connection.prepareAudioPacket as unknown as Mock<
|
||||
[Buffer],
|
||||
typeof connection.prepareAudioPacket
|
||||
>;
|
||||
expect(prepareAudioPacket).toHaveBeenCalledTimes(6);
|
||||
@@ -251,7 +267,7 @@ describe('State transitions', () => {
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
expect(connection.setSpeaking).toBeCalledTimes(1);
|
||||
expect(connection.setSpeaking).toHaveBeenLastCalledWith(false);
|
||||
expect(deleteAudioPlayerMock).toHaveBeenCalledTimes(1);
|
||||
expect(deleteAudioPlayer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('stop() causes resource to use silence padding frames', async () => {
|
||||
@@ -275,7 +291,7 @@ describe('State transitions', () => {
|
||||
|
||||
player.play(resource);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(addAudioPlayer).toBeCalledTimes(1);
|
||||
expect(player.checkPlayable()).toEqual(true);
|
||||
|
||||
player.stop();
|
||||
@@ -298,7 +314,8 @@ describe('State transitions', () => {
|
||||
|
||||
await wait();
|
||||
expect(player.checkPlayable()).toEqual(false);
|
||||
const prepareAudioPacket = connection.prepareAudioPacket as unknown as jest.Mock<
|
||||
const prepareAudioPacket = connection.prepareAudioPacket as unknown as Mock<
|
||||
[Buffer],
|
||||
typeof connection.prepareAudioPacket
|
||||
>;
|
||||
expect(prepareAudioPacket).toHaveBeenCalledTimes(5);
|
||||
@@ -306,7 +323,7 @@ describe('State transitions', () => {
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
expect(connection.setSpeaking).toBeCalledTimes(1);
|
||||
expect(connection.setSpeaking).toHaveBeenLastCalledWith(false);
|
||||
expect(deleteAudioPlayerMock).toHaveBeenCalledTimes(1);
|
||||
expect(deleteAudioPlayer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('Plays silence 5 times for unreadable stream before quitting', async () => {
|
||||
@@ -328,10 +345,11 @@ describe('State transitions', () => {
|
||||
|
||||
player.play(resource);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Playing);
|
||||
expect(addAudioPlayerMock).toBeCalledTimes(1);
|
||||
expect(addAudioPlayer).toBeCalledTimes(1);
|
||||
expect(player.checkPlayable()).toEqual(true);
|
||||
|
||||
const prepareAudioPacket = connection.prepareAudioPacket as unknown as jest.Mock<
|
||||
const prepareAudioPacket = connection.prepareAudioPacket as unknown as Mock<
|
||||
[Buffer],
|
||||
typeof connection.prepareAudioPacket
|
||||
>;
|
||||
|
||||
@@ -351,7 +369,7 @@ describe('State transitions', () => {
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
expect(connection.setSpeaking).toBeCalledTimes(1);
|
||||
expect(connection.setSpeaking).toHaveBeenLastCalledWith(false);
|
||||
expect(deleteAudioPlayerMock).toHaveBeenCalledTimes(1);
|
||||
expect(deleteAudioPlayer).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('checkPlayable() transitions to Idle for unreadable stream', async () => {
|
||||
@@ -397,6 +415,6 @@ test('Propagates errors from streams', async () => {
|
||||
const res = await once(player, 'error');
|
||||
const playerError = res[0] as AudioPlayerError;
|
||||
expect(playerError).toBeInstanceOf(AudioPlayerError);
|
||||
expect(AudioPlayerErrorMock).toHaveBeenCalledWith(error, resource);
|
||||
expect(AudioPlayerError).toHaveBeenCalledWith(error, resource);
|
||||
expect(player.state.status).toEqual(AudioPlayerStatus.Idle);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable no-promise-executor-return */
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { SILENCE_FRAME } from '../src/audio/AudioPlayer';
|
||||
import { AudioReceiveStream, EndBehaviorType } from '../src/receive/AudioReceiveStream';
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ import { Buffer } from 'node:buffer';
|
||||
import process from 'node:process';
|
||||
import { PassThrough, Readable } from 'node:stream';
|
||||
import { opus, VolumeTransformer } from 'prism-media';
|
||||
import { describe, test, expect, vitest, type MockedFunction, beforeAll, beforeEach } from 'vitest';
|
||||
import { SILENCE_FRAME } from '../src/audio/AudioPlayer';
|
||||
import { AudioResource, createAudioResource, NO_CONSTRAINT, VOLUME_CONSTRAINT } from '../src/audio/AudioResource';
|
||||
import { findPipeline as _findPipeline, StreamType, TransformerType, type Edge } from '../src/audio/TransformerGraph';
|
||||
|
||||
jest.mock('prism-media');
|
||||
jest.mock('../src/audio/TransformerGraph');
|
||||
vitest.mock('../src/audio/TransformerGraph');
|
||||
|
||||
async function wait() {
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
@@ -22,7 +22,7 @@ async function started(resource: AudioResource) {
|
||||
return resource;
|
||||
}
|
||||
|
||||
const findPipeline = _findPipeline as unknown as jest.MockedFunction<typeof _findPipeline>;
|
||||
const findPipeline = _findPipeline as unknown as MockedFunction<typeof _findPipeline>;
|
||||
|
||||
beforeAll(() => {
|
||||
// @ts-expect-error: No type
|
||||
@@ -37,7 +37,8 @@ beforeAll(() => {
|
||||
if (constraint === VOLUME_CONSTRAINT) {
|
||||
base.push({
|
||||
cost: 1,
|
||||
transformer: () => new VolumeTransformer({} as any),
|
||||
// Transformer type shouldn't matter: we are not testing prism-media, but rather the expectation that the stream is VolumeTransformer
|
||||
transformer: () => new VolumeTransformer({ type: 's16le' } as any),
|
||||
type: TransformerType.InlineVolume,
|
||||
});
|
||||
}
|
||||
@@ -96,7 +97,8 @@ describe('createAudioResource', () => {
|
||||
});
|
||||
|
||||
test('Infers from VolumeTransformer', () => {
|
||||
const stream = new VolumeTransformer({} as any);
|
||||
// Transformer type shouldn't matter: we are not testing prism-media, but rather the expectation that the stream is VolumeTransformer
|
||||
const stream = new VolumeTransformer({ type: 's16le' } as any);
|
||||
const resource = createAudioResource(stream, { inlineVolume: true });
|
||||
expect(findPipeline).toHaveBeenCalledWith(StreamType.Raw, NO_CONSTRAINT);
|
||||
expect(resource.volume).toEqual(stream);
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/dot-notation */
|
||||
import { GatewayOpcodes } from 'discord-api-types/v10';
|
||||
import { describe, test, expect, vitest, type Mocked, beforeEach } from 'vitest';
|
||||
import * as DataStore from '../src/DataStore';
|
||||
import type { VoiceConnection } from '../src/VoiceConnection';
|
||||
import * as _AudioPlayer from '../src/audio/AudioPlayer';
|
||||
|
||||
jest.mock('../src/VoiceConnection');
|
||||
jest.mock('../src/audio/AudioPlayer');
|
||||
vitest.mock('../src/VoiceConnection');
|
||||
vitest.mock('../src/audio/AudioPlayer');
|
||||
|
||||
const AudioPlayer = _AudioPlayer as unknown as jest.Mocked<typeof _AudioPlayer>;
|
||||
const AudioPlayer = _AudioPlayer as unknown as Mocked<typeof _AudioPlayer>;
|
||||
|
||||
function createVoiceConnection(joinConfig: Pick<DataStore.JoinConfig, 'group' | 'guildId'>): VoiceConnection {
|
||||
return {
|
||||
@@ -71,8 +72,8 @@ describe('DataStore', () => {
|
||||
});
|
||||
test('Managing Audio Players', async () => {
|
||||
const player = DataStore.addAudioPlayer(new AudioPlayer.AudioPlayer());
|
||||
const dispatchSpy = jest.spyOn(player as any, '_stepDispatch');
|
||||
const prepareSpy = jest.spyOn(player as any, '_stepPrepare');
|
||||
const dispatchSpy = vitest.spyOn(player as any, '_stepDispatch');
|
||||
const prepareSpy = vitest.spyOn(player as any, '_stepPrepare');
|
||||
expect(DataStore.hasAudioPlayer(player)).toEqual(true);
|
||||
expect(DataStore.addAudioPlayer(player)).toEqual(player);
|
||||
DataStore.deleteAudioPlayer(player);
|
||||
@@ -87,12 +88,12 @@ describe('DataStore', () => {
|
||||
test('Preparing Audio Frames', async () => {
|
||||
// Test functional player
|
||||
const player2 = DataStore.addAudioPlayer(new AudioPlayer.AudioPlayer());
|
||||
player2['checkPlayable'] = jest.fn(() => true);
|
||||
player2['checkPlayable'] = vitest.fn(() => true);
|
||||
const player3 = DataStore.addAudioPlayer(new AudioPlayer.AudioPlayer());
|
||||
const dispatchSpy2 = jest.spyOn(player2 as any, '_stepDispatch');
|
||||
const prepareSpy2 = jest.spyOn(player2 as any, '_stepPrepare');
|
||||
const dispatchSpy3 = jest.spyOn(player3 as any, '_stepDispatch');
|
||||
const prepareSpy3 = jest.spyOn(player3 as any, '_stepPrepare');
|
||||
const dispatchSpy2 = vitest.spyOn(player2 as any, '_stepDispatch');
|
||||
const prepareSpy2 = vitest.spyOn(player2 as any, '_stepPrepare');
|
||||
const dispatchSpy3 = vitest.spyOn(player3 as any, '_stepDispatch');
|
||||
const prepareSpy3 = vitest.spyOn(player3 as any, '_stepPrepare');
|
||||
await waitForEventLoop();
|
||||
DataStore.deleteAudioPlayer(player2);
|
||||
await waitForEventLoop();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { type EventEmitter, once } from 'node:events';
|
||||
import process from 'node:process';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { SSRCMap, type VoiceUserData } from '../src/receive/SSRCMap';
|
||||
|
||||
async function onceOrThrow<Emitter extends EventEmitter>(target: Emitter, event: string, after: number) {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { test, expect, vitest } from 'vitest';
|
||||
import { methods } from '../src/util/Secretbox';
|
||||
|
||||
jest.mock('tweetnacl');
|
||||
vitest.mock('tweetnacl');
|
||||
|
||||
test('Does not throw error with a package installed', () => {
|
||||
// @ts-expect-error: Unknown type
|
||||
expect(() => methods.open()).not.toThrowError();
|
||||
// @ts-expect-error We are testing
|
||||
expect(() => methods.open()).toThrow(TypeError);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { describe, test, expect, vitest } from 'vitest';
|
||||
import { SpeakingMap } from '../src/receive/SpeakingMap';
|
||||
import { noop } from '../src/util/util';
|
||||
|
||||
jest.useFakeTimers();
|
||||
vitest.useFakeTimers();
|
||||
|
||||
describe('SpeakingMap', () => {
|
||||
test('Emits start and end', () => {
|
||||
@@ -17,17 +18,17 @@ describe('SpeakingMap', () => {
|
||||
for (let index = 0; index < 10; index++) {
|
||||
speaking.onPacket(userId);
|
||||
setTimeout(noop, SpeakingMap.DELAY / 2);
|
||||
jest.advanceTimersToNextTimer();
|
||||
vitest.advanceTimersToNextTimer();
|
||||
|
||||
expect(starts).toEqual([userId]);
|
||||
expect(ends).toEqual([]);
|
||||
}
|
||||
|
||||
jest.advanceTimersToNextTimer();
|
||||
vitest.advanceTimersToNextTimer();
|
||||
expect(ends).toEqual([userId]);
|
||||
|
||||
speaking.onPacket(userId);
|
||||
jest.advanceTimersToNextTimer();
|
||||
vitest.advanceTimersToNextTimer();
|
||||
expect(starts).toEqual([userId, userId]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @ts-nocheck
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { findPipeline, StreamType, TransformerType, type Edge } from '../src/audio/TransformerGraph';
|
||||
|
||||
const noConstraint = () => true;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
/* eslint-disable @typescript-eslint/dot-notation */
|
||||
// @ts-nocheck
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { vitest, describe, test, expect, beforeEach } from 'vitest';
|
||||
import * as _DataStore from '../src/DataStore';
|
||||
import {
|
||||
createVoiceConnection,
|
||||
@@ -14,34 +15,42 @@ import {
|
||||
} from '../src/VoiceConnection';
|
||||
import * as _AudioPlayer from '../src/audio/AudioPlayer';
|
||||
import { PlayerSubscription as _PlayerSubscription } from '../src/audio/PlayerSubscription';
|
||||
import * as _Networking from '../src/networking/Networking';
|
||||
import * as Networking from '../src/networking/Networking';
|
||||
import type { DiscordGatewayAdapterLibraryMethods } from '../src/util/adapter';
|
||||
|
||||
jest.mock('../src/audio/AudioPlayer');
|
||||
jest.mock('../src/audio/PlayerSubscription');
|
||||
jest.mock('../src/DataStore');
|
||||
jest.mock('../src/networking/Networking');
|
||||
vitest.mock('../src/audio/AudioPlayer');
|
||||
vitest.mock('../src/audio/PlayerSubscription');
|
||||
vitest.mock('../src/DataStore');
|
||||
vitest.mock('../src/networking/Networking', async (importOriginal) => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
const actual = await importOriginal<typeof import('../src/networking/Networking')>();
|
||||
const Networking = actual.Networking;
|
||||
Networking.prototype.createWebSocket = vitest.fn();
|
||||
return {
|
||||
...actual,
|
||||
Networking,
|
||||
};
|
||||
});
|
||||
|
||||
const DataStore = _DataStore as unknown as jest.Mocked<typeof _DataStore>;
|
||||
const Networking = _Networking as unknown as jest.Mocked<typeof _Networking>;
|
||||
const AudioPlayer = _AudioPlayer as unknown as jest.Mocked<typeof _AudioPlayer>;
|
||||
const PlayerSubscription = _PlayerSubscription as unknown as jest.Mock<_PlayerSubscription>;
|
||||
const DataStore = _DataStore as unknown as vitest.Mocked<typeof _DataStore>;
|
||||
const AudioPlayer = _AudioPlayer as unknown as vitest.Mocked<typeof _AudioPlayer>;
|
||||
const PlayerSubscription = _PlayerSubscription as unknown as vitest.Mock<_PlayerSubscription>;
|
||||
|
||||
Networking.Networking.mockImplementation(function mockedConstructor() {
|
||||
this.state = {};
|
||||
return this;
|
||||
const _NetworkingClass = Networking.Networking;
|
||||
vitest.spyOn(Networking, 'Networking').mockImplementation((...args) => {
|
||||
return new _NetworkingClass(...args);
|
||||
});
|
||||
|
||||
function createFakeAdapter() {
|
||||
const sendPayload = jest.fn();
|
||||
const sendPayload = vitest.fn();
|
||||
sendPayload.mockReturnValue(true);
|
||||
const destroy = jest.fn();
|
||||
const destroy = vitest.fn();
|
||||
const libMethods: Partial<DiscordGatewayAdapterLibraryMethods> = {};
|
||||
return {
|
||||
sendPayload,
|
||||
destroy,
|
||||
libMethods,
|
||||
creator: jest.fn((methods) => {
|
||||
creator: vitest.fn((methods) => {
|
||||
Object.assign(libMethods, methods);
|
||||
return {
|
||||
sendPayload,
|
||||
@@ -124,7 +133,7 @@ describe('createVoiceConnection', () => {
|
||||
adapterCreator: existingAdapter.creator,
|
||||
});
|
||||
|
||||
const stateSetter = jest.spyOn(existingVoiceConnection, 'state', 'set');
|
||||
const stateSetter = vitest.spyOn(existingVoiceConnection, 'state', 'set');
|
||||
|
||||
// @ts-expect-error: We're testing
|
||||
DataStore.getVoiceConnection.mockImplementation((guildId, group = 'default') =>
|
||||
@@ -163,7 +172,7 @@ describe('createVoiceConnection', () => {
|
||||
reason: VoiceConnectionDisconnectReason.EndpointRemoved,
|
||||
};
|
||||
|
||||
const rejoinSpy = jest.spyOn(existingVoiceConnection, 'rejoin');
|
||||
const rejoinSpy = vitest.spyOn(existingVoiceConnection, 'rejoin');
|
||||
|
||||
// @ts-expect-error: We're testing
|
||||
DataStore.getVoiceConnection.mockImplementation((guildId, group = 'default') =>
|
||||
@@ -222,7 +231,7 @@ describe('createVoiceConnection', () => {
|
||||
describe('VoiceConnection#addServerPacket', () => {
|
||||
test('Stores the packet and attempts to configure networking', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection.configureNetworking = jest.fn();
|
||||
voiceConnection.configureNetworking = vitest.fn();
|
||||
const dummy = {
|
||||
endpoint: 'discord.com',
|
||||
guild_id: 123,
|
||||
@@ -236,7 +245,7 @@ describe('VoiceConnection#addServerPacket', () => {
|
||||
test('Overwrites existing packet', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['packets'].server = Symbol('old') as any;
|
||||
voiceConnection.configureNetworking = jest.fn();
|
||||
voiceConnection.configureNetworking = vitest.fn();
|
||||
const dummy = {
|
||||
endpoint: 'discord.com',
|
||||
guild_id: 123,
|
||||
@@ -250,7 +259,7 @@ describe('VoiceConnection#addServerPacket', () => {
|
||||
test('Disconnects when given a null endpoint', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['packets'].server = Symbol('old') as any;
|
||||
voiceConnection.configureNetworking = jest.fn();
|
||||
voiceConnection.configureNetworking = vitest.fn();
|
||||
const dummy = {
|
||||
endpoint: null,
|
||||
guild_id: 123,
|
||||
@@ -344,7 +353,7 @@ describe('VoiceConnection#configureNetworking', () => {
|
||||
adapter,
|
||||
});
|
||||
expect((voiceConnection.state as unknown as VoiceConnectionConnectingState).networking).toBeInstanceOf(
|
||||
Networking.Networking,
|
||||
_NetworkingClass,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -399,24 +408,24 @@ describe('VoiceConnection#onNetworkingClose', () => {
|
||||
describe('VoiceConnection#onNetworkingStateChange', () => {
|
||||
test('Does nothing when status code identical', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
const stateSetter = vitest.spyOn(voiceConnection, 'state', 'set');
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Ready } as any,
|
||||
);
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Closed } as any,
|
||||
);
|
||||
expect(stateSetter).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Does nothing when not in Ready or Connecting states', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
const stateSetter = vitest.spyOn(voiceConnection, 'state', 'set');
|
||||
const call = [
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Closed } as any,
|
||||
];
|
||||
voiceConnection['_state'] = { status: VoiceConnectionStatus.Signalling } as any;
|
||||
voiceConnection['onNetworkingStateChange'](call[0], call[1]);
|
||||
@@ -429,7 +438,7 @@ describe('VoiceConnection#onNetworkingStateChange', () => {
|
||||
|
||||
test('Transitions to Ready', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
const stateSetter = vitest.spyOn(voiceConnection, 'state', 'set');
|
||||
voiceConnection['_state'] = {
|
||||
...(voiceConnection.state as VoiceConnectionSignallingState),
|
||||
status: VoiceConnectionStatus.Connecting,
|
||||
@@ -437,8 +446,8 @@ describe('VoiceConnection#onNetworkingStateChange', () => {
|
||||
};
|
||||
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Closed } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Ready } as any,
|
||||
);
|
||||
|
||||
expect(stateSetter).toHaveBeenCalledTimes(1);
|
||||
@@ -447,7 +456,7 @@ describe('VoiceConnection#onNetworkingStateChange', () => {
|
||||
|
||||
test('Transitions to Connecting', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const stateSetter = jest.spyOn(voiceConnection, 'state', 'set');
|
||||
const stateSetter = vitest.spyOn(voiceConnection, 'state', 'set');
|
||||
voiceConnection['_state'] = {
|
||||
...(voiceConnection.state as VoiceConnectionSignallingState),
|
||||
status: VoiceConnectionStatus.Connecting,
|
||||
@@ -455,8 +464,8 @@ describe('VoiceConnection#onNetworkingStateChange', () => {
|
||||
};
|
||||
|
||||
voiceConnection['onNetworkingStateChange'](
|
||||
{ code: _Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: _Networking.NetworkingStatusCode.Identifying } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Ready } as any,
|
||||
{ code: Networking.NetworkingStatusCode.Identifying } as any,
|
||||
);
|
||||
|
||||
expect(stateSetter).toHaveBeenCalledTimes(1);
|
||||
@@ -598,7 +607,7 @@ describe('VoiceConnection#subscribe', () => {
|
||||
test('Does nothing in Destroyed state', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const player = new AudioPlayer.AudioPlayer();
|
||||
player['subscribe'] = jest.fn();
|
||||
player['subscribe'] = vitest.fn();
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Destroyed };
|
||||
expect(voiceConnection.subscribe(player)).toBeUndefined();
|
||||
expect(player['subscribe']).not.toHaveBeenCalled();
|
||||
@@ -610,7 +619,7 @@ describe('VoiceConnection#subscribe', () => {
|
||||
const adapter = (voiceConnection.state as VoiceConnectionSignallingState).adapter;
|
||||
const player = new AudioPlayer.AudioPlayer();
|
||||
const dummy = Symbol('dummy');
|
||||
player['subscribe'] = jest.fn().mockImplementation(() => dummy);
|
||||
player['subscribe'] = vitest.fn().mockImplementation(() => dummy);
|
||||
expect(voiceConnection.subscribe(player)).toEqual(dummy);
|
||||
expect(player['subscribe']).toHaveBeenCalledWith(voiceConnection);
|
||||
expect(voiceConnection.state).toMatchObject({
|
||||
@@ -624,7 +633,7 @@ describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
test('Does nothing in Destroyed state', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const subscription = new PlayerSubscription(voiceConnection, new AudioPlayer.AudioPlayer());
|
||||
subscription.unsubscribe = jest.fn();
|
||||
subscription.unsubscribe = vitest.fn();
|
||||
|
||||
voiceConnection.state = { status: VoiceConnectionStatus.Destroyed };
|
||||
voiceConnection['onSubscriptionRemoved'](subscription);
|
||||
@@ -635,7 +644,7 @@ describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
test('Does nothing when subscription is not the same as the stored one', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const subscription = new PlayerSubscription(voiceConnection, new AudioPlayer.AudioPlayer());
|
||||
subscription.unsubscribe = jest.fn();
|
||||
subscription.unsubscribe = vitest.fn();
|
||||
|
||||
voiceConnection.state = { ...(voiceConnection.state as VoiceConnectionSignallingState), subscription };
|
||||
voiceConnection['onSubscriptionRemoved'](Symbol('new subscription') as any);
|
||||
@@ -649,7 +658,7 @@ describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
test('Unsubscribes in a live state with matching subscription', () => {
|
||||
const { voiceConnection } = createFakeVoiceConnection();
|
||||
const subscription = new PlayerSubscription(voiceConnection, new AudioPlayer.AudioPlayer());
|
||||
subscription.unsubscribe = jest.fn();
|
||||
subscription.unsubscribe = vitest.fn();
|
||||
|
||||
voiceConnection.state = { ...(voiceConnection.state as VoiceConnectionSignallingState), subscription };
|
||||
voiceConnection['onSubscriptionRemoved'](subscription);
|
||||
@@ -667,7 +676,7 @@ describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
|
||||
const oldNetworking = new Networking.Networking({} as any, false);
|
||||
oldNetworking.state = {
|
||||
code: _Networking.NetworkingStatusCode.Ready,
|
||||
code: Networking.NetworkingStatusCode.Ready,
|
||||
connectionData: {} as any,
|
||||
connectionOptions: {} as any,
|
||||
udp: new EventEmitter() as any,
|
||||
@@ -697,7 +706,7 @@ describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
|
||||
const oldNetworking = new Networking.Networking({} as any, false);
|
||||
oldNetworking.state = {
|
||||
code: _Networking.NetworkingStatusCode.Ready,
|
||||
code: Networking.NetworkingStatusCode.Ready,
|
||||
connectionData: {} as any,
|
||||
connectionOptions: {} as any,
|
||||
udp,
|
||||
@@ -726,7 +735,7 @@ describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
|
||||
const newNetworking = new Networking.Networking({} as any, false);
|
||||
newNetworking.state = {
|
||||
code: _Networking.NetworkingStatusCode.Ready,
|
||||
code: Networking.NetworkingStatusCode.Ready,
|
||||
connectionData: {} as any,
|
||||
connectionOptions: {} as any,
|
||||
udp: new EventEmitter() as any,
|
||||
@@ -749,7 +758,7 @@ describe('VoiceConnection#onSubscriptionRemoved', () => {
|
||||
describe('Adapter', () => {
|
||||
test('onVoiceServerUpdate', () => {
|
||||
const { adapter, voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['addServerPacket'] = jest.fn();
|
||||
voiceConnection['addServerPacket'] = vitest.fn();
|
||||
const dummy = Symbol('dummy') as any;
|
||||
adapter.libMethods.onVoiceServerUpdate!(dummy);
|
||||
expect(voiceConnection['addServerPacket']).toHaveBeenCalledWith(dummy);
|
||||
@@ -757,7 +766,7 @@ describe('Adapter', () => {
|
||||
|
||||
test('onVoiceStateUpdate', () => {
|
||||
const { adapter, voiceConnection } = createFakeVoiceConnection();
|
||||
voiceConnection['addStatePacket'] = jest.fn();
|
||||
voiceConnection['addStatePacket'] = vitest.fn();
|
||||
const dummy = Symbol('dummy') as any;
|
||||
adapter.libMethods.onVoiceStateUpdate!(dummy);
|
||||
expect(voiceConnection['addStatePacket']).toHaveBeenCalledWith(dummy);
|
||||
|
||||
@@ -5,20 +5,27 @@ import { Buffer } from 'node:buffer';
|
||||
import { once } from 'node:events';
|
||||
import process from 'node:process';
|
||||
import { VoiceOpcodes } from 'discord-api-types/voice/v4';
|
||||
import { describe, test, expect, vitest, beforeEach } from 'vitest';
|
||||
import { RTP_PACKET_DESKTOP, RTP_PACKET_CHROME, RTP_PACKET_ANDROID } from '../__mocks__/rtp';
|
||||
import { VoiceConnection as _VoiceConnection, VoiceConnectionStatus } from '../src/VoiceConnection';
|
||||
import { VoiceConnection, VoiceConnectionStatus } from '../src/VoiceConnection';
|
||||
import { VoiceReceiver } from '../src/receive/VoiceReceiver';
|
||||
import { methods } from '../src/util/Secretbox';
|
||||
|
||||
jest.mock('../src/VoiceConnection');
|
||||
jest.mock('../src/receive/SSRCMap');
|
||||
vitest.mock('../src/VoiceConnection', async (importOriginal) => {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
|
||||
const actual = await importOriginal<typeof import('../src/VoiceConnection')>();
|
||||
return {
|
||||
...actual,
|
||||
VoiceConnection: vitest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
const openSpy = jest.spyOn(methods, 'open');
|
||||
vitest.mock('../src/receive/SSRCMap');
|
||||
|
||||
const openSpy = vitest.spyOn(methods, 'open');
|
||||
|
||||
openSpy.mockImplementation((buffer) => buffer);
|
||||
|
||||
const VoiceConnection = _VoiceConnection as unknown as jest.Mocked<typeof _VoiceConnection>;
|
||||
|
||||
async function nextTick() {
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
return new Promise((resolve) => process.nextTick(resolve));
|
||||
@@ -56,9 +63,9 @@ describe('VoiceReceiver', () => {
|
||||
['RTP Packet Chrome', RTP_PACKET_CHROME],
|
||||
['RTP Packet Android', RTP_PACKET_ANDROID],
|
||||
])('onUdpMessage: %s', async (testName, RTP_PACKET) => {
|
||||
receiver['decrypt'] = jest.fn().mockImplementationOnce(() => RTP_PACKET.decrypted);
|
||||
receiver['decrypt'] = vitest.fn().mockImplementationOnce(() => RTP_PACKET.decrypted);
|
||||
|
||||
const spy = jest.spyOn(receiver.ssrcMap, 'get');
|
||||
const spy = vitest.spyOn(receiver.ssrcMap, 'get');
|
||||
spy.mockImplementation(() => ({
|
||||
audioSSRC: RTP_PACKET.ssrc,
|
||||
userId: '123',
|
||||
@@ -76,9 +83,9 @@ describe('VoiceReceiver', () => {
|
||||
});
|
||||
|
||||
test('onUdpMessage: destroys stream on decrypt failure', async () => {
|
||||
receiver['decrypt'] = jest.fn().mockImplementationOnce(() => null);
|
||||
receiver['decrypt'] = vitest.fn().mockImplementationOnce(() => null);
|
||||
|
||||
const spy = jest.spyOn(receiver.ssrcMap, 'get');
|
||||
const spy = vitest.spyOn(receiver.ssrcMap, 'get');
|
||||
spy.mockImplementation(() => ({
|
||||
audioSSRC: RTP_PACKET_DESKTOP.ssrc,
|
||||
userId: '123',
|
||||
@@ -95,7 +102,7 @@ describe('VoiceReceiver', () => {
|
||||
});
|
||||
|
||||
test('subscribe: only allows one subscribe stream per SSRC', () => {
|
||||
const spy = jest.spyOn(receiver.ssrcMap, 'get');
|
||||
const spy = vitest.spyOn(receiver.ssrcMap, 'get');
|
||||
spy.mockImplementation(() => ({
|
||||
audioSSRC: RTP_PACKET_DESKTOP.ssrc,
|
||||
userId: '123',
|
||||
@@ -107,7 +114,7 @@ describe('VoiceReceiver', () => {
|
||||
|
||||
describe('onWsPacket', () => {
|
||||
test('CLIENT_DISCONNECT packet', () => {
|
||||
const spy = jest.spyOn(receiver.ssrcMap, 'delete');
|
||||
const spy = vitest.spyOn(receiver.ssrcMap, 'delete');
|
||||
receiver['onWsPacket']({
|
||||
op: VoiceOpcodes.ClientDisconnect,
|
||||
d: {
|
||||
@@ -118,7 +125,7 @@ describe('VoiceReceiver', () => {
|
||||
});
|
||||
|
||||
test('SPEAKING packet', () => {
|
||||
const spy = jest.spyOn(receiver.ssrcMap, 'update');
|
||||
const spy = vitest.spyOn(receiver.ssrcMap, 'update');
|
||||
receiver['onWsPacket']({
|
||||
op: VoiceOpcodes.Speaking,
|
||||
d: {
|
||||
@@ -134,7 +141,7 @@ describe('VoiceReceiver', () => {
|
||||
});
|
||||
|
||||
test('CLIENT_CONNECT packet', () => {
|
||||
const spy = jest.spyOn(receiver.ssrcMap, 'update');
|
||||
const spy = vitest.spyOn(receiver.ssrcMap, 'update');
|
||||
receiver['onWsPacket']({
|
||||
op: VoiceOpcodes.ClientConnect,
|
||||
d: {
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
import { Buffer } from 'node:buffer';
|
||||
import { createSocket as _createSocket } from 'node:dgram';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { describe, test, expect, vitest, beforeEach, afterEach } from 'vitest';
|
||||
import { VoiceUDPSocket } from '../src/networking/VoiceUDPSocket';
|
||||
|
||||
jest.mock('node:dgram');
|
||||
jest.useFakeTimers();
|
||||
vitest.mock('node:dgram');
|
||||
vitest.useFakeTimers();
|
||||
|
||||
const createSocket = _createSocket as unknown as jest.Mock<typeof _createSocket>;
|
||||
const createSocket = _createSocket as unknown as vitest.Mock<typeof _createSocket>;
|
||||
|
||||
beforeEach(() => {
|
||||
createSocket.mockReset();
|
||||
@@ -32,7 +33,7 @@ const VALID_RESPONSE = Buffer.from([
|
||||
async function wait() {
|
||||
return new Promise((resolve) => {
|
||||
setImmediate(resolve);
|
||||
jest.advanceTimersToNextTimer();
|
||||
vitest.advanceTimersToNextTimer();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -48,7 +49,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
*/
|
||||
test('Resolves and cleans up with a successful flow', async () => {
|
||||
const fake = new FakeSocket();
|
||||
fake.send = jest.fn().mockImplementation((buffer: Buffer, port: number, address: string) => {
|
||||
fake.send = vitest.fn().mockImplementation((buffer: Buffer, port: number, address: string) => {
|
||||
fake.emit('message', VALID_RESPONSE);
|
||||
});
|
||||
createSocket.mockImplementation((type) => fake as any);
|
||||
@@ -71,7 +72,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
test('Waits for a valid response in an unexpected flow', async () => {
|
||||
const fake = new FakeSocket();
|
||||
const fakeResponse = Buffer.from([1, 2, 3, 4, 5]);
|
||||
fake.send = jest.fn().mockImplementation(async (buffer: Buffer, port: number, address: string) => {
|
||||
fake.send = vitest.fn().mockImplementation(async (buffer: Buffer, port: number, address: string) => {
|
||||
fake.emit('message', fakeResponse);
|
||||
await wait();
|
||||
fake.emit('message', VALID_RESPONSE);
|
||||
@@ -91,7 +92,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
|
||||
test('Rejects if socket closes before IP discovery can be completed', async () => {
|
||||
const fake = new FakeSocket();
|
||||
fake.send = jest.fn().mockImplementation(async (buffer: Buffer, port: number, address: string) => {
|
||||
fake.send = vitest.fn().mockImplementation(async (buffer: Buffer, port: number, address: string) => {
|
||||
await wait();
|
||||
fake.close();
|
||||
});
|
||||
@@ -104,7 +105,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
|
||||
test('Stays alive when messages are echoed back', async () => {
|
||||
const fake = new FakeSocket();
|
||||
fake.send = jest.fn().mockImplementation(async (buffer: Buffer) => {
|
||||
fake.send = vitest.fn().mockImplementation(async (buffer: Buffer) => {
|
||||
await wait();
|
||||
fake.emit('message', buffer);
|
||||
});
|
||||
@@ -115,7 +116,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
socket.on('close', () => (closed = true));
|
||||
|
||||
for (let index = 0; index < 30; index++) {
|
||||
jest.advanceTimersToNextTimer();
|
||||
vitest.advanceTimersToNextTimer();
|
||||
await wait();
|
||||
}
|
||||
|
||||
@@ -124,7 +125,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
|
||||
test('Recovers from intermittent responses', async () => {
|
||||
const fake = new FakeSocket();
|
||||
const fakeSend = jest.fn();
|
||||
const fakeSend = vitest.fn();
|
||||
fake.send = fakeSend;
|
||||
createSocket.mockImplementation(() => fake as any);
|
||||
socket = new VoiceUDPSocket({ ip: '1.2.3.4', port: 25_565 });
|
||||
@@ -134,7 +135,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
socket.on('close', () => (closed = true));
|
||||
|
||||
for (let index = 0; index < 10; index++) {
|
||||
jest.advanceTimersToNextTimer();
|
||||
vitest.advanceTimersToNextTimer();
|
||||
await wait();
|
||||
}
|
||||
|
||||
@@ -144,7 +145,7 @@ describe('VoiceUDPSocket#performIPDiscovery', () => {
|
||||
});
|
||||
expect(closed).toEqual(false);
|
||||
for (let index = 0; index < 30; index++) {
|
||||
jest.advanceTimersToNextTimer();
|
||||
vitest.advanceTimersToNextTimer();
|
||||
await wait();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { type EventEmitter, once } from 'node:events';
|
||||
import { VoiceOpcodes } from 'discord-api-types/voice/v4';
|
||||
import WS from 'jest-websocket-mock';
|
||||
import { describe, test, expect, beforeEach } from 'vitest';
|
||||
import WS from 'vitest-websocket-mock';
|
||||
import { VoiceWebSocket } from '../src/networking/VoiceWebSocket';
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { describe, test, expect, vitest } from 'vitest';
|
||||
import { abortAfter } from '../src/util/abortAfter';
|
||||
|
||||
jest.useFakeTimers();
|
||||
vitest.useFakeTimers();
|
||||
|
||||
const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
|
||||
const clearTimeoutSpy = vitest.spyOn(global, 'clearTimeout');
|
||||
|
||||
describe('abortAfter', () => {
|
||||
test('Aborts after the given delay', () => {
|
||||
const [ac, signal] = abortAfter(100);
|
||||
expect(ac.signal).toEqual(signal);
|
||||
expect(signal.aborted).toEqual(false);
|
||||
jest.runAllTimers();
|
||||
vitest.runAllTimers();
|
||||
expect(signal.aborted).toEqual(true);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@ import EventEmitter, { once } from 'node:events';
|
||||
import process from 'node:process';
|
||||
import { Readable } from 'node:stream';
|
||||
import { opus as _opus } from 'prism-media';
|
||||
import { describe, test, expect, vitest, type Mock, beforeAll, beforeEach } from 'vitest';
|
||||
import { StreamType } from '../src/audio/index';
|
||||
import { demuxProbe } from '../src/util/demuxProbe';
|
||||
|
||||
jest.mock('prism-media');
|
||||
vitest.mock('prism-media');
|
||||
|
||||
const WebmDemuxer = _opus.WebmDemuxer as unknown as jest.Mock<_opus.WebmDemuxer>;
|
||||
const OggDemuxer = _opus.OggDemuxer as unknown as jest.Mock<_opus.OggDemuxer>;
|
||||
const WebmDemuxer = _opus.WebmDemuxer as unknown as Mock<_opus.WebmDemuxer>;
|
||||
const OggDemuxer = _opus.OggDemuxer as unknown as Mock<_opus.OggDemuxer>;
|
||||
|
||||
async function nextTick() {
|
||||
// eslint-disable-next-line no-promise-executor-return
|
||||
@@ -47,8 +48,8 @@ async function collectStream(stream: Readable): Promise<Buffer> {
|
||||
}
|
||||
|
||||
describe('demuxProbe', () => {
|
||||
const webmWrite: jest.Mock<(buffer: Buffer) => void> = jest.fn();
|
||||
const oggWrite: jest.Mock<(buffer: Buffer) => void> = jest.fn();
|
||||
const webmWrite: Mock<(buffer: Buffer) => void> = vitest.fn();
|
||||
const oggWrite: Mock<(buffer: Buffer) => void> = vitest.fn();
|
||||
|
||||
beforeAll(() => {
|
||||
WebmDemuxer.prototype = {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import process from 'node:process';
|
||||
import { describe, test, expect, vitest, beforeEach } from 'vitest';
|
||||
import { VoiceConnectionStatus, type VoiceConnection } from '../src/VoiceConnection';
|
||||
import { entersState } from '../src/util/entersState';
|
||||
|
||||
@@ -10,12 +11,12 @@ function createFakeVoiceConnection(status = VoiceConnectionStatus.Signalling) {
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
vitest.useFakeTimers();
|
||||
});
|
||||
|
||||
describe('entersState', () => {
|
||||
test('Returns the target once the state has been entered before timeout', async () => {
|
||||
jest.useRealTimers();
|
||||
vitest.useRealTimers();
|
||||
const vc = createFakeVoiceConnection();
|
||||
process.nextTick(() => vc.emit(VoiceConnectionStatus.Ready, null as any, null as any));
|
||||
const result = await entersState(vc, VoiceConnectionStatus.Ready, 1_000);
|
||||
@@ -25,12 +26,12 @@ describe('entersState', () => {
|
||||
test('Rejects once the timeout is exceeded', async () => {
|
||||
const vc = createFakeVoiceConnection();
|
||||
const promise = entersState(vc, VoiceConnectionStatus.Ready, 1_000);
|
||||
jest.runAllTimers();
|
||||
vitest.runAllTimers();
|
||||
await expect(promise).rejects.toThrowError();
|
||||
});
|
||||
|
||||
test('Returns the target once the state has been entered before signal is aborted', async () => {
|
||||
jest.useRealTimers();
|
||||
vitest.useRealTimers();
|
||||
const vc = createFakeVoiceConnection();
|
||||
const ac = new AbortController();
|
||||
process.nextTick(() => vc.emit(VoiceConnectionStatus.Ready, null as any, null as any));
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// @ts-nocheck
|
||||
import { describe, test, expect, vitest, beforeAll, beforeEach } from 'vitest';
|
||||
import * as VoiceConnection from '../src/VoiceConnection';
|
||||
import { joinVoiceChannel } from '../src/joinVoiceChannel';
|
||||
|
||||
const adapterCreator = () => ({ destroy: jest.fn(), send: jest.fn() }) as any;
|
||||
const createVoiceConnection = jest.spyOn(VoiceConnection, 'createVoiceConnection');
|
||||
const adapterCreator = () => ({ destroy: vitest.fn(), send: vitest.fn() }) as any;
|
||||
const createVoiceConnection = vitest.spyOn(VoiceConnection, 'createVoiceConnection');
|
||||
|
||||
beforeAll(() => {
|
||||
createVoiceConnection.mockImplementation(() => null as any);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/**
|
||||
* @type {import('@babel/core').TransformOptions}
|
||||
*/
|
||||
module.exports = {
|
||||
parserOpts: { strictMode: true },
|
||||
sourceMaps: 'inline',
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: { node: 'current' },
|
||||
modules: 'commonjs',
|
||||
},
|
||||
],
|
||||
'@babel/preset-typescript',
|
||||
],
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* @type {import('@jest/types').Config.InitialOptions}
|
||||
*/
|
||||
module.exports = {
|
||||
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
|
||||
testEnvironment: 'node',
|
||||
collectCoverage: true,
|
||||
collectCoverageFrom: ['src/**/*.ts'],
|
||||
coverageDirectory: 'coverage',
|
||||
coverageReporters: ['text', 'lcov', 'cobertura'],
|
||||
};
|
||||
@@ -6,7 +6,7 @@
|
||||
"scripts": {
|
||||
"build": "tsc --noEmit && tsup && node scripts/postbuild.mjs",
|
||||
"build:docs": "tsc -p tsconfig.docs.json",
|
||||
"test": "jest --coverage",
|
||||
"test": "vitest run",
|
||||
"lint": "prettier --check . && cross-env TIMING=1 eslint --format=pretty src __tests__",
|
||||
"format": "prettier --write . && cross-env TIMING=1 eslint --fix --format=pretty src __tests__",
|
||||
"fmt": "pnpm run format",
|
||||
@@ -70,27 +70,24 @@
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.24.6",
|
||||
"@babel/preset-env": "^7.24.6",
|
||||
"@babel/preset-typescript": "^7.24.6",
|
||||
"@discordjs/api-extractor": "workspace:^",
|
||||
"@discordjs/opus": "^0.9.0",
|
||||
"@discordjs/scripts": "workspace:^",
|
||||
"@favware/cliff-jumper": "^4.1.0",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^16.18.105",
|
||||
"@types/node": "18.19.45",
|
||||
"@vitest/coverage-v8": "2.0.5",
|
||||
"cross-env": "^7.0.3",
|
||||
"esbuild-plugin-version-injector": "^1.2.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-neon": "^0.1.62",
|
||||
"eslint-formatter-pretty": "^6.0.1",
|
||||
"jest": "^29.7.0",
|
||||
"jest-websocket-mock": "^2.5.0",
|
||||
"mock-socket": "^9.3.1",
|
||||
"prettier": "^3.3.3",
|
||||
"tsup": "^8.2.4",
|
||||
"turbo": "^2.0.14",
|
||||
"tweetnacl": "^1.0.3",
|
||||
"typescript": "~5.5.4"
|
||||
"typescript": "~5.5.4",
|
||||
"vitest": "^2.0.5",
|
||||
"vitest-websocket-mock": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
||||
100
pnpm-lock.yaml
generated
100
pnpm-lock.yaml
generated
@@ -1602,7 +1602,7 @@ importers:
|
||||
version: 0.37.101
|
||||
prism-media:
|
||||
specifier: ^1.3.5
|
||||
version: 1.3.5
|
||||
version: 1.3.5(@discordjs/opus@0.9.0(encoding@0.1.13))
|
||||
tslib:
|
||||
specifier: ^2.6.3
|
||||
version: 2.6.3
|
||||
@@ -1610,30 +1610,24 @@ importers:
|
||||
specifier: ^8.18.0
|
||||
version: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)
|
||||
devDependencies:
|
||||
'@babel/core':
|
||||
specifier: ^7.24.6
|
||||
version: 7.25.2
|
||||
'@babel/preset-env':
|
||||
specifier: ^7.24.6
|
||||
version: 7.25.4(@babel/core@7.25.2)
|
||||
'@babel/preset-typescript':
|
||||
specifier: ^7.24.6
|
||||
version: 7.24.7(@babel/core@7.25.2)
|
||||
'@discordjs/api-extractor':
|
||||
specifier: workspace:^
|
||||
version: link:../api-extractor
|
||||
'@discordjs/opus':
|
||||
specifier: ^0.9.0
|
||||
version: 0.9.0(encoding@0.1.13)
|
||||
'@discordjs/scripts':
|
||||
specifier: workspace:^
|
||||
version: link:../scripts
|
||||
'@favware/cliff-jumper':
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0
|
||||
'@types/jest':
|
||||
specifier: ^29.5.12
|
||||
version: 29.5.12
|
||||
'@types/node':
|
||||
specifier: ^16.18.105
|
||||
version: 16.18.105
|
||||
specifier: 18.19.45
|
||||
version: 18.19.45
|
||||
'@vitest/coverage-v8':
|
||||
specifier: 2.0.5
|
||||
version: 2.0.5(vitest@2.0.5(@edge-runtime/vm@3.2.0)(@types/node@18.19.45)(happy-dom@14.12.3)(terser@5.31.6))
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
@@ -1649,21 +1643,12 @@ importers:
|
||||
eslint-formatter-pretty:
|
||||
specifier: ^6.0.1
|
||||
version: 6.0.1
|
||||
jest:
|
||||
specifier: ^29.7.0
|
||||
version: 29.7.0(@types/node@16.18.105)(ts-node@10.9.2(@types/node@16.18.105)(typescript@5.5.4))
|
||||
jest-websocket-mock:
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0
|
||||
mock-socket:
|
||||
specifier: ^9.3.1
|
||||
version: 9.3.1
|
||||
prettier:
|
||||
specifier: ^3.3.3
|
||||
version: 3.3.3
|
||||
tsup:
|
||||
specifier: ^8.2.4
|
||||
version: 8.2.4(@microsoft/api-extractor@7.43.0(@types/node@16.18.105))(jiti@1.21.6)(postcss@8.4.41)(typescript@5.5.4)(yaml@2.5.0)
|
||||
version: 8.2.4(@microsoft/api-extractor@7.43.0(@types/node@18.19.45))(jiti@1.21.6)(postcss@8.4.41)(typescript@5.5.4)(yaml@2.5.0)
|
||||
turbo:
|
||||
specifier: ^2.0.14
|
||||
version: 2.0.14
|
||||
@@ -1673,6 +1658,12 @@ importers:
|
||||
typescript:
|
||||
specifier: ~5.5.4
|
||||
version: 5.5.4
|
||||
vitest:
|
||||
specifier: ^2.0.5
|
||||
version: 2.0.5(@edge-runtime/vm@3.2.0)(@types/node@18.19.45)(happy-dom@14.12.3)(terser@5.31.6)
|
||||
vitest-websocket-mock:
|
||||
specifier: ^0.3.0
|
||||
version: 0.3.0(vitest@2.0.5(@edge-runtime/vm@3.2.0)(@types/node@18.19.45)(happy-dom@14.12.3)(terser@5.31.6))
|
||||
|
||||
packages/ws:
|
||||
dependencies:
|
||||
@@ -2623,6 +2614,14 @@ packages:
|
||||
resolution: {integrity: sha512-98b3i+Y19RFq1Xke4NkVY46x8KjJQjldHUuEbCqMvp1F5Iq9HgnGpu91jOi/Ufazhty32eRsKnnzS8n4c+L93g==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@discordjs/node-pre-gyp@0.4.5':
|
||||
resolution: {integrity: sha512-YJOVVZ545x24mHzANfYoy0BJX5PDyeZlpiJjDkUBM/V/Ao7TFX9lcUvCN4nr0tbr5ubeaXxtEBILUrHtTphVeQ==}
|
||||
hasBin: true
|
||||
|
||||
'@discordjs/opus@0.9.0':
|
||||
resolution: {integrity: sha512-NEE76A96FtQ5YuoAVlOlB3ryMPrkXbUCTQICHGKb8ShtjXyubGicjRMouHtP1RpuDdm16cDa+oI3aAMo1zQRUQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
'@discordjs/rest@2.3.0':
|
||||
resolution: {integrity: sha512-C1kAJK8aSYRv3ZwMG8cvrrW4GN0g5eMdP8AuN8ODH5DyOCbHgJspze1my3xHOAgwLJdKUbWNVyAeJ9cEdduqIg==}
|
||||
engines: {node: '>=16.11.0'}
|
||||
@@ -9553,9 +9552,6 @@ packages:
|
||||
resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
jest-websocket-mock@2.5.0:
|
||||
resolution: {integrity: sha512-a+UJGfowNIWvtIKIQBHoEWIUqRxxQHFx4CXT+R5KxxKBtEQ5rS3pPOV/5299sHzqbmeCzxxY5qE4+yfXePePig==}
|
||||
|
||||
jest-worker@29.7.0:
|
||||
resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
@@ -10665,6 +10661,9 @@ packages:
|
||||
no-case@3.0.4:
|
||||
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
||||
|
||||
node-addon-api@5.1.0:
|
||||
resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==}
|
||||
|
||||
node-dir@0.1.17:
|
||||
resolution: {integrity: sha512-tmPX422rYgofd4epzrNoOXiE8XFZYOcCq1vD7MAXCDO+O+zndlA2ztdKKMa+EeuBG5tHETpr4ml4RGgpqDCCAg==}
|
||||
engines: {node: '>= 0.10.5'}
|
||||
@@ -13375,6 +13374,11 @@ packages:
|
||||
terser:
|
||||
optional: true
|
||||
|
||||
vitest-websocket-mock@0.3.0:
|
||||
resolution: {integrity: sha512-kTEFtfHIUDiiiEBj/CR6WajugqObjnuNdolGRJA3vo3Xt+fmfd1Ghwe+NpJytG6OE57noHOCUXzs2R9XUF0cwg==}
|
||||
peerDependencies:
|
||||
vitest: '>=1 <2'
|
||||
|
||||
vitest@2.0.5:
|
||||
resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==}
|
||||
engines: {node: ^18.0.0 || >=20.0.0}
|
||||
@@ -14850,6 +14854,29 @@ snapshots:
|
||||
dependencies:
|
||||
discord-api-types: 0.37.97
|
||||
|
||||
'@discordjs/node-pre-gyp@0.4.5(encoding@0.1.13)':
|
||||
dependencies:
|
||||
detect-libc: 2.0.3
|
||||
https-proxy-agent: 5.0.1
|
||||
make-dir: 3.1.0
|
||||
node-fetch: 2.7.0(encoding@0.1.13)
|
||||
nopt: 5.0.0
|
||||
npmlog: 5.0.1
|
||||
rimraf: 3.0.2
|
||||
semver: 7.5.4
|
||||
tar: 6.2.1
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
'@discordjs/opus@0.9.0(encoding@0.1.13)':
|
||||
dependencies:
|
||||
'@discordjs/node-pre-gyp': 0.4.5(encoding@0.1.13)
|
||||
node-addon-api: 5.1.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
- supports-color
|
||||
|
||||
'@discordjs/rest@2.3.0':
|
||||
dependencies:
|
||||
'@discordjs/collection': 2.1.0
|
||||
@@ -24231,11 +24258,6 @@ snapshots:
|
||||
jest-util: 29.7.0
|
||||
string-length: 4.0.2
|
||||
|
||||
jest-websocket-mock@2.5.0:
|
||||
dependencies:
|
||||
jest-diff: 29.7.0
|
||||
mock-socket: 9.3.1
|
||||
|
||||
jest-worker@29.7.0:
|
||||
dependencies:
|
||||
'@types/node': 18.19.45
|
||||
@@ -25954,6 +25976,8 @@ snapshots:
|
||||
lower-case: 2.0.2
|
||||
tslib: 2.6.3
|
||||
|
||||
node-addon-api@5.1.0: {}
|
||||
|
||||
node-dir@0.1.17:
|
||||
dependencies:
|
||||
minimatch: 3.1.2
|
||||
@@ -26667,7 +26691,9 @@ snapshots:
|
||||
dependencies:
|
||||
parse-ms: 4.0.0
|
||||
|
||||
prism-media@1.3.5: {}
|
||||
prism-media@1.3.5(@discordjs/opus@0.9.0(encoding@0.1.13)):
|
||||
optionalDependencies:
|
||||
'@discordjs/opus': 0.9.0(encoding@0.1.13)
|
||||
|
||||
proc-log@3.0.0: {}
|
||||
|
||||
@@ -29328,6 +29354,12 @@ snapshots:
|
||||
fsevents: 2.3.3
|
||||
terser: 5.31.6
|
||||
|
||||
vitest-websocket-mock@0.3.0(vitest@2.0.5(@edge-runtime/vm@3.2.0)(@types/node@18.19.45)(happy-dom@14.12.3)(terser@5.31.6)):
|
||||
dependencies:
|
||||
jest-diff: 29.7.0
|
||||
mock-socket: 9.3.1
|
||||
vitest: 2.0.5(@edge-runtime/vm@3.2.0)(@types/node@18.19.45)(happy-dom@14.12.3)(terser@5.31.6)
|
||||
|
||||
vitest@2.0.5(@edge-runtime/vm@3.2.0)(@types/node@16.18.105)(happy-dom@14.12.3)(terser@5.31.6):
|
||||
dependencies:
|
||||
'@ampproject/remapping': 2.3.0
|
||||
|
||||
Reference in New Issue
Block a user