mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-06 08:16:43 +00:00
test(ui): pick select options by role instead of by text
Clicking a Base UI select entry found by text or by a title attribute is a race. The text node exists one render before the popup finishes entering, and until then the positioner still carries pointer-events: none, so user-event refuses the click and the test throws. Querying by role only matches once the popup is exposed to the accessibility tree, which is after that window closes. Route the 37 remaining select interactions through chooseSelectOption, which does the role query. Instrumenting the converted files shows the text query resolving while the popup was still pointer-blocked on 6 of 41 samples; the role query was never blocked. Seven files kept their text queries because their popup entries carry no accessible role, so there is nothing to query by.
This commit is contained in:
parent
a5f9410681
commit
eb53639ecb
19 changed files with 63 additions and 84 deletions
|
|
@ -5,6 +5,7 @@ import { describe, it, expect, vi, beforeEach } from "vitest";
|
|||
import AddAgentForm from "./add_agent_form";
|
||||
import * as networking from "@/components/networking";
|
||||
import type { AgentCreateInfo } from "@/components/networking";
|
||||
import { chooseSelectOption } from "../../../../../tests/test-utils";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
createAgentCall: vi.fn(),
|
||||
|
|
@ -309,8 +310,7 @@ describe("AddAgentForm submit payload", () => {
|
|||
|
||||
await user.type(await screen.findByLabelText("Allowed Models"), "gpt-4o,");
|
||||
await user.keyboard("{Escape}");
|
||||
await user.click(screen.getByLabelText("Allowed Agents (Sub-Agents)"));
|
||||
await user.click(await screen.findByTitle("Sub Agent One"));
|
||||
await chooseSelectOption(user, screen.getByLabelText("Allowed Agents (Sub-Agents)"), "Sub Agent One");
|
||||
await user.keyboard("{Escape}");
|
||||
await user.click(screen.getByText(/Configure which models, agents, and MCP tools/));
|
||||
await user.click(screen.getByRole("button", { name: /^Next/ }));
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import React from "react";
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import BudgetModal from "./budget_modal";
|
||||
import { chooseSelectOption } from "../../../../../tests/test-utils";
|
||||
|
||||
const { createMock } = vi.hoisted(() => ({ createMock: vi.fn() }));
|
||||
|
||||
|
|
@ -63,8 +64,7 @@ describe("BudgetModal", () => {
|
|||
await openOptionalSettings(user);
|
||||
fireEvent.change(screen.getByLabelText("Max Budget (USD)"), { target: { value: "42.567" } });
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("monthly"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "monthly");
|
||||
|
||||
await create(user);
|
||||
|
||||
|
|
@ -80,8 +80,7 @@ describe("BudgetModal", () => {
|
|||
|
||||
await openOptionalSettings(user);
|
||||
fireEvent.change(screen.getByLabelText("Max Budget (USD)"), { target: { value: "42.567" } });
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("monthly"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "monthly");
|
||||
|
||||
await user.click(screen.getByText("Optional Settings"));
|
||||
await waitFor(() => expect(screen.queryByLabelText("Max Budget (USD)")).not.toBeInTheDocument());
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|||
import type { components } from "@/lib/http/schema";
|
||||
|
||||
import EditBudgetModal from "./edit_budget_modal";
|
||||
import { chooseSelectOption } from "../../../../../tests/test-utils";
|
||||
|
||||
const { updateMock } = vi.hoisted(() => ({ updateMock: vi.fn() }));
|
||||
|
||||
|
|
@ -73,8 +74,7 @@ describe("EditBudgetModal", () => {
|
|||
await user.clear(screen.getByLabelText("Max Budget (USD)"));
|
||||
fireEvent.change(screen.getByLabelText("Max Budget (USD)"), { target: { value: "42.567" } });
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("monthly"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "monthly");
|
||||
|
||||
await save(user);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import userEvent from "@testing-library/user-event";
|
|||
import { renderWithProviders } from "@/../tests/test-utils";
|
||||
import CoordinationRedisTypeSelector from "./CoordinationRedisTypeSelector";
|
||||
import { COORDINATION_REDIS_TYPE_DESCRIPTIONS } from "./coordinationRedisFields";
|
||||
import { chooseSelectOption } from "../../../../../../tests/test-utils";
|
||||
|
||||
describe("CoordinationRedisTypeSelector", () => {
|
||||
it("labels the control and shows the current selection", () => {
|
||||
|
|
@ -36,8 +37,7 @@ describe("CoordinationRedisTypeSelector", () => {
|
|||
const user = userEvent.setup();
|
||||
renderWithProviders(<CoordinationRedisTypeSelector redisType="node" onTypeChange={onTypeChange} />);
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Cluster"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Cluster");
|
||||
|
||||
expect(onTypeChange).toHaveBeenCalledTimes(1);
|
||||
expect(onTypeChange.mock.calls[0][0]).toBe("cluster");
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ import {
|
|||
useStopShadowEval,
|
||||
type ShadowEvalJob,
|
||||
} from "./useShadowEval";
|
||||
import { chooseSelectOption } from "../../../../../tests/test-utils";
|
||||
|
||||
const job = (overrides: Partial<ShadowEvalJob> = {}): ShadowEvalJob => ({
|
||||
job_id: "job-1",
|
||||
|
|
@ -437,8 +438,7 @@ describe("ShadowEvalSection", () => {
|
|||
await user.click(within(keyList).getByText("prod-alpha"));
|
||||
await user.click(keyInput);
|
||||
await user.click(within(keyList).getByText("staging-beta"));
|
||||
await user.click(screen.getByPlaceholderText("Select up to 4 auto-routers"));
|
||||
await user.click(await screen.findByText("gpt-auto"));
|
||||
await chooseSelectOption(user, screen.getByPlaceholderText("Select up to 4 auto-routers"), "gpt-auto");
|
||||
|
||||
expect(screen.getByText("Start shadow eval")).toBeDisabled();
|
||||
|
||||
|
|
@ -470,8 +470,7 @@ describe("ShadowEvalSection", () => {
|
|||
await user.click(screen.getByPlaceholderText("Search teams by alias"));
|
||||
const teamList = await screen.findByTestId("paginated-multi-select-list");
|
||||
await user.click(within(teamList).getByText("engineering"));
|
||||
await user.click(screen.getByPlaceholderText("Select up to 4 auto-routers"));
|
||||
await user.click(await screen.findByText("gpt-auto"));
|
||||
await chooseSelectOption(user, screen.getByPlaceholderText("Select up to 4 auto-routers"), "gpt-auto");
|
||||
await user.click(screen.getByPlaceholderText("Select a judge model"));
|
||||
await user.click(await screen.findByRole("option", { name: /anthropic\/claude-sonnet-5/ }));
|
||||
await user.click(screen.getByText("Start shadow eval"));
|
||||
|
|
@ -502,8 +501,7 @@ describe("ShadowEvalSection", () => {
|
|||
await user.click(screen.getByPlaceholderText("Search keys by alias"));
|
||||
const keyList = await screen.findByTestId("paginated-multi-select-list");
|
||||
await user.click(within(keyList).getByText("prod-alpha"));
|
||||
await user.click(screen.getByPlaceholderText("Select up to 4 auto-routers"));
|
||||
await user.click(await screen.findByText("gpt-auto"));
|
||||
await chooseSelectOption(user, screen.getByPlaceholderText("Select up to 4 auto-routers"), "gpt-auto");
|
||||
await user.click(screen.getByPlaceholderText("Select a judge model"));
|
||||
await user.click(await screen.findByRole("option", { name: /anthropic\/claude-sonnet-5/ }));
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { describe, expect, it, vi } from "vitest";
|
|||
|
||||
import { ToolTestPanel } from "./ToolTestPanel";
|
||||
import { InputSchema, MCPTool } from "@/components/mcp_tools/types";
|
||||
import { chooseSelectOption } from "../../../../../tests/test-utils";
|
||||
|
||||
const buildTool = (schema: InputSchema | string): MCPTool => ({
|
||||
name: "demo-tool",
|
||||
|
|
@ -229,8 +230,7 @@ describe("ToolTestPanel argument payload", () => {
|
|||
const onSubmit = await submitPanel(
|
||||
{ type: "object", properties: { active: { type: "boolean", default: false } } },
|
||||
async (user) => {
|
||||
await user.click(screen.getByLabelText("active"));
|
||||
await user.click(await screen.findByText("True"));
|
||||
await chooseSelectOption(user, screen.getByLabelText("active"), "True");
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import userEvent, { PointerEventsCheckLevel } from "@testing-library/user-event"
|
|||
import { renderWithProviders } from "@/../tests/test-utils";
|
||||
import * as networking from "@/components/networking";
|
||||
import PolicyTestPanel from "./policy_test_panel";
|
||||
import { chooseSelectOption } from "../../../../../tests/test-utils";
|
||||
|
||||
vi.mock("@/components/networking");
|
||||
|
||||
|
|
@ -24,8 +25,7 @@ const setup = () => {
|
|||
};
|
||||
|
||||
const pickOption = async (user: ReturnType<typeof userEvent.setup>, label: string, option: string) => {
|
||||
await user.click(screen.getByLabelText(label));
|
||||
await user.click(await screen.findByTitle(option));
|
||||
await chooseSelectOption(user, screen.getByLabelText(label), option);
|
||||
};
|
||||
|
||||
const simulate = async (user: ReturnType<typeof userEvent.setup>) => {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
|||
import { deletePromptCall, getPromptsList } from "@/components/networking";
|
||||
|
||||
import PromptsPanel from "./index";
|
||||
import { chooseSelectOption } from "../../../../../tests/test-utils";
|
||||
|
||||
vi.mock("@/components/networking", () => ({
|
||||
getPromptsList: vi.fn(),
|
||||
|
|
@ -118,8 +119,7 @@ describe("PromptsPanel toolbar", () => {
|
|||
|
||||
expect(screen.getByText("All Environments")).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Production"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Production");
|
||||
|
||||
await waitFor(() => expect(mockGetPromptsList).toHaveBeenLastCalledWith("sk-test", "production"));
|
||||
});
|
||||
|
|
@ -131,12 +131,10 @@ describe("PromptsPanel toolbar", () => {
|
|||
renderPanel("Admin");
|
||||
await screen.findByText("table-loaded");
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Production"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Production");
|
||||
await waitFor(() => expect(screen.getByRole("combobox")).toHaveTextContent("Production"));
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("All Environments"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "All Environments");
|
||||
|
||||
await waitFor(() => expect(screen.getByRole("combobox")).toHaveTextContent("All Environments"));
|
||||
await waitFor(() => expect(mockGetPromptsList).toHaveBeenLastCalledWith("sk-test", undefined));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { fireEvent, screen, waitFor, within } from "@testing-library/react";
|
|||
import userEvent from "@testing-library/user-event";
|
||||
import { beforeEach, describe, expect, it, MockedFunction, vi } from "vitest";
|
||||
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { chooseSelectOption, renderWithProviders } from "../../../tests/test-utils";
|
||||
import { Team } from "../key_team_helpers/key_list";
|
||||
import { TeamsResponse, useTeamsTable } from "@/app/(dashboard)/hooks/teams/useTeams";
|
||||
import { TeamsTable } from "./TeamsTable";
|
||||
|
|
@ -243,8 +243,7 @@ describe("row actions", () => {
|
|||
await user.click(await screen.findByText("Edit team"));
|
||||
expect(onEditTeam).toHaveBeenCalledWith(expect.objectContaining({ team_id: "team-1" }));
|
||||
|
||||
await user.click(screen.getByTestId("team-actions-team-1"));
|
||||
await user.click(await screen.findByText("Delete team"));
|
||||
await chooseSelectOption(user, screen.getByTestId("team-actions-team-1"), "Delete team", "menuitem");
|
||||
expect(onDeleteTeam).toHaveBeenCalledWith(expect.objectContaining({ team_id: "team-1" }));
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { screen, waitFor, within, fireEvent } from "@testing-library/react";
|
|||
import userEvent from "@testing-library/user-event";
|
||||
import type { OnUrlUpdateFunction } from "nuqs/adapters/testing";
|
||||
import { vi, it, expect, beforeEach, describe, Mock, MockedFunction } from "vitest";
|
||||
import { renderWithProviders } from "../../../tests/test-utils";
|
||||
import { chooseSelectOption, renderWithProviders } from "../../../tests/test-utils";
|
||||
import { VirtualKeysTable } from "./VirtualKeysTable";
|
||||
import { KeyResponse, Team } from "../key_team_helpers/key_list";
|
||||
import { useKeyInfo } from "@/app/(dashboard)/hooks/keys/useKeyInfo";
|
||||
|
|
@ -327,8 +327,7 @@ it("sorts by the backend max_budget field when 'Budget descending' is chosen fro
|
|||
const user = userEvent.setup();
|
||||
renderWithProviders(<VirtualKeysTable />);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-spend"));
|
||||
await user.click(await screen.findByText("Budget descending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-spend"), "Budget descending", "menuitem");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUseKeys).toHaveBeenLastCalledWith(
|
||||
|
|
@ -343,8 +342,7 @@ it("emphasizes the active field in the Spend / Budget header so the sorted colum
|
|||
const user = userEvent.setup();
|
||||
renderWithProviders(<VirtualKeysTable />);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-spend"));
|
||||
await user.click(await screen.findByText("Budget descending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-spend"), "Budget descending", "menuitem");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText("Budget", { selector: "[data-sort-field='max_budget']" })).toHaveClass("font-semibold");
|
||||
|
|
@ -356,8 +354,7 @@ it("sorts by spend ascending when 'Spend ascending' is chosen from the Spend / B
|
|||
const user = userEvent.setup();
|
||||
renderWithProviders(<VirtualKeysTable />);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-spend"));
|
||||
await user.click(await screen.findByText("Spend ascending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-spend"), "Spend ascending", "menuitem");
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockUseKeys).toHaveBeenLastCalledWith(1, 50, expect.objectContaining({ sortBy: "spend", sortOrder: "asc" }));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import userEvent, { PointerEventsCheckLevel } from "@testing-library/user-event";
|
||||
import { fireEvent, renderWithProviders, screen, waitFor } from "../../tests/test-utils";
|
||||
import { chooseSelectOption, fireEvent, renderWithProviders, screen, waitFor } from "../../tests/test-utils";
|
||||
import AddPassThroughEndpoint from "./add_pass_through";
|
||||
|
||||
const createPassThroughEndpoint = vi.fn();
|
||||
|
|
@ -153,8 +153,7 @@ describe("add_pass_through submit payload", () => {
|
|||
await openModal(user);
|
||||
await fillRequiredFields(user);
|
||||
|
||||
await user.click(screen.getByLabelText(/HTTP Methods/));
|
||||
await user.click(await screen.findByTitle("POST"));
|
||||
await chooseSelectOption(user, screen.getByLabelText(/HTTP Methods/), "POST");
|
||||
|
||||
await submit(user);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useState } from "react";
|
|||
import { Controller, useForm } from "react-hook-form";
|
||||
import userEvent, { PointerEventsCheckLevel } from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi, beforeEach } from "vitest";
|
||||
import { fireEvent, renderWithProviders, screen, waitFor } from "../../../tests/test-utils";
|
||||
import { chooseSelectOption, fireEvent, renderWithProviders, screen, waitFor } from "../../../tests/test-utils";
|
||||
import KeyLifecycleSettings from "./KeyLifecycleSettings";
|
||||
|
||||
const CREATE_PLACEHOLDER = "e.g., 30d or leave empty to never expire";
|
||||
|
|
@ -167,8 +167,7 @@ describe("KeyLifecycleSettings", () => {
|
|||
await user.click(screen.getByRole("switch"));
|
||||
expect(await screen.findByText("Rotation Interval")).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("90 days"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "90 days");
|
||||
|
||||
await waitFor(() => expect(screen.getAllByTitle("90 days").some(isRenderedSelection)).toBe(true));
|
||||
expect(screen.getByTestId("rotation-interval-value")).toHaveTextContent("90d");
|
||||
|
|
@ -181,8 +180,7 @@ describe("KeyLifecycleSettings", () => {
|
|||
await user.click(screen.getByRole("switch"));
|
||||
expect(await screen.findByText("Rotation Interval")).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Custom interval"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Custom interval");
|
||||
|
||||
expect(await screen.findByPlaceholderText("e.g., 1s, 5m, 2h, 14d")).toBeInTheDocument();
|
||||
expect(screen.getByText("Supported formats: seconds (s), minutes (m), hours (h), days (d)")).toBeInTheDocument();
|
||||
|
|
@ -196,8 +194,7 @@ describe("KeyLifecycleSettings", () => {
|
|||
await user.click(screen.getByRole("switch"));
|
||||
expect(await screen.findByText("Rotation Interval")).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Custom interval"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Custom interval");
|
||||
|
||||
const customInput = await screen.findByPlaceholderText("e.g., 1s, 5m, 2h, 14d");
|
||||
fireEvent.change(customInput, { target: { value: "14d" } });
|
||||
|
|
@ -213,14 +210,12 @@ describe("KeyLifecycleSettings", () => {
|
|||
await user.click(screen.getByRole("switch"));
|
||||
expect(await screen.findByText("Rotation Interval")).toBeInTheDocument();
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Custom interval"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Custom interval");
|
||||
const customInput = await screen.findByPlaceholderText("e.g., 1s, 5m, 2h, 14d");
|
||||
fireEvent.change(customInput, { target: { value: "14d" } });
|
||||
await waitFor(() => expect(screen.getByTestId("rotation-interval-value")).toHaveTextContent("14d"));
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("7 days"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "7 days");
|
||||
|
||||
await waitFor(() => expect(screen.getByTestId("rotation-interval-value")).toHaveTextContent("7d"));
|
||||
expect(screen.queryByPlaceholderText("e.g., 1s, 5m, 2h, 14d")).not.toBeInTheDocument();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { act, fireEvent, render, screen } from "@testing-library/react";
|
|||
import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import ModelSelector from "./ModelSelector";
|
||||
import { chooseSelectOption } from "../../../tests/test-utils";
|
||||
|
||||
vi.mock("@/components/llm_calls/fetch_models", () => ({
|
||||
fetchAvailableModels: vi.fn().mockResolvedValue([]),
|
||||
|
|
@ -9,8 +10,7 @@ vi.mock("@/components/llm_calls/fetch_models", () => ({
|
|||
|
||||
const openCustomModelInput = async () => {
|
||||
const user = userEvent.setup();
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Enter custom model"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Enter custom model");
|
||||
return screen.getByPlaceholderText("Enter custom model name");
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { describe, expect, it, vi } from "vitest";
|
|||
import { DataTable } from "./DataTable";
|
||||
import { DataTableMultiSortHeader, DataTableSortHeader } from "./DataTableSortHeader";
|
||||
import { DataTableViewOptions } from "./DataTableViewOptions";
|
||||
import { chooseSelectOption } from "../../../../tests/test-utils";
|
||||
|
||||
interface Person {
|
||||
id: string;
|
||||
|
|
@ -176,16 +177,13 @@ describe("DataTable sorting", () => {
|
|||
const user = userEvent.setup();
|
||||
render(<DataTable data={CHARLIE_ALICE_BOB} columns={dropdownSortColumns} sortingMode="client" />);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-name"));
|
||||
await user.click(await screen.findByText("Ascending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-name"), "Ascending", "menuitem");
|
||||
expect(names()).toEqual(["Alice", "Bob", "Charlie"]);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-name"));
|
||||
await user.click(await screen.findByText("Descending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-name"), "Descending", "menuitem");
|
||||
expect(names()).toEqual(["Charlie", "Bob", "Alice"]);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-name"));
|
||||
await user.click(await screen.findByText("Reset"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-name"), "Reset", "menuitem");
|
||||
expect(names()).toEqual(["Charlie", "Alice", "Bob"]);
|
||||
});
|
||||
|
||||
|
|
@ -202,12 +200,10 @@ describe("DataTable sorting", () => {
|
|||
/>,
|
||||
);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-spend"));
|
||||
await user.click(await screen.findByText("Budget descending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-spend"), "Budget descending", "menuitem");
|
||||
expect(onSortingChange).toHaveBeenLastCalledWith([{ id: "max_budget", desc: true }]);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-spend"));
|
||||
await user.click(await screen.findByText("Spend ascending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-spend"), "Spend ascending", "menuitem");
|
||||
expect(onSortingChange).toHaveBeenLastCalledWith([{ id: "spend", desc: false }]);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { useState } from "react";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { DataTableSortHeader, type DataTableSortVariant } from "./DataTableSortHeader";
|
||||
import { chooseSelectOption } from "../../../../tests/test-utils";
|
||||
|
||||
interface Item {
|
||||
name: string;
|
||||
|
|
@ -84,16 +85,13 @@ describe("DataTableSortHeader", () => {
|
|||
const user = userEvent.setup();
|
||||
render(<SortHeaderHarness variant="dropdown-tristate" />);
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-name"));
|
||||
await user.click(await screen.findByText("Descending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-name"), "Descending", "menuitem");
|
||||
expect(screen.getByTestId("sort-trigger-name").querySelector('[data-sort-indicator="desc"]')).not.toBeNull();
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-name"));
|
||||
await user.click(await screen.findByText("Ascending"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-name"), "Ascending", "menuitem");
|
||||
expect(screen.getByTestId("sort-trigger-name").querySelector('[data-sort-indicator="asc"]')).not.toBeNull();
|
||||
|
||||
await user.click(screen.getByTestId("sort-trigger-name"));
|
||||
await user.click(await screen.findByText("Reset"));
|
||||
await chooseSelectOption(user, screen.getByTestId("sort-trigger-name"), "Reset", "menuitem");
|
||||
expect(screen.getByTestId("sort-trigger-name").querySelector('[data-sort-indicator="none"]')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { describe, expect, it, vi } from "vitest";
|
|||
|
||||
import { PaginatedSearchSelect } from "./PaginatedSearchSelect";
|
||||
import type { SearchSelectOption } from "./SearchSelect";
|
||||
import { chooseSelectOption } from "../../../tests/test-utils";
|
||||
|
||||
const OPTIONS: SearchSelectOption[] = [
|
||||
{ label: "alias-alpha", value: "alias-alpha" },
|
||||
|
|
@ -63,8 +64,7 @@ describe("PaginatedSearchSelect", () => {
|
|||
}
|
||||
render(<Controlled />);
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("alias-beta"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "alias-beta");
|
||||
|
||||
expect(screen.getByRole("combobox")).toHaveValue("alias-beta");
|
||||
await new Promise((resolve) => setTimeout(resolve, 400));
|
||||
|
|
@ -136,8 +136,7 @@ describe("PaginatedSearchSelect", () => {
|
|||
const onValueChange = vi.fn();
|
||||
renderSelect({ onValueChange });
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("alias-beta"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "alias-beta");
|
||||
|
||||
expect(onValueChange).toHaveBeenCalledWith("alias-beta");
|
||||
});
|
||||
|
|
@ -255,8 +254,7 @@ describe("PaginatedSearchSelect", () => {
|
|||
}
|
||||
render(<Refetching />);
|
||||
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Beta Team"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Beta Team");
|
||||
await user.click(screen.getByRole("button", { name: "refetch" }));
|
||||
|
||||
expect(screen.getByRole("combobox")).toHaveValue("Beta Team");
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import userEvent from "@testing-library/user-event";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { SearchSelect } from "./SearchSelect";
|
||||
import { chooseSelectOption } from "../../../tests/test-utils";
|
||||
|
||||
const OPTIONS = [
|
||||
{ label: "Acme Prod", value: "team-1" },
|
||||
|
|
@ -71,8 +72,7 @@ describe("SearchSelect", () => {
|
|||
const onValueChange = vi.fn();
|
||||
const user = userEvent.setup();
|
||||
render(<SearchSelect options={OPTIONS} onValueChange={onValueChange} />);
|
||||
await user.click(screen.getByRole("combobox"));
|
||||
await user.click(await screen.findByText("Growth"));
|
||||
await chooseSelectOption(user, screen.getByRole("combobox"), "Growth");
|
||||
expect(onValueChange).toHaveBeenCalledWith("team-2");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import userEvent from "@testing-library/user-event";
|
|||
import { useState } from "react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { renderWithProviders, testQueryClient } from "../../../tests/test-utils";
|
||||
import { chooseSelectOption, renderWithProviders, testQueryClient } from "../../../tests/test-utils";
|
||||
import { ERROR_CODE_OPTIONS } from "./constants";
|
||||
import { LOG_FILTER_IDS } from "./log_filter_logic";
|
||||
import { RequestLogsFilters } from "./RequestLogsFilters";
|
||||
|
|
@ -125,8 +125,7 @@ describe("RequestLogsFilters", () => {
|
|||
const user = userEvent.setup();
|
||||
const { set } = renderFilters();
|
||||
|
||||
await user.click(await screen.findByPlaceholderText("Search an internal user"));
|
||||
await user.click(await screen.findByText("alice@example.com"));
|
||||
await chooseSelectOption(user, await screen.findByPlaceholderText("Search an internal user"), "alice@example.com");
|
||||
|
||||
expect(set).toHaveBeenCalledWith(LOG_FILTER_IDS.USER_ID, "alice@example.com");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -45,19 +45,22 @@ const pointerBlocked = (element: HTMLElement): boolean => {
|
|||
};
|
||||
|
||||
/**
|
||||
* Opens a Base UI Select and picks an option by its accessible name.
|
||||
* Opens a Base UI popup and picks an entry by its accessible name.
|
||||
*
|
||||
* The option is in the DOM one render before the popup finishes entering, and until then its
|
||||
* positioner still carries `pointer-events: none`, which user-event refuses to click. Waiting on
|
||||
* the option text alone is a race that React 19's flush timing loses.
|
||||
* Querying the entry by text or by a title attribute matches the moment the node exists, which is
|
||||
* one render before the popup finishes entering. Until then the positioner still carries
|
||||
* `pointer-events: none` and user-event refuses to click, so that shape is a race a fast machine
|
||||
* loses. The role query only matches once the popup is open to the accessibility tree, which is
|
||||
* what makes this wait correct rather than lucky.
|
||||
*/
|
||||
export const chooseSelectOption = async (
|
||||
user: Pick<ReturnType<typeof userEvent.setup>, "click">,
|
||||
trigger: HTMLElement,
|
||||
optionName: string | RegExp,
|
||||
role: "option" | "menuitem" | "menuitemradio" = "option",
|
||||
) => {
|
||||
await user.click(trigger);
|
||||
const option = await screen.findByRole("option", { name: optionName });
|
||||
const option = await screen.findByRole(role, { name: optionName });
|
||||
await waitFor(() => expect(pointerBlocked(option)).toBe(false));
|
||||
await user.click(option);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue