import { describe, it, expect } from 'vitest' import { chromaAt, pcAt } from './fretboard' import { triadNotes, diatonicChords, scaleNotes, intervalTarget } from './theory' import { triadShape, STRING_SETS } from './triads' import { cagedShapes } from './caged' import { deriveRoles } from '../../drills' describe('fretboard mapping', () => { it('maps open strings and frets correctly (index 0 = high e)', () => { expect(pcAt(0, 0)).toBe('E') // high e open expect(pcAt(5, 0)).toBe('E') // low E open expect(pcAt(5, 3)).toBe('G') // low E, 3rd fret expect(pcAt(4, 2)).toBe('B') // A string, 2nd fret expect(chromaAt(0, 0)).toBe(4) // E }) }) describe('theory helpers', () => { it('builds triads and diatonic chords', () => { expect(triadNotes('C', 'major')).toEqual(['C', 'E', 'G']) expect(triadNotes('A', 'minor')).toEqual(['A', 'C', 'E']) expect(diatonicChords('G').map((c) => c.name)).toEqual([ 'G', 'Am', 'Bm', 'C', 'D', 'Em', 'F#dim', ]) expect(scaleNotes('G', 'dorian')).toEqual(['G', 'A', 'Bb', 'C', 'D', 'E', 'F']) expect(intervalTarget('A', '5P')).toBe('E') }) }) describe('deriveRoles', () => { it('accents, downbeats, and subdivisions land correctly', () => { // 4/4 sixteenths, accent on beat 1. const roles = deriveRoles(16, 4, [0]) expect(roles[0]).toBe('accent') expect(roles[4]).toBe('normal') // beat 2 downbeat expect(roles[1]).toBe('subdivision') }) }) describe('triad inversions', () => { it('produces C major tones on the 1-2-3 string set', () => { const set = STRING_SETS.find((s) => s.id === '123')! for (const inv of ['root', '1st', '2nd']) { const shape = triadShape('C', 'major', set, inv) const chromas = shape.frets .map((f, string) => (f >= 0 ? chromaAt(string, f) : null)) .filter((c): c is number => c !== null) .sort((a, b) => a - b) // C(0) E(4) G(7) expect(chromas).toEqual([0, 4, 7]) } }) }) describe('CAGED shapes', () => { it('returns 5 shapes with the E-shape barre at fret 3 for key G', () => { const shapes = cagedShapes('G') expect(shapes).toHaveLength(5) const eShape = shapes.find((s) => s.form === 'E shape')! expect(eShape.barreFret).toBe(3) // Every marked position should be a chord tone of G major (G B D). const gTones = new Set([7, 11, 2]) for (const p of shapes[0].positions) { expect(gTones.has(chromaAt(p.string, p.fret))).toBe(true) } }) })