import { describe, expect, it } from "vitest"; import { defineToolcraft, fireEvent, renderControlsPanelWithSchema, screen, waitFor, } from "../testing/controls-panel-test-utils"; describe("ControlsPanel curves", () => { it("renders single curves without RGB channel tabs", () => { const schema = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { easing: { defaultValue: { activeChannel: "RGB", points: { RGB: [ { x: 0, y: 0 }, { x: 1, y: 1 }, ], }, }, label: "Easing", target: "animation.easing", type: "curves", variant: "single", }, speed: { defaultValue: 1, label: "Speed", max: 2, min: 0, target: "animation.speed", type: "slider", }, }, title: "Motion", }, ], title: "Controls", }, }, }); const { container } = renderControlsPanelWithSchema(schema); expect(screen.getByRole("img", { name: "Easing curve editor" })).toBeTruthy(); expect( screen .getAllByText("Easing") .some((element) => element.closest("[data-control-field-label]")), ).toBeTruthy(); expect( screen .getByRole("img", { name: "Easing curve editor" }) .closest("[data-control-item-compound-context]"), ).toBeNull(); expect( container.querySelector('[data-curve-interpolation="monotone"]'), ).toBeTruthy(); expect(screen.queryByText("RGB")).toBeNull(); expect(screen.queryByText("R")).toBeNull(); expect(screen.queryByText("G")).toBeNull(); expect(screen.queryByText("B")).toBeNull(); }); it("keeps RGB curves as sectioned compound controls with channel tabs", () => { const schema = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { curves: { defaultValue: { activeChannel: "RGB", points: { B: [ { x: 0, y: 0 }, { x: 1, y: 1 }, ], G: [ { x: 0, y: 0 }, { x: 1, y: 1 }, ], R: [ { x: 0, y: 0 }, { x: 1, y: 1 }, ], RGB: [ { x: 0, y: 0 }, { x: 1, y: 1 }, ], }, }, label: "Tone curves", target: "tone.curves", type: "curves", }, intensity: { defaultValue: 50, label: "Intensity", max: 100, min: 0, target: "tone.intensity", type: "slider", }, }, title: "Tone", }, ], title: "Controls", }, }, }); const { container } = renderControlsPanelWithSchema(schema); expect(screen.getByText("RGB")).toBeTruthy(); expect(screen.getByText("R")).toBeTruthy(); expect( container.querySelector('[data-control-section-divider="compound"]'), ).toBeTruthy(); }); it("passes explicit curve interpolation from schema", () => { const schema = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { toneCurve: { defaultValue: { activeChannel: "RGB", points: { RGB: [ { x: 0, y: 0 }, { x: 0.2, y: 0.85 }, { x: 1, y: 1 }, ], }, }, interpolation: "smooth", label: "Tone curve", target: "tone.curve", type: "curves", variant: "single", }, }, title: "Tone", }, ], title: "Controls", }, }, }); const { container } = renderControlsPanelWithSchema(schema); expect( container.querySelector('[data-curve-interpolation="smooth"]'), ).toBeTruthy(); }); it("selects curve points with pointer events and deletes selected interior points", async () => { const schema = defineToolcraft({ canvas: { enabled: false }, panels: { controls: { sections: [ { controls: { toneCurve: { defaultValue: { activeChannel: "RGB", points: { RGB: [ { x: 0, y: 0 }, { x: 0.5, y: 0.5 }, { x: 1, y: 1 }, ], }, }, label: "Tone curve", target: "tone.curve", type: "curves", variant: "single", }, }, title: "Tone", }, ], title: "Controls", }, }, }); renderControlsPanelWithSchema(schema); const point = screen.getByRole("button", { name: "Curve point 2" }); fireEvent.pointerDown(point, { pointerId: 1 }); fireEvent.pointerUp(point, { pointerId: 1 }); await waitFor(() => expect(point.getAttribute("aria-pressed")).toBe("true")); fireEvent.keyDown(point, { key: "Delete" }); await waitFor(() => { const values = JSON.parse(screen.getByTestId("values-json").textContent ?? "{}"); expect(values["tone.curve"].points.RGB).toEqual([ { x: 0, y: 0 }, { x: 1, y: 1 }, ]); }); }); });