tests/parameters.test.js
- import { describe, it, expect, vi, afterEach } from "vitest";
- import { FIXTURES, MOCKS, STUBS } from "./api.js";
- const { makeStep } = FIXTURES;
- const { fileReader: stubFileReader } = STUBS;
- const { downloadBlob } = await MOCKS.download;
- const { importParameters, exportParameters, buildParameters } =
- await import("../src/controller/parameters.js");
- describe("importParameters", () => {
- afterEach(() => {
- vi.clearAllMocks();
- vi.unstubAllGlobals();
- });
-
- it("parses JSON; rejects on invalid JSON or FileReader error", async () => {
- const data = { step1: { key: 3 } };
- stubFileReader(function () {
- this.onload({ target: { result: JSON.stringify(data) } });
- });
- await expect(importParameters(new Blob())).resolves.toEqual(data);
- stubFileReader(function () {
- this.onload({ target: { result: "not json" } });
- });
- await expect(importParameters(new Blob())).rejects.toThrow(
- "Could not parse",
- );
- stubFileReader(function () {
- this.onerror();
- });
- await expect(importParameters(new Blob())).rejects.toThrow(
- "Could not read the file.",
- );
- });
- });
- describe("exportParameters", () => {
-
- it("serializes theta as JSON blob with parameters.json filename", () => {
- exportParameters({ step1: { key: 0 } });
- expect(downloadBlob).toHaveBeenCalledOnce();
- const [blob, filename] = downloadBlob.mock.calls[0];
- expect(blob.type).toBe("application/json");
- expect(filename).toBe("parameters.json");
- });
- });
- describe("buildParameters", () => {
-
- it("extracts flat parameter defaults for scalars and checkboxes", async () => {
- const step = makeStep(1, [
- { name: "key", type: "number", default: 5 },
- { type: "checkboxes", items: [{ name: "mute", default: false }] },
- ]);
- const { steps, parameters } = await buildParameters([step]);
- expect(parameters.step1.key).toBe(5);
- expect(parameters.step1.mute).toBe(false);
- expect(steps[0].id).toBe("step1");
- expect(steps[0].name).toBe("Step 1");
- });
-
- it("resolves nested defaults across same-step and cross-step counters; falls back when max is missing or counters are unresolvable", async () => {
- const step1 = makeStep(1, [
- { name: "parts", type: "number", default: 2, max: 2 },
- {
- name: "voices",
- type: "nested",
- counter: "parts",
- template: [{ name: "pitch", type: "number", default: 60 }],
- },
- {
- name: "moods",
- type: "nested",
- defaults: [{ pitch: 48 }, { pitch: 72, mute: false }],
- template: [
- { name: "pitch", type: "number", default: 60 },
- { type: "checkboxes", items: [{ name: "mute", default: true }] },
- ],
- },
- ]);
- const step2 = makeStep(2, [
- {
- name: "vols",
- type: "nested",
- counter: "step1.parts",
- template: [{ name: "vol", type: "number", default: 80 }],
- },
- ]);
- const both = await buildParameters([step1, step2]);
- expect(both.parameters.step1.voices).toHaveLength(2);
- expect(both.parameters.step1.voices[0].pitch).toBe(60);
- expect(both.parameters.step1.moods[0].mute).toBe(true);
- expect(both.parameters.step1.moods[1].mute).toBe(false);
- expect(both.parameters.step2.vols).toHaveLength(2);
- expect(both.parameters.step2.vols[0].vol).toBe(80);
- const noMax = await buildParameters([
- makeStep(1, [
- { name: "parts", type: "number", default: 2 },
- {
- name: "voices",
- type: "nested",
- counter: "parts",
- template: [{ name: "pitch", type: "number", default: 60 }],
- },
- ]),
- ]);
- expect(noMax.parameters.step1.voices).toHaveLength(1);
- const unresolvable = await buildParameters([step2]);
- expect(unresolvable.parameters.step2.vols).toHaveLength(1);
- });
- });