test: add 120 tests for 24 mixer effect components#1629
Conversation
…t files The mixer effects area was the largest untested UI section in the codebase: 25 effect cards + shared layout, 3025 LOC, only 1 test file (LfoWaveformPreview). Coverage added: - Dynamics: CompressorCard (11), LimiterCard (5), GateCard (5), DeEsserCard (5), TransientShaperCard (3) - EQ: EQ3Card (3), ParametricEQCard (4) - Time-based: DelayCard (4), ReverbCard (3), AlgorithmicReverbCard (4), ConvolverCard (5), ChorusCard (4), FlangerCard (3), PhaserCard (3) - Distortion: DistortionCard (5), SaturationCard (5) - Stereo: StereoImagerCard (2) - Spectral: SpectralFreezeCard (5), SpectralBlurCard (2), SpectralFilterCard (4), SpectralMorphCard (6) - Noise: NoiseReductionCard (4) - Shared: EffectCardLayout (5), ParamGroup (3), ModeButton (5), effectColors (4), FilterCard (7) - Test helpers: effectTestHelpers.ts with 22 factory functions Quality gates: 0 TypeScript errors, 7140 tests pass (0 regressions), build succeeds. https://claude.ai/code/session_01Q6ZMsrMoPCrnTH8auYHZdd
There was a problem hiding this comment.
Pull request overview
Adds broad Vitest + React Testing Library coverage for the Mixer Effects UI, addressing the large previously-untested surface area described in #1628 by introducing per-effect-card rendering/interaction tests plus shared test infrastructure.
Changes:
- Added new test suites for the majority of mixer effect card components (rendering + basic interactions like mode switches/toggles/selects).
- Added shared effect factories/helpers to simplify constructing
TrackEffectfixtures for tests. - Added tests for shared mixer-effects UI utilities (layout + color resolution).
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| src/components/mixer/effects/tests/AlgorithmicReverbCard.test.tsx | Tests rendering + reverb-type switching for AlgorithmicReverbCard |
| src/components/mixer/effects/tests/ChorusCard.test.tsx | Tests ChorusCard controls + visualization presence |
| src/components/mixer/effects/tests/CompressorCard.test.tsx | Tests CompressorCard controls, sidechain selector behavior, and rAF lifecycle |
| src/components/mixer/effects/tests/ConvolverCard.test.tsx | Tests ConvolverCard controls + IR type select updates |
| src/components/mixer/effects/tests/DeEsserCard.test.tsx | Tests DeEsserCard controls, mode switching, and visualization presence |
| src/components/mixer/effects/tests/DelayCard.test.tsx | Tests DelayCard controls, visualization presence, and wet % display |
| src/components/mixer/effects/tests/DistortionCard.test.tsx | Tests DistortionCard controls, type switching, and curve presence |
| src/components/mixer/effects/tests/EffectCardLayout.test.tsx | Tests shared layout components (EffectCardLayout/ParamGroup/ModeButton) |
| src/components/mixer/effects/tests/effectColors.test.ts | Tests EFFECT_COLORS completeness/format and resolveEffectColor output |
| src/components/mixer/effects/tests/effectTestHelpers.ts | Adds shared factories/helpers for constructing effect fixtures and mocks |
| src/components/mixer/effects/tests/EQ3Card.test.tsx | Tests EQ3Card knob/slider labels and displayed frequency values |
| src/components/mixer/effects/tests/FilterCard.test.tsx | Tests FilterCard modes, LFO toggle, and conditional LFO controls |
| src/components/mixer/effects/tests/FlangerCard.test.tsx | Tests FlangerCard controls and presence of mod/LFO preview components |
| src/components/mixer/effects/tests/GateCard.test.tsx | Tests GateCard controls, mode switching, and curve presence |
| src/components/mixer/effects/tests/LfoWaveformPreview.test.tsx | Tests LfoWaveformPreview rendering and animation behavior |
| src/components/mixer/effects/tests/LimiterCard.test.tsx | Tests LimiterCard controls, style switching, and curve presence |
| src/components/mixer/effects/tests/NoiseReductionCard.test.tsx | Tests NoiseReductionCard controls and mode switching |
| src/components/mixer/effects/tests/ParametricEQCard.test.tsx | Tests ParametricEQCard mode switching and simple-mode footer labels |
| src/components/mixer/effects/tests/PhaserCard.test.tsx | Tests PhaserCard controls and modulation display presence |
| src/components/mixer/effects/tests/ReverbCard.test.tsx | Tests ReverbCard controls and curve presence |
| src/components/mixer/effects/tests/SaturationCard.test.tsx | Tests SaturationCard controls, type switching, and curve presence |
| src/components/mixer/effects/tests/SpectralBlurCard.test.tsx | Tests SpectralBlurCard controls |
| src/components/mixer/effects/tests/SpectralFilterCard.test.tsx | Tests SpectralFilterCard controls, point count display, and reset presence |
| src/components/mixer/effects/tests/SpectralFreezeCard.test.tsx | Tests SpectralFreezeCard controls and freeze toggle updates |
| src/components/mixer/effects/tests/SpectralMorphCard.test.tsx | Tests SpectralMorphCard controls, lock toggle updates, and source selector presence |
| src/components/mixer/effects/tests/StereoImagerCard.test.tsx | Tests StereoImagerCard controls and stereo visualization presence |
| src/components/mixer/effects/tests/TransientShaperCard.test.tsx | Tests TransientShaperCard controls and visualization presence |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // ─── Store mock ────────────────────────────────────────────────────────────── | ||
|
|
||
| const mockUpdateTrackEffect = vi.fn(); | ||
| const mockSetSidechainSource = vi.fn(); | ||
| const mockEnsureAutomationLane = vi.fn(); | ||
| const mockClearAutomationLane = vi.fn(); | ||
|
|
||
| const defaultTracks = [ | ||
| { id: 'track-1', displayName: 'Kick', clips: [], type: 'audio' as const }, | ||
| { id: 'track-2', displayName: 'Snare', clips: [], type: 'audio' as const }, | ||
| ]; | ||
|
|
||
| export function setupProjectStoreMock(overrides: Record<string, unknown> = {}) { | ||
| const mockStore = vi.fn((selector: (s: Record<string, unknown>) => unknown) => { | ||
| const state: Record<string, unknown> = { | ||
| updateTrackEffect: mockUpdateTrackEffect, | ||
| setSidechainSource: mockSetSidechainSource, | ||
| ensureAutomationLane: mockEnsureAutomationLane, | ||
| clearAutomationLane: mockClearAutomationLane, | ||
| project: { tracks: defaultTracks }, | ||
| ...overrides, | ||
| }; | ||
| return selector(state); | ||
| }); | ||
| return { mockStore, mockUpdateTrackEffect, mockSetSidechainSource }; | ||
| } | ||
|
|
There was a problem hiding this comment.
setupProjectStoreMock is exported but not used anywhere in the repo (the tests still hand-roll vi.mock('../../../../store/projectStore', ...) in each file). This is dead code right now and also keeps mock fns in module scope, which can become a cross-test coupling point if it starts being used. Either switch the effect card tests to use this helper (and expose a reset API), or remove it to avoid confusion.
| // ─── Store mock ────────────────────────────────────────────────────────────── | |
| const mockUpdateTrackEffect = vi.fn(); | |
| const mockSetSidechainSource = vi.fn(); | |
| const mockEnsureAutomationLane = vi.fn(); | |
| const mockClearAutomationLane = vi.fn(); | |
| const defaultTracks = [ | |
| { id: 'track-1', displayName: 'Kick', clips: [], type: 'audio' as const }, | |
| { id: 'track-2', displayName: 'Snare', clips: [], type: 'audio' as const }, | |
| ]; | |
| export function setupProjectStoreMock(overrides: Record<string, unknown> = {}) { | |
| const mockStore = vi.fn((selector: (s: Record<string, unknown>) => unknown) => { | |
| const state: Record<string, unknown> = { | |
| updateTrackEffect: mockUpdateTrackEffect, | |
| setSidechainSource: mockSetSidechainSource, | |
| ensureAutomationLane: mockEnsureAutomationLane, | |
| clearAutomationLane: mockClearAutomationLane, | |
| project: { tracks: defaultTracks }, | |
| ...overrides, | |
| }; | |
| return selector(state); | |
| }); | |
| return { mockStore, mockUpdateTrackEffect, mockSetSidechainSource }; | |
| } |
| // ─── EffectsEngine mock ────────────────────────────────────────────────────── | ||
|
|
||
| export const mockEffectsEngine = { | ||
| updateEffectParams: vi.fn(), | ||
| getCompressorReduction: vi.fn(() => 0), | ||
| getSidechainReduction: vi.fn(() => 0), | ||
| getGateReduction: vi.fn(() => 0), | ||
| getDeEsserReduction: vi.fn(() => 0), | ||
| getLimiterReduction: vi.fn(() => 0), | ||
| getSpectralData: vi.fn(() => new Float32Array(128)), | ||
| getParametricEQSpectrumData: vi.fn(() => null), | ||
| }; | ||
|
|
There was a problem hiding this comment.
mockEffectsEngine is exported but not referenced anywhere in the repo (each test file mocks effectsEngine inline instead). Consider either updating the effect tests to reuse this shared mock (and resetting it per test), or deleting it so effectTestHelpers.ts only contains helpers that are actually used.
| // ─── EffectsEngine mock ────────────────────────────────────────────────────── | |
| export const mockEffectsEngine = { | |
| updateEffectParams: vi.fn(), | |
| getCompressorReduction: vi.fn(() => 0), | |
| getSidechainReduction: vi.fn(() => 0), | |
| getGateReduction: vi.fn(() => 0), | |
| getDeEsserReduction: vi.fn(() => 0), | |
| getLimiterReduction: vi.fn(() => 0), | |
| getSpectralData: vi.fn(() => new Float32Array(128)), | |
| getParametricEQSpectrumData: vi.fn(() => null), | |
| }; |
|
|
||
| export function makeGateEffect(overrides: Record<string, unknown> = {}) { | ||
| return makeEffect('gate', { | ||
| threshold: -40, range: -80, attack: 0.5, hold: 10, release: 50, |
There was a problem hiding this comment.
makeGateEffect uses attack: 0.5, hold: 10, release: 50, which are wildly out of range relative to the GateCard UI (it multiplies these by 1000 and expects ~0.1–50ms attack, 0–500ms hold, 5–4000ms release). This makes the factory produce unrealistic/default-invalid params and could cause confusing renders or future tests to fail. Update the defaults to match the units expected by GateCard (e.g., values in seconds such as 0.001/0.01/0.05, or whatever the real engine units are).
| threshold: -40, range: -80, attack: 0.5, hold: 10, release: 50, | |
| threshold: -40, range: -80, attack: 0.001, hold: 0.01, release: 0.05, |
| ); | ||
| // Should just have the children wrapper, no mode/viz/footer wrappers | ||
| expect(screen.getByText('Only Children')).toBeDefined(); | ||
| expect(container.querySelectorAll('[style]').length).toBeGreaterThanOrEqual(1); |
There was a problem hiding this comment.
The "omits sections" assertion is currently ineffective: container.querySelectorAll('[style]') will always be >= 1 because EffectCardLayout always applies an inline style (the stagger animation) to the children wrapper even when mode/visualization/footer are omitted. This test should explicitly assert that the mode/visualization/footer wrappers are absent (e.g., by querying for something unique to those wrappers, or adding stable testids to the layout sections).
| expect(container.querySelectorAll('[style]').length).toBeGreaterThanOrEqual(1); | |
| expect(container.firstElementChild).not.toBeNull(); | |
| expect(container.firstElementChild?.childElementCount).toBe(1); |
| describe('resolveEffectColor', () => { | ||
| it('returns fallback hex when document is undefined', () => { | ||
| const color = resolveEffectColor('compressor'); | ||
| expect(color).toBe(EFFECT_COLORS.compressor); | ||
| }); | ||
|
|
There was a problem hiding this comment.
The test description "returns fallback hex when document is undefined" doesn’t match the configured Vitest environment (jsdom), where document is always defined. As written, this test is really asserting the fallback path when the CSS custom property resolves to an empty string. Either stub globalThis.document to undefined for this test, or rename the test to reflect what it’s actually validating.
- Remove unused setupProjectStoreMock and mockEffectsEngine exports from effectTestHelpers.ts (dead code, cross-test coupling risk) - Fix makeGateEffect defaults to use realistic seconds-based values (0.001s attack, 0.01s hold, 0.05s release) matching GateCard's expected input range - Fix EffectCardLayout "omits sections" test to assert actual child count instead of ineffective style query - Fix effectColors test description to accurately reflect jsdom behavior (CSS custom property empty fallback, not undefined document) https://claude.ai/code/session_01Q6ZMsrMoPCrnTH8auYHZdd
Summary
Closes #1628
The mixer effects area was the largest untested UI section in the codebase: 25 effect cards + shared layout components, ~3025 LOC, with only 1 test file (LfoWaveformPreview).
This PR adds 120 new tests across 25 new test files, covering all 24 mixer effect card components plus shared layout utilities.
Coverage Added
Tests Verify
Quality Gates
Test plan
npx tsc --noEmit)https://claude.ai/code/session_01Q6ZMsrMoPCrnTH8auYHZdd