You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
6.4 KiB
150 lines
6.4 KiB
import { useState } from "react";
|
|
import { describe, it, expect, vi } from "vitest";
|
|
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
|
import {
|
|
buildMultiAnglePrompt,
|
|
DEFAULT_MULTI_ANGLE_SETTINGS,
|
|
MULTI_ANGLE_PRESET_SETTINGS,
|
|
MultiAngleEditor,
|
|
type MultiAnglePresetId,
|
|
type MultiAngleSettings,
|
|
} from "@/components/MultiAngleEditor";
|
|
|
|
vi.mock("@/i18n", () => {
|
|
const messages: Record<string, string> = {
|
|
"multiAngle.editorTitle": "Multi-Angle Editor",
|
|
"multiAngle.sightControl": "Multi-angle sight control",
|
|
"multiAngle.noImage": "No image",
|
|
"multiAngle.horizontal": "Horizontal",
|
|
"multiAngle.vertical": "Vertical",
|
|
"multiAngle.zoom": "Zoom",
|
|
"multiAngle.prompt": "Prompt",
|
|
"multiAngle.enableCustomPrompt": "Enable custom prompt",
|
|
"multiAngle.promptDialog": "Multi-angle prompt",
|
|
"multiAngle.promptInput": "Multi-angle prompt input",
|
|
"multiAngle.promptPlaceholder": "Optional prompt",
|
|
"multiAngle.resetAria": "Reset multi-angle parameters",
|
|
"multiAngle.reset": "Reset",
|
|
"multiAngle.adjustVerticalUp": "Adjust vertical up",
|
|
"multiAngle.adjustVerticalDown": "Adjust vertical down",
|
|
"multiAngle.adjustHorizontalLeft": "Adjust horizontal left",
|
|
"multiAngle.adjustHorizontalRight": "Adjust horizontal right",
|
|
"multiAngle.preset.custom": "Custom",
|
|
"multiAngle.preset.fisheye": "Fisheye",
|
|
"multiAngle.preset.tilt": "Tilt",
|
|
"multiAngle.preset.frontOverhead": "Front overhead",
|
|
"multiAngle.preset.frontLow": "Front low angle",
|
|
"multiAngle.preset.panoramicOverhead": "Panoramic overhead",
|
|
"multiAngle.preset.back": "Back view",
|
|
};
|
|
|
|
return {
|
|
useI18n: () => ({
|
|
t: (key: string) => messages[key] ?? key,
|
|
}),
|
|
};
|
|
});
|
|
|
|
function MultiAngleHarness() {
|
|
const [settings, setSettings] = useState<MultiAngleSettings>(DEFAULT_MULTI_ANGLE_SETTINGS);
|
|
|
|
return (
|
|
<MultiAngleEditor
|
|
previewImage="data:image/png;base64,input"
|
|
settings={settings}
|
|
onChange={(patch) => setSettings((current) => ({ ...current, ...patch, preset: patch.preset ?? "custom" }))}
|
|
onSelectPreset={(preset: MultiAnglePresetId) => setSettings({ ...MULTI_ANGLE_PRESET_SETTINGS[preset] })}
|
|
onTogglePrompt={(enabled) => setSettings((current) => ({ ...current, includePrompt: enabled }))}
|
|
onReset={() =>
|
|
setSettings((current) => ({
|
|
...DEFAULT_MULTI_ANGLE_SETTINGS,
|
|
includePrompt: current.includePrompt,
|
|
promptText: current.promptText,
|
|
}))
|
|
}
|
|
/>
|
|
);
|
|
}
|
|
|
|
describe("MultiAngleEditor", () => {
|
|
it("renders default custom multi-angle controls", () => {
|
|
render(<MultiAngleHarness />);
|
|
|
|
expect(screen.getByText("Multi-Angle Editor")).toBeInTheDocument();
|
|
expect(screen.getByRole("button", { name: "Custom" })).toBeEnabled();
|
|
expect(screen.getByRole("button", { name: "Fisheye" })).toBeEnabled();
|
|
expect(screen.getByRole("button", { name: "Tilt" })).toBeEnabled();
|
|
expect(screen.getByLabelText("Horizontal")).toHaveValue("0");
|
|
expect(screen.getByLabelText("Vertical")).toHaveValue("-30");
|
|
expect(screen.getByLabelText("Zoom")).toHaveValue("5");
|
|
expect(screen.getByRole("switch", { name: "Prompt" })).toHaveAttribute("aria-checked", "false");
|
|
expect(screen.queryByRole("dialog", { name: "Multi-angle prompt" })).not.toBeInTheDocument();
|
|
});
|
|
|
|
it("applies the fisheye preset parameters and prompt", () => {
|
|
render(<MultiAngleHarness />);
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "Fisheye" }));
|
|
|
|
expect(screen.getByRole("button", { name: "Fisheye" })).toHaveClass("border-[var(--border-strong)]");
|
|
expect(screen.getByLabelText("Horizontal")).toHaveValue("0");
|
|
expect(screen.getByLabelText("Vertical")).toHaveValue("30");
|
|
expect(screen.getByLabelText("Zoom")).toHaveValue("10");
|
|
expect(screen.getByRole("switch", { name: "Prompt" })).toHaveAttribute("aria-checked", "true");
|
|
expect(screen.getByDisplayValue(/Ultra-close fisheye lens/)).toBeInTheDocument();
|
|
expect(screen.getByDisplayValue(/barrel distortion/)).toBeInTheDocument();
|
|
});
|
|
|
|
it("syncs cube drag movement with custom parameters", async () => {
|
|
render(<MultiAngleHarness />);
|
|
|
|
const globe = screen.getByRole("application", { name: "Multi-angle sight control" });
|
|
fireEvent.change(screen.getByLabelText("Vertical"), { target: { value: "0" } });
|
|
|
|
fireEvent.pointerDown(globe, { pointerId: 1, pointerType: "mouse", clientX: 100, clientY: 100, buttons: 1 });
|
|
fireEvent.pointerMove(globe, { pointerId: 1, pointerType: "mouse", clientX: 150, clientY: 80, buttons: 1 });
|
|
|
|
await waitFor(() => expect(screen.getByLabelText("Horizontal")).toHaveValue("40"));
|
|
expect(screen.getByLabelText("Vertical")).toHaveValue("-16");
|
|
|
|
fireEvent.pointerUp(globe, { pointerId: 1, pointerType: "mouse" });
|
|
|
|
fireEvent.change(screen.getByLabelText("Horizontal"), { target: { value: "360" } });
|
|
fireEvent.change(screen.getByLabelText("Vertical"), { target: { value: "57" } });
|
|
fireEvent.change(screen.getByLabelText("Zoom"), { target: { value: "8.6" } });
|
|
|
|
expect(globe).toHaveAttribute("data-horizontal", "360");
|
|
expect(globe).toHaveAttribute("data-vertical", "57");
|
|
expect(globe).toHaveAttribute("data-zoom", "8.6");
|
|
});
|
|
|
|
it("wraps horizontal cube drag across zero degrees", async () => {
|
|
render(<MultiAngleHarness />);
|
|
|
|
const globe = screen.getByRole("application", { name: "Multi-angle sight control" });
|
|
|
|
fireEvent.pointerDown(globe, { pointerId: 1, pointerType: "mouse", clientX: 100, clientY: 100, buttons: 1 });
|
|
fireEvent.pointerMove(globe, { pointerId: 1, pointerType: "mouse", clientX: 80, clientY: 100, buttons: 1 });
|
|
|
|
await waitFor(() => expect(screen.getByLabelText("Horizontal")).toHaveValue("344"));
|
|
});
|
|
|
|
it("builds a platform-neutral multi-angle prompt", () => {
|
|
const prompt = buildMultiAnglePrompt({
|
|
...DEFAULT_MULTI_ANGLE_SETTINGS,
|
|
horizontal: 316,
|
|
vertical: 57,
|
|
zoom: 8.6,
|
|
includePrompt: true,
|
|
promptText: "Keep background layers clear",
|
|
});
|
|
|
|
expect(prompt).toMatch(/horizontal orbit 316 degrees counterclockwise/);
|
|
expect(prompt).toMatch(/front overhead view/);
|
|
expect(prompt).toMatch(/shot scale zoom 8.6/);
|
|
expect(prompt).toMatch(/normal 2D image/);
|
|
expect(prompt).toMatch(/Do not create a 3D model/);
|
|
expect(prompt).toMatch(/prompt: Keep background layers clear/);
|
|
expect(prompt).not.toMatch(/libTV/i);
|
|
});
|
|
});
|
|
|