Home Manual Reference Source Test

tests/step3notes.test.js

  1. import { describe, it, expect, vi } from "vitest";
  2. import { timeGrid } from "../src/model/theory.js";
  3. import { FIXTURES, INSTANCES } from "./api.js";
  4. const { step3Params } = FIXTURES;
  5. const { step3 } = INSTANCES;
  6. /** @test {Step3Notes} */
  7. describe("Step3Notes", () => {
  8. /** @test {Step3Notes#schema} */
  9. it("schema exposes top-level descriptors and format functions for pitch, seed, interval and rest; instrument groups carry running GM program numbers", () => {
  10. const params = step3.schema();
  11. expect(params.map((p) => p.name)).toEqual([
  12. "voices",
  13. "voice",
  14. "seed_pct",
  15. "dissonance",
  16. "max_interval",
  17. "rest_pct",
  18. "constraints",
  19. ]);
  20. const voiceTpl = params.find((p) => p.name === "voice").template;
  21. expect(voiceTpl.find((t) => t.name === "min_pitch").format(60)).toBe(
  22. "C<sub>4</sub>",
  23. );
  24. expect(voiceTpl.find((t) => t.name === "max_pitch").format(67)).toBe(
  25. "G<sub>4</sub>",
  26. );
  27. expect(params.find((p) => p.name === "seed_pct").format(25)).toBe("25%");
  28. expect(params.find((p) => p.name === "max_interval").format(6)).toContain(
  29. "Semitones",
  30. );
  31. expect(params.find((p) => p.name === "rest_pct").format(10)).toBe("10%");
  32. const instrument = voiceTpl.find((t) => t.name === "instrument");
  33. expect(instrument.groups).toHaveLength(16);
  34. expect(instrument.groups[0].label).toBe("Piano");
  35. expect(instrument.groups[0].options[0]).toEqual({
  36. value: 0,
  37. label: "Acoustic Grand Piano",
  38. });
  39. expect(instrument.groups[1].options[0]).toEqual({
  40. value: 8,
  41. label: "Celesta",
  42. });
  43. expect(instrument.groups[15].options[7]).toEqual({
  44. value: 127,
  45. label: "Gunshot",
  46. });
  47. });
  48. /** @test {Step3Notes#solve} */
  49. it("solve returns a 'No solution' message on null solver result; exposes solution + time-grid metadata for the composer cache on a real solution; derives bars from chords length when present", async () => {
  50. const noSol = await step3.solve(
  51. step3Params(),
  52. vi.fn().mockResolvedValue(null),
  53. );
  54. expect(noSol.textOutput).toContain("No solution");
  55. expect(noSol.solution).toBeUndefined();
  56. const params = step3Params();
  57. const { subdivision, numSubBeats } = timeGrid(
  58. params.voice,
  59. params.voices,
  60. params.numerator,
  61. params.bars,
  62. );
  63. const sol = { note_0_0: 60 };
  64. const out = await step3.solve(params, vi.fn().mockResolvedValue(sol));
  65. expect(out.textOutput).toContain("solution found");
  66. expect(out.solution).toBe(sol);
  67. expect(out.numSubBeats).toBe(numSubBeats);
  68. expect(out.subdivision).toBe(subdivision);
  69. const fromChords = await step3.solve(
  70. step3Params({ bars: 99, chords: ["I", "V"] }),
  71. vi.fn().mockResolvedValue(sol),
  72. );
  73. expect(fromChords.numSubBeats).toBe(8);
  74. });
  75. });