import { render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, mock } from "bun:test";
import type { ReactElement } from "react";
import React from "react";
import { Field } from "@seed-design/react-field";
import {
TextFieldRoot,
TextFieldInput,
TextFieldTextarea,
TextFieldPrefixIcon,
TextFieldPrefixText,
TextFieldSuffixIcon,
TextFieldSuffixText,
} from "./TextField";
function setUp(jsx: ReactElement) {
return {
user: userEvent.setup(),
...render(jsx),
};
}
describe("TextField", () => {
describe("props merging", () => {
describe("TextFieldInput", () => {
it("should merge props from TextFieldRoot", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveValue("initial");
expect(input).toHaveAttribute("placeholder", "Placeholder");
});
it("should merge props from Field context", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveAttribute("name", "test-field");
expect(input).toHaveAttribute("aria-required", "true");
expect(input).toHaveAttribute("aria-invalid", "true");
expect(input).toBeDisabled();
});
it("should prioritize direct props over context props", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveAttribute("name", "direct-name");
});
it("should merge data attributes", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveAttribute("data-disabled", "");
expect(input).toHaveAttribute("data-readonly", "");
expect(input).toHaveAttribute("data-invalid", "");
expect(input).toHaveAttribute("data-custom", "value");
});
it("should handle uncontrolled modes", async () => {
const handleChange = mock(() => {});
const { getByRole, user } = setUp(
,
);
const input = getByRole("textbox");
await user.type(input, "test");
expect(handleChange).toHaveBeenCalledWith("test");
});
it("should handle controlled modes", async () => {
function Controlled() {
const [value, setValue] = React.useState("");
return (
);
}
const { getByRole, user } = setUp();
const input = getByRole("textbox");
await user.type(input, "test");
expect(input).toHaveValue("test");
});
});
describe("TextFieldTextarea", () => {
it("should merge props from TextFieldRoot", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveValue("initial");
expect(input).toHaveAttribute("placeholder", "Placeholder");
});
it("should merge props from Field context", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveAttribute("name", "test-field");
expect(input).toHaveAttribute("aria-required", "true");
expect(input).toHaveAttribute("aria-invalid", "true");
expect(input).toBeDisabled();
});
it("should prioritize direct props over context props", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveAttribute("name", "direct-name");
expect(input).not.toBeDisabled();
});
it("should merge data attributes", () => {
const { getByRole } = setUp(
,
);
const input = getByRole("textbox");
expect(input).toHaveAttribute("data-disabled", "");
expect(input).toHaveAttribute("data-readonly", "");
expect(input).toHaveAttribute("data-invalid", "");
expect(input).toHaveAttribute("data-custom", "value");
});
it("should handle uncontrolled modes", async () => {
const handleChange = mock(() => {});
const { getByRole, user } = setUp(
,
);
const input = getByRole("textbox");
await user.type(input, "test");
expect(handleChange).toHaveBeenCalledWith("test");
});
it("should handle controlled modes", async () => {
function Controlled() {
const [value, setValue] = React.useState("");
return (
);
}
const { getByRole, user } = setUp();
const input = getByRole("textbox");
await user.type(input, "test");
expect(input).toHaveValue("test");
});
});
describe("Prefix and Suffix components", () => {
it("should apply state props to affix elements", () => {
const { getByTestId } = setUp(
Prefix
} />
} />
Suffix
,
);
const prefixIcon = getByTestId("prefix-icon");
expect(prefixIcon).toHaveAttribute("data-disabled", "");
expect(prefixIcon).toHaveAttribute("data-invalid", "");
expect(prefixIcon).toHaveAttribute("data-readonly", "");
const suffixIcon = getByTestId("suffix-icon");
expect(suffixIcon).toHaveAttribute("data-disabled", "");
expect(suffixIcon).toHaveAttribute("data-invalid", "");
expect(suffixIcon).toHaveAttribute("data-readonly", "");
});
});
describe("Complex prop merging scenarios", () => {
// this is possible, but you shouldn't be doing this
it("should handle nested Field and TextField contexts", () => {
const { getByRole } = setUp(
Label
Description
,
);
const input = getByRole("textbox");
expect(input).toHaveAttribute("name", "field");
expect(input).toHaveAttribute("aria-required", "true");
expect(input).toHaveAttribute("aria-invalid", "true");
expect(input).toHaveAttribute("aria-describedby");
});
});
});
});