Home Manual Reference Source Test

tests/parameters.test.js

  1. import { describe, it, expect, vi, afterEach } from "vitest";
  2. import { FIXTURES, MOCKS, STUBS } from "./api.js";
  3. const { makeStep } = FIXTURES;
  4. const { fileReader: stubFileReader } = STUBS;
  5. const { downloadBlob } = await MOCKS.download;
  6. const { importParameters, exportParameters, buildParameters } =
  7. await import("../src/controller/parameters.js");
  8. /** @test {importParameters} */
  9. describe("importParameters", () => {
  10. afterEach(() => {
  11. vi.clearAllMocks();
  12. vi.unstubAllGlobals();
  13. });
  14. /** @test {importParameters} */
  15. it("parses JSON; rejects on invalid JSON or FileReader error", async () => {
  16. const data = { step1: { key: 3 } };
  17. stubFileReader(function () {
  18. this.onload({ target: { result: JSON.stringify(data) } });
  19. });
  20. await expect(importParameters(new Blob())).resolves.toEqual(data);
  21. stubFileReader(function () {
  22. this.onload({ target: { result: "not json" } });
  23. });
  24. await expect(importParameters(new Blob())).rejects.toThrow(
  25. "Could not parse",
  26. );
  27. stubFileReader(function () {
  28. this.onerror();
  29. });
  30. await expect(importParameters(new Blob())).rejects.toThrow(
  31. "Could not read the file.",
  32. );
  33. });
  34. });
  35. /** @test {exportParameters} */
  36. describe("exportParameters", () => {
  37. /** @test {exportParameters} */
  38. it("serializes theta as JSON blob with parameters.json filename", () => {
  39. exportParameters({ step1: { key: 0 } });
  40. expect(downloadBlob).toHaveBeenCalledOnce();
  41. const [blob, filename] = downloadBlob.mock.calls[0];
  42. expect(blob.type).toBe("application/json");
  43. expect(filename).toBe("parameters.json");
  44. });
  45. });
  46. /** @test {buildParameters} */
  47. describe("buildParameters", () => {
  48. /** @test {buildParameters} */
  49. it("extracts flat parameter defaults for scalars and checkboxes", async () => {
  50. const step = makeStep(1, [
  51. { name: "key", type: "number", default: 5 },
  52. { type: "checkboxes", items: [{ name: "mute", default: false }] },
  53. ]);
  54. const { steps, parameters } = await buildParameters([step]);
  55. expect(parameters.step1.key).toBe(5);
  56. expect(parameters.step1.mute).toBe(false);
  57. expect(steps[0].id).toBe("step1");
  58. expect(steps[0].name).toBe("Step 1");
  59. });
  60. /** @test {buildParameters} */
  61. it("resolves nested defaults across same-step and cross-step counters; falls back when max is missing or counters are unresolvable", async () => {
  62. const step1 = makeStep(1, [
  63. { name: "parts", type: "number", default: 2, max: 2 },
  64. {
  65. name: "voices",
  66. type: "nested",
  67. counter: "parts",
  68. template: [{ name: "pitch", type: "number", default: 60 }],
  69. },
  70. {
  71. name: "moods",
  72. type: "nested",
  73. defaults: [{ pitch: 48 }, { pitch: 72, mute: false }],
  74. template: [
  75. { name: "pitch", type: "number", default: 60 },
  76. { type: "checkboxes", items: [{ name: "mute", default: true }] },
  77. ],
  78. },
  79. ]);
  80. const step2 = makeStep(2, [
  81. {
  82. name: "vols",
  83. type: "nested",
  84. counter: "step1.parts",
  85. template: [{ name: "vol", type: "number", default: 80 }],
  86. },
  87. ]);
  88. const both = await buildParameters([step1, step2]);
  89. expect(both.parameters.step1.voices).toHaveLength(2);
  90. expect(both.parameters.step1.voices[0].pitch).toBe(60);
  91. expect(both.parameters.step1.moods[0].mute).toBe(true); // perItem missing -> cb.default
  92. expect(both.parameters.step1.moods[1].mute).toBe(false); // perItem override
  93. expect(both.parameters.step2.vols).toHaveLength(2);
  94. expect(both.parameters.step2.vols[0].vol).toBe(80);
  95. const noMax = await buildParameters([
  96. makeStep(1, [
  97. { name: "parts", type: "number", default: 2 },
  98. {
  99. name: "voices",
  100. type: "nested",
  101. counter: "parts",
  102. template: [{ name: "pitch", type: "number", default: 60 }],
  103. },
  104. ]),
  105. ]);
  106. expect(noMax.parameters.step1.voices).toHaveLength(1);
  107. const unresolvable = await buildParameters([step2]);
  108. expect(unresolvable.parameters.step2.vols).toHaveLength(1);
  109. });
  110. });