Home Manual Reference Source Test

tests/step2chords.test.js

  1. import { describe, it, expect, vi, afterEach } from "vitest";
  2. import { chordGraph } from "../src/model/theory.js";
  3. import { FIXTURES, INSTANCES, STUBS } from "./api.js";
  4. const { step2Params } = FIXTURES;
  5. const { step2 } = INSTANCES;
  6. /** @test {Step2Chords} */
  7. describe("Step2Chords", () => {
  8. afterEach(() => vi.unstubAllGlobals());
  9. /** @test {Step2Chords#schema} */
  10. it("schema appends the preset list from index.json; falls back to manual-only on fetch failure", async () => {
  11. STUBS.fetchJson([{ id: "bach", name: "Bach", works: 412 }]);
  12. const okParams = await step2.schema();
  13. const okStyle = okParams
  14. .find((p) => p.name === "part")
  15. .template.find((t) => t.name === "style");
  16. expect(okStyle.options).toEqual(["manual", "bach"]);
  17. expect(okStyle.labels).toEqual(["Manual", "Bach (412)"]);
  18. STUBS.fetchReject("nope");
  19. STUBS.silenceConsole("warn");
  20. const failParams = await step2.schema();
  21. const failStyle = failParams
  22. .find((p) => p.name === "part")
  23. .template.find((t) => t.name === "style");
  24. expect(failStyle.options).toEqual(["manual"]);
  25. });
  26. /** @test {Step2Chords#solve} */
  27. it("solve emits an NC-filled progression with chord-graph data and an empty concreteProgression in disabled mode without invoking the solver", async () => {
  28. const solver = vi.fn();
  29. const out = await step2.solve(
  30. { enable: false, mode: "major", key: 0, bars: 3, preset: null },
  31. solver,
  32. );
  33. expect(solver).not.toHaveBeenCalled();
  34. expect(out.chords).toEqual(["NC", "NC", "NC"]);
  35. expect(out.chordGraph.path).toBeNull();
  36. expect(out.chordGraph.graph.nodes).toContain("I");
  37. expect(out.textOutput).toContain("disabled");
  38. expect(out.concreteProgression).toBe("");
  39. });
  40. /** @test {Step2Chords#solve} */
  41. it("solve returns progression, cost and path on solver success; reports 'No chord progression found' when path is null", async () => {
  42. const params = { ...step2Params({ bars: 3 }), key: 0 };
  43. const { nodes } = chordGraph(params.mode, params.preset);
  44. const intOf = Object.fromEntries(nodes.map((node, i) => [node, i + 1]));
  45. const okSolver = vi.fn().mockResolvedValue({
  46. path: [intOf["I"], intOf["V"], intOf["I"]],
  47. total_cost: 4,
  48. });
  49. const ok = await step2.solve(params, okSolver);
  50. expect(ok.chords).toEqual(["I", "V", "I"]);
  51. expect(ok.concreteProgression).toBe("C -> G -> C");
  52. expect(ok.textOutput).toContain("Chord Progression Found");
  53. expect(ok.textOutput).toContain("Total cost: 4");
  54. expect(ok.chordGraph.path.nodes).toEqual(["I", "V", "I"]);
  55. expect(ok.chordGraph.path.edges).toBeInstanceOf(Set);
  56. const fail = await step2.solve(params, vi.fn().mockResolvedValue(null));
  57. expect(fail.textOutput).toBe("No chord progression found.");
  58. expect(fail.chordGraph.path).toBeNull();
  59. expect(fail.chords).toEqual([]);
  60. expect(fail.concreteProgression).toBe("");
  61. });
  62. });