import { assertEquals } from "@std/assert"; import { canonBeat, type ChordEvent, copyChords, insertLeadIn, moveTrack, newChordTrack, newProject, pasteChords, placeChord, } from "./model.ts"; const layout = (chords: ChordEvent[]) => chords.map(c => [c.symbol, c.start, c.start + c.dur]); Deno.test( "a placed chord cuts away what it overlaps, splitting a chord it lands inside", () => { const track = newChordTrack(); placeChord(track, { id: "a", start: 0, dur: 4, symbol: "C" }); placeChord(track, { id: "b", start: 1, dur: 0.25, symbol: "F" }); assertEquals(layout(track.chords), [ ["C", 0, 1], ["F", 1, 1.25], ["C", 1.25, 4], ]); placeChord(track, { id: "c", start: 0.75, dur: 1, symbol: "G" }); assertEquals(layout(track.chords), [ ["C", 0, 0.75], ["G", 0.75, 1.75], ["C", 1.75, 4], ]); }, ); Deno.test("chords on a triplet grid meet exactly, without slivers left over", () => { const third = 1 / 3; const track = newChordTrack(); // a chord placed by clicking: snapped straight onto the grid, at exactly 2 placeChord(track, { id: "g", start: 6 * third, dur: third, symbol: "G" }); // then six typed from the start, the cursor adding up thirds as it goes (to 1.9999999999999998) let at = 0; for (const symbol of ["C", "F", "G", "C", "Am", "Dm", "E"]) { placeChord(track, { id: symbol + at, start: at, dur: third, symbol }); at += third; } assertEquals( track.chords.map(c => c.symbol), ["C", "F", "G", "C", "Am", "Dm", "E"], ); for (const [a, b] of track.chords.slice(1).map((c, i) => [track.chords[i], c])) { assertEquals(canonBeat(a.start + a.dur), b.start); } }); Deno.test("a lead-in takes the back half of the chord before, so the target stays put", () => { const track = newChordTrack(); placeChord(track, { id: "a", start: 0, dur: 4, symbol: "C" }); placeChord(track, { id: "b", start: 4, dur: 4, symbol: "Am" }); assertEquals(insertLeadIn(track, "b", ["E7"], 0.5), true); assertEquals(layout(track.chords), [ ["C", 0, 2], ["E7", 2, 4], ["Am", 4, 8], ]); // a ii–V shares the space assertEquals(insertLeadIn(track, "a", ["Dm7", "G7"], 0.5), true); assertEquals(layout(track.chords).slice(0, 3), [ ["Dm7", 0, 0.5], ["G7", 0.5, 1], ["C", 1, 2], ]); }); Deno.test("with nothing right before it, a lead-in takes the target's front half", () => { const track = newChordTrack(); placeChord(track, { id: "a", start: 4, dur: 4, symbol: "Am" }); assertEquals(insertLeadIn(track, "a", ["E7"], 1), true); assertEquals(layout(track.chords), [ ["E7", 4, 6], ["Am", 6, 8], ]); // no room left on a whole-bar grid assertEquals(insertLeadIn(track, "a", ["E7"], 4), false); assertEquals(layout(track.chords), [ ["E7", 4, 6], ["Am", 6, 8], ]); }); Deno.test("copied chords paste with their spacing, over what's there", () => { const track = newChordTrack(); for (const [start, dur, symbol] of [ [0, 4, "C"], [4, 2, "F"], [6, 2, "G"], [8, 4, "Am"], ] as const) { placeChord(track, { id: symbol, start, dur, symbol }); } // F and Am: not next to each other const clip = copyChords(track, ["F", "Am"])!; assertEquals(clip.span, 8); const ids = pasteChords(track, clip, 1); // the pasted Am (beats 5 to 9) writes over the end of F, all of G, and the start of Am assertEquals(layout(track.chords), [ ["C", 0, 1], ["F", 1, 3], ["C", 3, 4], ["F", 4, 5], ["Am", 5, 9], ["Am", 9, 12], ]); assertEquals(ids.length, 2); // a hand voicing comes along, and isn't shared with the original const other = newChordTrack(); placeChord(other, { id: "G", start: 0, dur: 2, symbol: "G", notes: [43, 59, 62, 67] }); const [g] = pasteChords(track, copyChords(other, ["G"])!, 12); const pasted = track.chords.find(c => c.id === g)!; assertEquals(pasted.notes, [43, 59, 62, 67]); pasted.notes!.push(72); assertEquals(other.chords[0].notes, [43, 59, 62, 67]); }); Deno.test("tracks move to before the track they're dropped on, or to the end", () => { const p = newProject(); p.tracks = ["a", "b", "c", "d"].map(name => ({ ...newChordTrack(name), id: name })); const order = () => p.tracks.map(t => t.id).join(""); moveTrack(p, "a", 2); assertEquals(order(), "bacd"); moveTrack(p, "d", 0); assertEquals(order(), "dbac"); moveTrack(p, "b", 4); assertEquals(order(), "dacb"); });