import { render } from "@testing-library/react"; import { describe, expect, it } from "bun:test"; import type { ReactElement } from "react"; import { Field } from "@seed-design/react-field"; import { AttachmentInputRoot, AttachmentInputHiddenInput } from "./AttachmentInput"; function setUp(jsx: ReactElement) { return render(jsx); } describe("AttachmentInput", () => { describe("props merging", () => { describe("AttachmentInputHiddenInput", () => { it("should merge props from AttachmentInputRoot", () => { const { getByTestId } = setUp( , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("name")).toBe("attachment"); expect(input.getAttribute("type")).toBe("file"); }); it("should merge props from Field context", () => { const { getByTestId } = setUp( , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("name")).toBe("field-attachment"); expect(input.getAttribute("aria-required")).toBe("true"); expect(input.getAttribute("aria-invalid")).toBe("true"); expect(input).toBeDisabled(); }); it("should prioritize direct props over context props", () => { const { getByTestId } = setUp( , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("name")).toBe("direct-name"); }); it("should get id from Field context", () => { const { getByTestId } = setUp( , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("id")).toBeTruthy(); expect(input.getAttribute("id")).toContain("field:"); }); it("should have no id without Field context", () => { const { getByTestId } = setUp( , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("id")).toBeNull(); }); it("should connect aria-describedby when Field has Description", () => { const { getByTestId } = setUp( Upload your files , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("aria-describedby")).toBeTruthy(); }); it("should connect aria-describedby when Field has ErrorMessage", () => { const { getByTestId } = setUp( File too large , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("aria-describedby")).toBeTruthy(); }); }); describe("Complex prop merging scenarios", () => { it("should handle nested Field and AttachmentInput contexts", () => { const { getByTestId } = setUp( Attachment Max 10MB , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("name")).toBe("attachment"); expect(input.getAttribute("aria-required")).toBe("true"); expect(input.getAttribute("aria-invalid")).toBe("true"); expect(input.getAttribute("aria-describedby")).toBeTruthy(); }); it("should let Field disabled override AttachmentInput enabled", () => { const { getByTestId } = setUp( , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input).toBeDisabled(); }); it("should not put aria-readonly on the hidden input even when Field is read-only", () => { // aria-readonly is invalid on per ARIA 1.2 — it's only valid on // textbox/spinbutton/checkbox/switch/slider/etc. Field spreads aria-readonly to every // child input, so AttachmentInputHiddenInput has to strip it. const { getByTestId } = setUp( , ); const input = getByTestId("hidden-input") as HTMLInputElement; expect(input.getAttribute("aria-readonly")).toBeNull(); // hidden input itself stays enabled so the form value is preserved when read-only expect(input.disabled).toBe(false); }); }); }); });