chore: Upgrade to Vitest 4 and regroup tests for performance (#2618)

This commit is contained in:
Dermot Duffy
2026-07-26 11:28:58 -07:00
committed by GitHub
parent a0fd464d23
commit 0c3d46ad3d
36 changed files with 1041 additions and 1207 deletions
@@ -1,4 +1,4 @@
import { describe, expect, it, vi } from 'vitest';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { ActionSet } from '../../../../src/card-controller/actions/actions/set';
import { createLogAction } from '../../../../src/utils/action';
@@ -6,6 +6,10 @@ import { arrayify } from '../../../../src/utils/basic';
import { createCardAPI } from '../../../test-utils';
describe('ActionSet', () => {
afterEach(() => {
vi.restoreAllMocks();
});
const createAPI = () => {
const api = createCardAPI();
vi.mocked(api.getLockManager().getAllowedActions).mockImplementation((actions) =>
+6 -2
View File
@@ -29,9 +29,13 @@ import {
// Replace Ringtone with a fresh `mock<Ringtone>()` per construction so each
// CallManager gets an isolated, type-safe ringtone we can assert on. The
// real Ringtone creates an AudioContext, which we never want in tests.
// real Ringtone creates an AudioContext, which we never want in tests. The
// implementation must be callable with `new`, so it cannot be an arrow
// function.
vi.mock('../../../src/card-controller/call/ringtone', () => ({
Ringtone: vi.fn().mockImplementation(() => mock<Ringtone>()),
Ringtone: vi.fn().mockImplementation(function () {
return mock<Ringtone>();
}),
}));
// Each test creates a new CallManager which constructs a new Ringtone, so the
+18 -6
View File
@@ -12,20 +12,32 @@ import type { RingtoneConfig } from '../../../src/config/schema/live';
// Each tone constructor returns a fresh `mock<>()` per `new` call; the mock
// implementation persists across `vi.clearAllMocks()` (which only clears call
// records, not implementations) so tests don't need per-test re-installation.
// The implementations must be callable with `new`, so they cannot be arrow
// functions.
vi.mock('../../../src/card-controller/call/tones/chime', () => ({
ChimeTone: vi.fn().mockImplementation(() => mock<ChimeTone>()),
ChimeTone: vi.fn().mockImplementation(function () {
return mock<ChimeTone>();
}),
}));
vi.mock('../../../src/card-controller/call/tones/westminster', () => ({
WestminsterTone: vi.fn().mockImplementation(() => mock<WestminsterTone>()),
WestminsterTone: vi.fn().mockImplementation(function () {
return mock<WestminsterTone>();
}),
}));
vi.mock('../../../src/card-controller/call/tones/arpeggio', () => ({
ArpeggioTone: vi.fn().mockImplementation(() => mock<ArpeggioTone>()),
ArpeggioTone: vi.fn().mockImplementation(function () {
return mock<ArpeggioTone>();
}),
}));
vi.mock('../../../src/card-controller/call/tones/melody', () => ({
MelodyTone: vi.fn().mockImplementation(() => mock<MelodyTone>()),
MelodyTone: vi.fn().mockImplementation(function () {
return mock<MelodyTone>();
}),
}));
vi.mock('../../../src/card-controller/call/tones/custom', () => ({
CustomTone: vi.fn().mockImplementation(() => mock<CustomTone>()),
CustomTone: vi.fn().mockImplementation(function () {
return mock<CustomTone>();
}),
}));
// Returns the most recently constructed instance of a mocked class.
@@ -187,7 +199,7 @@ describe('lock', () => {
describe('natural finish', () => {
it('should release the lock when the tone fires its finished handler', () => {
let finishedHandler: (() => void) | undefined;
vi.mocked(ChimeTone).mockImplementationOnce(() => {
vi.mocked(ChimeTone).mockImplementationOnce(function () {
const tone = mock<ChimeTone>();
vi.mocked(tone.start).mockImplementation((handler) => {
finishedHandler = handler;
@@ -8,7 +8,7 @@ interface AudioMocks {
// and pushed here in construction order, so tests can dispatch real events
// and read real properties (`loop`, `currentTime`, etc.) on the instances.
instances: HTMLAudioElement[];
ctor: Mock<[string?], HTMLAudioElement>;
ctor: Mock<(url?: string) => HTMLAudioElement>;
}
// Uses real jsdom HTMLAudioElement instances and only stubs the parts jsdom
@@ -33,7 +33,10 @@ const useAudioElementMocks = (): AudioMocks => {
vi.spyOn(HTMLMediaElement.prototype, 'pause').mockImplementation(() => {});
const RealAudio = window.Audio;
handle.ctor = vi.fn((url?: string) => {
// The source calls `new Audio(...)`, and a mock implementation must be
// callable with `new`, so it cannot be an arrow function.
handle.ctor = vi.fn(function (url?: string) {
const audio = new RealAudio(url);
handle.instances.push(audio);
return audio;
@@ -10,7 +10,7 @@ import { mock, type MockProxy } from 'vitest-mock-extended';
// over the same observable state.
interface AudioMocks {
audioContext: MockProxy<AudioContext>;
audioContextCtor: Mock<[], MockProxy<AudioContext>>;
audioContextCtor: Mock<() => MockProxy<AudioContext>>;
// Filled in the order `createOscillator()` / `createGain()` were called.
oscillators: MockProxy<OscillatorNode>[];
@@ -73,7 +73,11 @@ export const useAudioMocks = (): AudioMocks => {
configurable: true,
});
audio.audioContextCtor = vi.fn(() => audio.audioContext);
// The source calls `new AudioContext()`, and a mock implementation must be
// callable with `new`, so it cannot be an arrow function.
audio.audioContextCtor = vi.fn(function () {
return audio.audioContext;
});
vi.stubGlobal('AudioContext', audio.audioContextCtor);
});
@@ -5,28 +5,43 @@ import type { EffectComponent } from '../../../src/card-controller/effects/types
import type { EffectName } from '../../../src/types';
import { flushPromises } from '../../test-utils';
// The source constructs each effect component with `new`, and a mock
// implementation must be callable with `new`, so it cannot be an arrow
// function.
vi.mock('../../../src/components/effects/fireworks', () => ({
AdvancedCameraCardEffectFireworks: vi.fn(() => createMockEffectComponent()),
AdvancedCameraCardEffectFireworks: vi.fn(function () {
return createMockEffectComponent();
}),
}));
vi.mock('../../../src/components/effects/check', () => ({
AdvancedCameraCardEffectCheck: vi.fn(() => createMockEffectComponent()),
AdvancedCameraCardEffectCheck: vi.fn(function () {
return createMockEffectComponent();
}),
}));
vi.mock('../../../src/components/effects/ghost', () => ({
AdvancedCameraCardEffectGhost: vi.fn(() => createMockEffectComponent()),
AdvancedCameraCardEffectGhost: vi.fn(function () {
return createMockEffectComponent();
}),
}));
vi.mock('../../../src/components/effects/hearts', () => ({
AdvancedCameraCardEffectHearts: vi.fn(() => createMockEffectComponent()),
AdvancedCameraCardEffectHearts: vi.fn(function () {
return createMockEffectComponent();
}),
}));
vi.mock('../../../src/components/effects/shamrocks', () => ({
AdvancedCameraCardEffectShamrocks: vi.fn(() => createMockEffectComponent()),
AdvancedCameraCardEffectShamrocks: vi.fn(function () {
return createMockEffectComponent();
}),
}));
vi.mock('../../../src/components/effects/snow', () => ({
AdvancedCameraCardEffectSnow: vi.fn(() => createMockEffectComponent()),
AdvancedCameraCardEffectSnow: vi.fn(function () {
return createMockEffectComponent();
}),
}));
const createMockEffectComponent = (): EffectComponent => {
@@ -11,6 +11,7 @@ vi.mock('../../../src/card-controller/fullscreen/factory');
describe('FullscreenManager', () => {
afterEach(() => {
vi.restoreAllMocks();
vi.clearAllMocks();
});
it('should initialize', () => {
@@ -29,6 +29,7 @@ describe('ScreenfullFullScreenProvider', () => {
afterEach(() => {
vi.restoreAllMocks();
vi.clearAllMocks();
});
describe('should connect', () => {