import { describe, expect, test } from "vitest"; import { userEvent } from "vitest/browser"; import { render } from "vitest-browser-react"; import { SendouSelect, SendouSelectItem, SendouSelectItemSection, } from "./Select"; const SECTIONS = [ { heading: "2023", key: "2023", items: [ { id: 2, name: "Season 2" }, { id: 1, name: "Season 1" }, { id: 0, name: "Season 0" }, ], }, ]; function GroupedSelect(props: { search?: { placeholder?: string }; selectedKey?: number; }) { return ( {({ heading, items, key }: (typeof SECTIONS)[number]) => ( {items.map((item) => ( {item.name} ))} )} ); } describe("SendouSelect", () => { test("renders an item with a falsy (0) key when there is no search", async () => { const screen = await render(); await screen.getByRole("button").click(); await expect .element(screen.getByRole("option", { name: "Season 0" })) .toBeVisible(); }); test("filters items when search is enabled", async () => { const screen = await render(); await screen.getByRole("button").click(); await screen.getByRole("combobox").fill("Season 1"); await expect .element(screen.getByRole("option", { name: "Season 1" })) .toBeVisible(); await expect .element(screen.getByRole("option", { name: "Season 2" })) .not.toBeInTheDocument(); }); test("shows the empty state when nothing matches the search", async () => { const screen = await render(); await screen.getByRole("button").click(); await screen.getByRole("combobox").fill("nope"); await expect.element(screen.getByText("No results")).toBeVisible(); await expect .element(screen.getByRole("group", { name: "2023" })) .not.toBeInTheDocument(); }); test("keeps the popover open when Enter has no match to commit", async () => { const screen = await render(); await screen.getByRole("button").click(); await screen.getByRole("combobox").fill("Season 1"); await expect .element(screen.getByRole("combobox")) .toHaveAttribute( "aria-activedescendant", (await screen.getByRole("option", { name: "Season 1" }).element()).id, ); await screen.getByRole("combobox").fill("nope"); await userEvent.keyboard("{Enter}"); await expect.element(screen.getByRole("listbox")).toBeVisible(); await expect .element(screen.getByRole("button", { name: /Season/ })) .toHaveTextContent("Pick a season"); }); test("commits the focused search result on Enter", async () => { const screen = await render(); await screen.getByRole("button").click(); await screen.getByRole("combobox").fill("Season 1"); await userEvent.keyboard("{Enter}"); await expect.element(screen.getByRole("listbox")).not.toBeInTheDocument(); await expect .element(screen.getByRole("button")) .toHaveTextContent("Season 1"); }); test("mounts only the selected option while closed", async () => { const screen = await render(); const options = () => screen.container.querySelectorAll('[role="option"]').length; expect(options()).toBe(1); await expect .element(screen.getByRole("button")) .toHaveTextContent("Season 1"); await screen.getByRole("button").click(); await expect .element(screen.getByRole("option", { name: "Season 2" })) .toBeVisible(); expect(options()).toBe(3); }); test("tabbing out of the open listbox closes it", async () => { const screen = await render( <> , ); await screen.getByRole("button", { name: /Season/ }).click(); await expect.element(screen.getByRole("listbox")).toBeVisible(); await userEvent.keyboard("{Tab}"); await expect.element(screen.getByRole("listbox")).not.toBeInTheDocument(); await expect .element(screen.getByRole("button", { name: "After" })) .toHaveFocus(); }); test("type-ahead focuses the option matching what was typed", async () => { const screen = await render(); await screen.getByRole("button").click(); await expect.element(screen.getByRole("listbox")).toHaveFocus(); await userEvent.keyboard("season 0"); const option = screen.getByRole("option", { name: "Season 0" }); await expect .element(screen.getByRole("listbox")) .toHaveAttribute("aria-activedescendant", (await option.element()).id); }); test("repeating a type-ahead character cycles through the matches", async () => { const screen = await render(); await screen.getByRole("button").click(); await expect.element(screen.getByRole("listbox")).toHaveFocus(); await userEvent.keyboard("ss"); const option = screen.getByRole("option", { name: "Season 1" }); await expect .element(screen.getByRole("listbox")) .toHaveAttribute("aria-activedescendant", (await option.element()).id); }); test("hovering an option moves the active descendant to it", async () => { const screen = await render(); await screen.getByRole("button").click(); const option = screen.getByRole("option", { name: "Season 0" }); await option.hover(); await expect .element(screen.getByRole("listbox")) .toHaveAttribute("aria-activedescendant", (await option.element()).id); }); });