import { expect, test, vi } from "vitest"; import { fireEvent, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { renderApp } from "../test/render"; import { ObjectForm } from "./object-form"; import { pruneFields } from "./prune-fields"; test("create mode: shows visibility (draft/internal only) and submits assembled values", async () => { const onSubmit = vi.fn(); renderApp( {}} />); await userEvent.type(await screen.findByLabelText(/object number/i), "A-9"); await userEvent.type(screen.getByLabelText(/^name/i), "Amphora"); await userEvent.type(screen.getByLabelText(/inscription/i), "To the gods"); const visibility = screen.getByLabelText(/visibility/i) as HTMLSelectElement; expect([...visibility.options].map((o) => o.value)).toEqual(expect.arrayContaining(["draft", "internal"])); expect([...visibility.options].map((o) => o.value)).not.toContain("public"); await userEvent.click(screen.getByRole("button", { name: /create object/i })); await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce()); const values = onSubmit.mock.calls[0][0]; expect(values.core.object_number).toBe("A-9"); expect(values.visibility).toBe("draft"); expect(values.fields.inscription).toBe("To the gods"); }); test("Cmd/Ctrl+Enter submits the form", async () => { const onSubmit = vi.fn(); renderApp( {}} />); await userEvent.type(await screen.findByLabelText(/object number/i), "A-9"); await userEvent.type(screen.getByLabelText(/^name/i), "Amphora"); await userEvent.type(screen.getByLabelText(/inscription/i), "To the gods"); const numberInput = screen.getByLabelText(/object number/i); fireEvent.keyDown(numberInput, { key: "Enter", metaKey: true }); await waitFor(() => expect(onSubmit).toHaveBeenCalledOnce()); }); test("required core + required flexible field block submit", async () => { const onSubmit = vi.fn(); renderApp( {}} />); await userEvent.click(await screen.findByRole("button", { name: /create object/i })); await waitFor(() => expect(screen.getAllByText(/required/i).length).toBeGreaterThan(0)); expect(onSubmit).not.toHaveBeenCalled(); }); test("number_of_objects of 0 is blocked client-side with the minCount message", async () => { const onSubmit = vi.fn(); renderApp( {}} />); await userEvent.type(await screen.findByLabelText(/object number/i), "A-9"); await userEvent.type(screen.getByLabelText(/^name/i), "Amphora"); await userEvent.type(screen.getByLabelText(/inscription/i), "To the gods"); const count = screen.getByLabelText(/number of objects/i); await userEvent.clear(count); await userEvent.type(count, "0"); await userEvent.click(screen.getByRole("button", { name: /create object/i })); expect(await screen.findByText("Must be at least 1")).toBeInTheDocument(); expect(onSubmit).not.toHaveBeenCalled(); }); test("edit mode: no visibility control, save button, prefilled values", async () => { const onSubmit = vi.fn(); renderApp( {}} defaults={{ core: { object_number: "A-1", object_name: "Amphora", number_of_objects: 1, brief_description: null, current_location: "Vault 3", current_owner: null, recorder: null, recording_date: null }, fields: { inscription: "hi" }, }} />, ); expect(await screen.findByDisplayValue("Amphora")).toBeInTheDocument(); expect(screen.queryByLabelText(/visibility/i)).not.toBeInTheDocument(); expect(screen.getByRole("button", { name: /save/i })).toBeInTheDocument(); }); test("pruneFields: localized_text keeps only the default-language key, other object fields unaffected", () => { const localizedTextKeys = new Set(["title_ml"]); const result = pruneFields( { title_ml: { en: "Old", sv: "Ny" }, other: "x" }, localizedTextKeys, "sv", ); expect(result).toEqual({ title_ml: { sv: "Ny" }, other: "x" }); expect(Object.keys(result.title_ml as Record)).not.toContain("en"); }); test("pruneFields: localized_text with only non-default lang produces empty object (key omitted)", () => { const localizedTextKeys = new Set(["title_ml"]); const result = pruneFields( { title_ml: { en: "English only" } }, localizedTextKeys, "sv", ); expect(result).toEqual({}); }); test("pruneFields: non-localized_text object fields are preserved as-is", () => { const localizedTextKeys = new Set(["title_ml"]); const result = pruneFields( { nested_obj: { a: "1", b: "2" } }, localizedTextKeys, "sv", ); expect(result).toEqual({ nested_obj: { a: "1", b: "2" } }); });