Conversation
Adds comprehensive test coverage for previously untested piano roll components: - PianoRollConstants: 47 tests (isBlackKey, midiNoteToName, gridSizeToBeats, normalizeMidiVelocity, velocityToColor, visual styles, tool shortcuts) - PianoRollEmptyState: 4 tests (rendering, empty state messaging) - TransformMenu: 13 tests (dropdown, transform types, parameter panels) - GeneratePatternDialog: 10 tests (role/genre/key/scale selectors, density) - QuantizeDialog: 13 tests (grid size, strength, swing, scope, apply/cancel) Piano roll test total: 149 tests across 11 files (was 58 across 6). All 7111 tests pass. Zero TypeScript errors. https://claude.ai/code/session_01AAbmUU34sN8t5XRPsvuyzh
There was a problem hiding this comment.
Pull request overview
Adds Vitest/@testing-library coverage for several previously untested Piano Roll utilities and UI components to increase confidence in note transforms, dialogs, and empty-state rendering (closing #1621).
Changes:
- Introduces new unit tests for
PianoRollConstantsutility functions and visual-style helpers. - Adds basic render/interaction tests for
TransformMenu,QuantizeDialog, andGeneratePatternDialog. - Adds a small render/layout test suite for
PianoRollEmptyState.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/components/pianoroll/tests/PianoRollConstants.test.ts | Adds unit tests for constants, note name/key helpers, velocity helpers, and visual-style functions. |
| src/components/pianoroll/tests/PianoRollEmptyState.test.tsx | Verifies empty-state text, icon presence, and centering layout classes. |
| src/components/pianoroll/tests/TransformMenu.test.tsx | Covers disabled/enabled state and basic navigation between transform list and parameter panels. |
| src/components/pianoroll/tests/GeneratePatternDialog.test.tsx | Adds smoke tests for dialog rendering and key UI controls (role/genre/density/regenerate/cancel). |
| src/components/pianoroll/tests/QuantizeDialog.test.tsx | Adds smoke tests for dialog rendering, labels/controls, and cancel behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -0,0 +1,112 @@ | |||
| import { describe, it, expect, vi, beforeEach } from 'vitest'; | |||
There was a problem hiding this comment.
vi is imported but never used in this test file. Remove it to keep the test dependencies minimal and avoid confusing readers about mocked timers/mocks.
| import { describe, it, expect, vi, beforeEach } from 'vitest'; | |
| import { describe, it, expect, beforeEach } from 'vitest'; |
| function setupDialog() { | ||
| useProjectStore.getState().createProject(); | ||
| useProjectStore.getState().addTrack('midi'); | ||
| useUIStore.setState({ | ||
| showQuantizeDialog: true, | ||
| quantizeTarget: { | ||
| clipId: 'clip-1', | ||
| noteIds: ['note-1', 'note-2', 'note-3'], | ||
| }, | ||
| }); |
There was a problem hiding this comment.
setupDialog() creates a project and adds a track, but it never creates a MIDI clip with id clip-1 or notes with ids note-1..note-3. As a result, QuantizeDialog's preview logic (targetNotes lookup + setQuantizePreviewPositions) never runs in these tests, and the test data doesn't reflect real usage where the target clip/notes exist. Create a pianoRoll track and a real MIDI clip (e.g. via ensureMidiClip / addClip with midiData) and seed it with notes matching quantizeTarget.noteIds.
| const tracks = useProjectStore.getState().project!.tracks; | ||
| const track = tracks[tracks.length - 1]; | ||
| const clip = track.clips[0]; | ||
| useUIStore.setState({ | ||
| showGeneratePatternDialog: true, | ||
| generatePatternClipId: clip?.id ?? 'clip-1', | ||
| }); | ||
| return { clipId: clip?.id ?? 'clip-1' }; |
There was a problem hiding this comment.
setupDialog() adds a track and then reads track.clips[0], but addTrack(...) creates a track with clips: [] (no default clip). This means generatePatternClipId will always fall back to 'clip-1', which likely doesn't exist in the project, so the tests aren't exercising behavior against a real MIDI clip. Consider creating a pianoRoll track and a MIDI clip explicitly (e.g. ensureMidiClip(track.id) or addClip(..., { midiData: { notes: [], grid: '1/16' } })) and using that returned clip id.
| const tracks = useProjectStore.getState().project!.tracks; | |
| const track = tracks[tracks.length - 1]; | |
| const clip = track.clips[0]; | |
| useUIStore.setState({ | |
| showGeneratePatternDialog: true, | |
| generatePatternClipId: clip?.id ?? 'clip-1', | |
| }); | |
| return { clipId: clip?.id ?? 'clip-1' }; | |
| const initialTracks = useProjectStore.getState().project!.tracks; | |
| const initialTrack = initialTracks[initialTracks.length - 1]; | |
| const createdClip = | |
| initialTrack.clips[0] ?? | |
| ({ | |
| id: 'clip-1', | |
| startBeat: 0, | |
| durationBeats: 16, | |
| midiData: { | |
| notes: [], | |
| grid: '1/16', | |
| }, | |
| } as any); | |
| if (!initialTrack.clips[0]) { | |
| useProjectStore.setState((state) => ({ | |
| project: state.project | |
| ? { | |
| ...state.project, | |
| tracks: state.project.tracks.map((track) => | |
| track.id === initialTrack.id | |
| ? { ...track, clips: [...track.clips, createdClip] } | |
| : track | |
| ), | |
| } | |
| : state.project, | |
| })); | |
| } | |
| const tracks = useProjectStore.getState().project!.tracks; | |
| const track = tracks[tracks.length - 1]; | |
| const clip = track.clips[0] ?? createdClip; | |
| useUIStore.setState({ | |
| showGeneratePatternDialog: true, | |
| generatePatternClipId: clip.id, | |
| }); | |
| return { clipId: clip.id }; |
| function setupProject() { | ||
| useProjectStore.getState().createProject(); | ||
| useProjectStore.getState().addTrack('midi'); | ||
| const tracks = useProjectStore.getState().project!.tracks; | ||
| const track = tracks[tracks.length - 1]; | ||
| const clip = track.clips[0]; | ||
| return { trackId: track.id, clipId: clip?.id ?? 'clip-1' }; | ||
| } | ||
|
|
||
| describe('TransformMenu', () => { | ||
| beforeEach(() => { | ||
| setupProject(); | ||
| }); |
There was a problem hiding this comment.
setupProject() adds a track and attempts to return clipId, but tracks are created with clips: [] by default and the returned ids are not used anywhere in the tests. Since these tests currently only validate menu UI state (and never invoke transformMidiNotes), consider removing the store setup entirely, or (if you plan to add apply/side-effect assertions) create a real MIDI clip via ensureMidiClip and pass the real clipId into TransformMenu so the tests reflect real usage.
- QuantizeDialog: remove unused vi import, use real MIDI clip with ensureMidiClip() and seeded notes instead of fake clip-1 id - GeneratePatternDialog: use ensureMidiClip() for real clip id - TransformMenu: remove unnecessary store setup (tests only validate menu UI state, not transform side effects) https://claude.ai/code/session_01AAbmUU34sN8t5XRPsvuyzh
Summary
Test plan
npm test— all 7111 tests passnpx tsc --noEmit— 0 errorsCloses #1621
https://claude.ai/code/session_01AAbmUU34sN8t5XRPsvuyzh