Files
cadence-ui/packages/ui/src/components/select.test.tsx
T

148 lines
4.6 KiB
TypeScript

import { useState } from "react";
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it } from "vitest";
import { Field, FieldDescription, FieldError } from "./field";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectSeparator,
SelectTrigger,
SelectValue
} from "./select";
function ReviewLaneSelect(props?: React.ComponentProps<typeof Select>) {
return (
<Select {...props}>
<SelectTrigger aria-label="Review lane">
<SelectValue placeholder="Choose a review lane" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>Review lane</SelectLabel>
<SelectItem value="editorial">Editorial review</SelectItem>
<SelectItem value="design">Design review</SelectItem>
<SelectSeparator />
<SelectItem value="legal">Legal review</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
);
}
describe("Select", () => {
it("renders default value and opens selectable content", async () => {
const user = userEvent.setup();
render(<ReviewLaneSelect defaultValue="design" />);
const trigger = screen.getByRole("combobox", { name: "Review lane" });
expect(trigger).toHaveTextContent("Design review");
expect(trigger).toHaveAttribute("data-slot", "trigger");
await user.click(trigger);
const listbox = await screen.findByRole("listbox");
const designOption = within(listbox).getByRole("option", { name: "Design review" });
expect(listbox).toHaveAttribute("data-slot", "content");
expect(designOption).toHaveAttribute("data-slot", "item");
});
it("updates controlled value after selecting an option", async () => {
const user = userEvent.setup();
function ControlledSelect() {
const [value, setValue] = useState("editorial");
return <ReviewLaneSelect value={value} onValueChange={setValue} />;
}
render(<ControlledSelect />);
const trigger = screen.getByRole("combobox", { name: "Review lane" });
expect(trigger).toHaveTextContent("Editorial review");
await user.click(trigger);
await user.click(await screen.findByRole("option", { name: "Legal review" }));
expect(trigger).toHaveTextContent("Legal review");
expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
});
it("supports field invalid state and described-by wiring", async () => {
const user = userEvent.setup();
render(
<Field id="routing" invalid>
<Select>
<SelectTrigger aria-label="Routing team">
<SelectValue placeholder="Choose a team" />
</SelectTrigger>
<SelectContent>
<SelectItem value="product">Product</SelectItem>
<SelectItem value="design">Design</SelectItem>
</SelectContent>
</Select>
<FieldDescription>Choose the primary owner.</FieldDescription>
<FieldError>Select a team before publishing.</FieldError>
</Field>
);
const trigger = screen.getByRole("combobox", { name: "Routing team" });
expect(trigger).toHaveAttribute("aria-invalid", "true");
expect(trigger).toHaveAttribute(
"aria-describedby",
expect.stringContaining("routing-description")
);
expect(trigger).toHaveAttribute(
"aria-describedby",
expect.stringContaining("routing-error")
);
await user.click(trigger);
expect(await screen.findByRole("option", { name: "Product" })).toBeInTheDocument();
});
it("exposes disabled state on the trigger", () => {
render(
<Select disabled>
<SelectTrigger aria-label="Disabled select">
<SelectValue placeholder="Disabled" />
</SelectTrigger>
<SelectContent>
<SelectItem value="one">One</SelectItem>
</SelectContent>
</Select>
);
const trigger = screen.getByRole("combobox", { name: "Disabled select" });
expect(trigger).toBeDisabled();
expect(trigger).toHaveAttribute("data-disabled", "");
});
it("opens from the keyboard so combobox interaction stays accessible", async () => {
const user = userEvent.setup();
render(<ReviewLaneSelect defaultValue="editorial" />);
const trigger = screen.getByRole("combobox", { name: "Review lane" });
trigger.focus();
await user.keyboard("{ArrowDown}");
const listbox = await screen.findByRole("listbox");
expect(trigger).toHaveAttribute("aria-expanded", "true");
expect(within(listbox).getByRole("option", { name: "Editorial review" })).toBeInTheDocument();
});
});