import { assert, assertEquals } from "@std/assert"; import { chordName, parseChord } from "./chord.ts"; import { connections } from "./connect.ts"; import { parseNote } from "./pitch.ts"; const C = { tonic: parseNote("C")!.note, mode: "ionian" as const }; const chord = (s: string) => parseChord(s, C)!; const routes = (prev: string | undefined, target: string) => connections(prev ? chord(prev) : undefined, chord(target)).map( r => `${r.name}: ${r.chords.map(chordName).join(" ")}`, ); Deno.test("the way in is coloured like where it's going", () => { const into = (target: string) => routes(undefined, target).slice(0, 2); assertEquals(into("Cmaj9"), ["V: G9", "ii–V: Dm9 G9"]); assertEquals(into("Cmaj7"), ["V: G7", "ii–V: Dm7 G7"]); assertEquals(into("C"), ["V: G7", "ii–V: Dm7 G7"]); // into a minor chord: the minor key's ii and V assertEquals(into("Am"), ["V: E7♭9", "ii–V: Bm7♭5 E7♭9"]); }); Deno.test("the ways in that follow on best from the chord before come first", () => { // from Am7, down a fifth to the ii assertEquals(routes("Am7", "Cmaj7")[0], "ii–V: Dm7 G7"); // from E♭maj7, down a fifth to ♭VI, then a semitone down to ii; and C's own shape planed down // the scale, ♭III–♭II–I, as the autocomposer does const fromThree = routes("Ebmaj7", "Cmaj7"); assertEquals(fromThree.slice(0, 2), ["♭VI–V: A♭maj7 G7", "ii–V: Dm7 G7"]); assert(fromThree.includes("planed ♭II: D♭maj7")); // from Dm7, the ii is already there: no playing it again const fromTwo = routes("Dm7", "Cmaj7"); assertEquals(fromTwo[0], "V: G7"); assert(!fromTwo.some(r => r.startsWith("ii–V"))); }); Deno.test("colourful ways in, where they fit", () => { const all = routes(undefined, "Cmaj7"); assert(all.includes("♭VI–V: A♭maj7 G7")); assert(all.includes("iv–♭VII: Fm7 B♭7")); assert(all.includes("iii–VI–ii–V: Em7 A7 Dm7 G7")); assert(all.includes("planed ♭II: D♭maj7")); // coltrane's major thirds, only from a major third away assert(!all.some(r => r.startsWith("coltrane"))); assert(routes("Abmaj7", "Cmaj7").includes("coltrane: B7 Emaj7 G7")); // no backdoor or iii–VI into a minor chord assert(!routes(undefined, "Am").some(r => r.startsWith("iv–") || r.startsWith("iii–"))); }); Deno.test("ways in are spelled without double flats or sharps", () => { for (const target of ["Ab", "Db", "Gbmaj7", "F#m", "C#"]) { for (const r of routes(undefined, target)) assert(!/𝄫|𝄪|♭♭|♯♯/.test(r), `${target}: ${r}`); } assert(routes(undefined, "Ab").includes("subV: A7")); });