mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
address greptile review feedback (greploop iteration 1)
- Prefix all 62 test names with 'should' per AGENTS.md convention
- Wrap fireEvent.click() calls in act() in ModelsCell.test.tsx
- Replace querySelector('.bg-blue-500') with within()+getByTestId in
TeamsFilters.test.tsx; add data-testid="active-filter-indicator" to source
- Add aria-label="Close" to X button in DeleteTeamModal.tsx; update test
to use getByRole('button', { name: /close/i }) instead of fragile index
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1c1c84974f
commit
ba617ba4cd
7 changed files with 84 additions and 79 deletions
|
|
@ -34,19 +34,19 @@ const buildProps = (overrides: Record<string, unknown> = {}) => ({
|
|||
});
|
||||
|
||||
describe("ModelRetrySettingsTab", () => {
|
||||
it("renders the 'Global Retry Policy' heading when selectedModelGroup is 'global'", () => {
|
||||
it("should render the 'Global Retry Policy' heading when selectedModelGroup is 'global'", () => {
|
||||
render(<ModelRetrySettingsTab {...buildProps()} />);
|
||||
|
||||
expect(screen.getByText("Global Retry Policy")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a model-specific heading when a model group is selected", () => {
|
||||
it("should render a model-specific heading when a model group is selected", () => {
|
||||
render(<ModelRetrySettingsTab {...buildProps({ selectedModelGroup: "gpt-4" })} />);
|
||||
|
||||
expect(screen.getByText("Retry Policy for gpt-4")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders a row for every error type in the retry policy map", () => {
|
||||
it("should render a row for every error type in the retry policy map", () => {
|
||||
render(<ModelRetrySettingsTab {...buildProps()} />);
|
||||
|
||||
expect(screen.getByText(/BadRequestError \(400\)/)).toBeInTheDocument();
|
||||
|
|
@ -57,7 +57,7 @@ describe("ModelRetrySettingsTab", () => {
|
|||
expect(screen.getByText(/InternalServerError \(500\)/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses defaultRetry when globalRetryPolicy is null (global scope)", () => {
|
||||
it("should use defaultRetry when globalRetryPolicy is null (global scope)", () => {
|
||||
render(<ModelRetrySettingsTab {...buildProps({ defaultRetry: 3 })} />);
|
||||
|
||||
// All 6 spinbutton inputs should show the defaultRetry value
|
||||
|
|
@ -67,7 +67,7 @@ describe("ModelRetrySettingsTab", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("shows globalRetryPolicy values when they are set (global scope)", () => {
|
||||
it("should show globalRetryPolicy values when they are set (global scope)", () => {
|
||||
const globalRetryPolicy: GlobalRetryPolicy = {
|
||||
RateLimitErrorRetries: 5,
|
||||
};
|
||||
|
|
@ -82,7 +82,7 @@ describe("ModelRetrySettingsTab", () => {
|
|||
expect(inputs[0]).toHaveValue("0");
|
||||
});
|
||||
|
||||
it("falls back to globalRetryPolicy when no model-specific value is set (model scope)", () => {
|
||||
it("should fall back to globalRetryPolicy when no model-specific value is set (model scope)", () => {
|
||||
const globalRetryPolicy: GlobalRetryPolicy = {
|
||||
TimeoutErrorRetries: 7,
|
||||
};
|
||||
|
|
@ -105,7 +105,7 @@ describe("ModelRetrySettingsTab", () => {
|
|||
expect(inputs[0]).toHaveValue("1");
|
||||
});
|
||||
|
||||
it("prefers model-specific retry count over the global value (model scope)", () => {
|
||||
it("should prefer model-specific retry count over the global value (model scope)", () => {
|
||||
const globalRetryPolicy: GlobalRetryPolicy = {
|
||||
RateLimitErrorRetries: 3,
|
||||
};
|
||||
|
|
@ -128,7 +128,7 @@ describe("ModelRetrySettingsTab", () => {
|
|||
expect(inputs[3]).toHaveValue("9");
|
||||
});
|
||||
|
||||
it("shows the global reference value text for each row in model-specific scope", () => {
|
||||
it("should show the global reference value text for each row in model-specific scope", () => {
|
||||
const globalRetryPolicy: GlobalRetryPolicy = { BadRequestErrorRetries: 2 };
|
||||
render(
|
||||
<ModelRetrySettingsTab
|
||||
|
|
@ -144,13 +144,13 @@ describe("ModelRetrySettingsTab", () => {
|
|||
expect(screen.getByText("(Global: 2)")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show global reference annotations in global scope", () => {
|
||||
it("should not show global reference annotations in global scope", () => {
|
||||
render(<ModelRetrySettingsTab {...buildProps({ selectedModelGroup: "global" })} />);
|
||||
|
||||
expect(screen.queryByText(/Global:/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls handleSaveRetrySettings when the Save button is clicked", async () => {
|
||||
it("should call handleSaveRetrySettings when the Save button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const handleSaveRetrySettings = vi.fn();
|
||||
render(<ModelRetrySettingsTab {...buildProps({ handleSaveRetrySettings })} />);
|
||||
|
|
@ -160,7 +160,7 @@ describe("ModelRetrySettingsTab", () => {
|
|||
expect(handleSaveRetrySettings).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("calls setGlobalRetryPolicy with an updater function when an input changes (global scope)", async () => {
|
||||
it("should call setGlobalRetryPolicy with an updater function when an input changes (global scope)", async () => {
|
||||
const user = userEvent.setup();
|
||||
const setGlobalRetryPolicy = vi.fn();
|
||||
render(
|
||||
|
|
@ -188,7 +188,7 @@ describe("ModelRetrySettingsTab", () => {
|
|||
expect(result).toMatchObject({ BadRequestErrorRetries: 4 });
|
||||
});
|
||||
|
||||
it("calls setModelGroupRetryPolicy with an updater function when an input changes (model scope)", async () => {
|
||||
it("should call setModelGroupRetryPolicy with an updater function when an input changes (model scope)", async () => {
|
||||
const user = userEvent.setup();
|
||||
const setModelGroupRetryPolicy = vi.fn();
|
||||
render(
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { render, screen } from "@testing-library/react";
|
||||
import { render, screen, within } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import React from "react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
|
@ -39,7 +39,7 @@ const renderFilters = (overrides: Partial<Parameters<typeof TeamsFilters>[0]> =
|
|||
};
|
||||
|
||||
describe("TeamsFilters", () => {
|
||||
it("renders the team name search input, Filters button, and Reset Filters button", () => {
|
||||
it("should render the team name search input, Filters button, and Reset Filters button", () => {
|
||||
renderFilters();
|
||||
|
||||
expect(screen.getByPlaceholderText("Search by Team Name...")).toBeInTheDocument();
|
||||
|
|
@ -47,13 +47,13 @@ describe("TeamsFilters", () => {
|
|||
expect(screen.getByRole("button", { name: /reset filters/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("reflects the current team_alias filter value in the search input", () => {
|
||||
it("should reflect the current team_alias filter value in the search input", () => {
|
||||
renderFilters({ filters: { ...emptyFilters, team_alias: "Platform" } });
|
||||
|
||||
expect(screen.getByPlaceholderText("Search by Team Name...")).toHaveValue("Platform");
|
||||
});
|
||||
|
||||
it("calls onChange with 'team_alias' key when the search input changes", async () => {
|
||||
it("should call onChange with 'team_alias' key when the search input changes", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onChange = vi.fn();
|
||||
renderFilters({ onChange });
|
||||
|
|
@ -63,7 +63,7 @@ describe("TeamsFilters", () => {
|
|||
expect(onChange).toHaveBeenCalledWith("team_alias", expect.stringContaining("D"));
|
||||
});
|
||||
|
||||
it("calls onToggleFilters with the inverted boolean when the Filters button is clicked", async () => {
|
||||
it("should call onToggleFilters with the inverted boolean when the Filters button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onToggleFilters = vi.fn();
|
||||
renderFilters({ showFilters: false, onToggleFilters });
|
||||
|
|
@ -73,7 +73,7 @@ describe("TeamsFilters", () => {
|
|||
expect(onToggleFilters).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it("calls onToggleFilters(false) when filters are currently expanded", async () => {
|
||||
it("should call onToggleFilters(false) when filters are currently expanded", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onToggleFilters = vi.fn();
|
||||
renderFilters({ showFilters: true, onToggleFilters });
|
||||
|
|
@ -83,7 +83,7 @@ describe("TeamsFilters", () => {
|
|||
expect(onToggleFilters).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it("calls onReset when the Reset Filters button is clicked", async () => {
|
||||
it("should call onReset when the Reset Filters button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onReset = vi.fn();
|
||||
renderFilters({ onReset });
|
||||
|
|
@ -93,19 +93,19 @@ describe("TeamsFilters", () => {
|
|||
expect(onReset).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not show the Team ID input when showFilters is false", () => {
|
||||
it("should not show the Team ID input when showFilters is false", () => {
|
||||
renderFilters({ showFilters: false });
|
||||
|
||||
expect(screen.queryByPlaceholderText("Enter Team ID")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows the Team ID input when showFilters is true", () => {
|
||||
it("should show the Team ID input when showFilters is true", () => {
|
||||
renderFilters({ showFilters: true });
|
||||
|
||||
expect(screen.getByPlaceholderText("Enter Team ID")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("calls onChange with 'team_id' key when the Team ID input changes", async () => {
|
||||
it("should call onChange with 'team_id' key when the Team ID input changes", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onChange = vi.fn();
|
||||
renderFilters({ showFilters: true, onChange });
|
||||
|
|
@ -115,37 +115,37 @@ describe("TeamsFilters", () => {
|
|||
expect(onChange).toHaveBeenCalledWith("team_id", expect.stringContaining("a"));
|
||||
});
|
||||
|
||||
it("reflects the current team_id filter value in the Team ID input", () => {
|
||||
it("should reflect the current team_id filter value in the Team ID input", () => {
|
||||
renderFilters({ showFilters: true, filters: { ...emptyFilters, team_id: "team-xyz" } });
|
||||
|
||||
expect(screen.getByPlaceholderText("Enter Team ID")).toHaveValue("team-xyz");
|
||||
});
|
||||
|
||||
it("shows a blue dot indicator on the Filters button when team_alias filter is active", () => {
|
||||
it("should show the active filter indicator on the Filters button when team_alias is set", () => {
|
||||
renderFilters({ filters: { ...emptyFilters, team_alias: "Platform" } });
|
||||
|
||||
const filtersButton = screen.getByRole("button", { name: /^filters$/i });
|
||||
expect(filtersButton.querySelector(".bg-blue-500")).toBeInTheDocument();
|
||||
expect(within(filtersButton).getByTestId("active-filter-indicator")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a blue dot indicator on the Filters button when team_id filter is active", () => {
|
||||
it("should show the active filter indicator on the Filters button when team_id is set", () => {
|
||||
renderFilters({ filters: { ...emptyFilters, team_id: "team-123" } });
|
||||
|
||||
const filtersButton = screen.getByRole("button", { name: /^filters$/i });
|
||||
expect(filtersButton.querySelector(".bg-blue-500")).toBeInTheDocument();
|
||||
expect(within(filtersButton).getByTestId("active-filter-indicator")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a blue dot indicator on the Filters button when organization_id filter is active", () => {
|
||||
it("should show the active filter indicator on the Filters button when organization_id is set", () => {
|
||||
renderFilters({ filters: { ...emptyFilters, organization_id: "org-1" } });
|
||||
|
||||
const filtersButton = screen.getByRole("button", { name: /^filters$/i });
|
||||
expect(filtersButton.querySelector(".bg-blue-500")).toBeInTheDocument();
|
||||
expect(within(filtersButton).getByTestId("active-filter-indicator")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show the blue dot indicator when all filters are empty", () => {
|
||||
it("should not show the active filter indicator when all filters are empty", () => {
|
||||
renderFilters({ filters: emptyFilters });
|
||||
|
||||
const filtersButton = screen.getByRole("button", { name: /^filters$/i });
|
||||
expect(filtersButton.querySelector(".bg-blue-500")).not.toBeInTheDocument();
|
||||
expect(within(filtersButton).queryByTestId("active-filter-indicator")).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ const TeamsFilters = ({
|
|||
</svg>
|
||||
Filters
|
||||
{(filters.team_id || filters.team_alias || filters.organization_id) && (
|
||||
<span className="w-2 h-2 rounded-full bg-blue-500"></span>
|
||||
<span data-testid="active-filter-indicator" className="w-2 h-2 rounded-full bg-blue-500"></span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { act, render, screen } from "@testing-library/react";
|
||||
import React from "react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { Team } from "@/components/key_team_helpers/key_list";
|
||||
|
|
@ -45,19 +45,19 @@ const renderModelsCell = (team: Team) =>
|
|||
);
|
||||
|
||||
describe("ModelsCell", () => {
|
||||
it("shows 'All Proxy Models' badge when the models array is empty", () => {
|
||||
it("should show 'All Proxy Models' badge when the models array is empty", () => {
|
||||
renderModelsCell(makeTeam([]));
|
||||
|
||||
expect(screen.getByText("All Proxy Models")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows an 'All Proxy Models' badge when the model value is 'all-proxy-models'", () => {
|
||||
it("should show an 'All Proxy Models' badge when the model value is 'all-proxy-models'", () => {
|
||||
renderModelsCell(makeTeam(["all-proxy-models"]));
|
||||
|
||||
expect(screen.getByText("All Proxy Models")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("displays individual model badges for up to 3 models without an accordion", () => {
|
||||
it("should display individual model badges for up to 3 models without an accordion", () => {
|
||||
renderModelsCell(makeTeam(["gpt-4", "gpt-3.5-turbo", "claude-3"]));
|
||||
|
||||
expect(screen.getByText("gpt-4")).toBeInTheDocument();
|
||||
|
|
@ -66,7 +66,7 @@ describe("ModelsCell", () => {
|
|||
expect(screen.queryByRole("button", { name: /accordion/i })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("truncates model names longer than 30 characters with an ellipsis", () => {
|
||||
it("should truncate model names longer than 30 characters with an ellipsis", () => {
|
||||
const longName = "a-very-long-model-name-exceeding-thirty-chars";
|
||||
renderModelsCell(makeTeam([longName]));
|
||||
|
||||
|
|
@ -75,7 +75,7 @@ describe("ModelsCell", () => {
|
|||
expect(badge.textContent!.length).toBeLessThanOrEqual(33); // 30 chars + "..."
|
||||
});
|
||||
|
||||
it("shows the first 3 models and a '+N more models' badge when there are more than 3 models", () => {
|
||||
it("should show the first 3 models and a '+N more models' badge when there are more than 3 models", () => {
|
||||
renderModelsCell(makeTeam(["m1", "m2", "m3", "m4", "m5"]));
|
||||
|
||||
expect(screen.getByText("m1")).toBeInTheDocument();
|
||||
|
|
@ -86,43 +86,51 @@ describe("ModelsCell", () => {
|
|||
expect(screen.queryByText("m5")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("uses singular 'more model' when there is exactly 1 overflow model", () => {
|
||||
it("should use singular 'more model' when there is exactly 1 overflow model", () => {
|
||||
renderModelsCell(makeTeam(["m1", "m2", "m3", "m4"]));
|
||||
|
||||
expect(screen.getByText("+1 more model")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows the accordion toggle button when there are more than 3 models", () => {
|
||||
it("should show the accordion toggle button when there are more than 3 models", () => {
|
||||
renderModelsCell(makeTeam(["m1", "m2", "m3", "m4"]));
|
||||
|
||||
expect(screen.getByRole("button", { name: /accordion/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("expands to show all models when the accordion toggle is clicked", () => {
|
||||
it("should expand to show all models when the accordion toggle is clicked", () => {
|
||||
renderModelsCell(makeTeam(["m1", "m2", "m3", "m4", "m5"]));
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /accordion/i }));
|
||||
act(() => {
|
||||
screen.getByRole("button", { name: /accordion/i }).click();
|
||||
});
|
||||
|
||||
expect(screen.getByText("m4")).toBeInTheDocument();
|
||||
expect(screen.getByText("m5")).toBeInTheDocument();
|
||||
expect(screen.queryByText("+2 more models")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("collapses back to show the overflow badge after a second click on the toggle", () => {
|
||||
it("should collapse back to show the overflow badge after a second click on the toggle", () => {
|
||||
renderModelsCell(makeTeam(["m1", "m2", "m3", "m4", "m5"]));
|
||||
|
||||
const toggle = screen.getByRole("button", { name: /accordion/i });
|
||||
fireEvent.click(toggle);
|
||||
fireEvent.click(toggle);
|
||||
act(() => {
|
||||
toggle.click();
|
||||
});
|
||||
act(() => {
|
||||
toggle.click();
|
||||
});
|
||||
|
||||
expect(screen.queryByText("m4")).not.toBeInTheDocument();
|
||||
expect(screen.getByText("+2 more models")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders 'all-proxy-models' entries in the overflow section as 'All Proxy Models' badges", () => {
|
||||
it("should render 'all-proxy-models' entries in the overflow section as 'All Proxy Models' badges", () => {
|
||||
renderModelsCell(makeTeam(["m1", "m2", "m3", "all-proxy-models"]));
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /accordion/i }));
|
||||
act(() => {
|
||||
screen.getByRole("button", { name: /accordion/i }).click();
|
||||
});
|
||||
|
||||
// There should now be an "All Proxy Models" badge in the expanded section
|
||||
expect(screen.getByText("All Proxy Models")).toBeInTheDocument();
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const renderModal = (props: Partial<Parameters<typeof DeleteTeamModal>[0]> = {})
|
|||
};
|
||||
|
||||
describe("DeleteTeamModal", () => {
|
||||
it("renders the title, team name label, and confirmation input", () => {
|
||||
it("should render the title, team name label, and confirmation input", () => {
|
||||
renderModal();
|
||||
|
||||
expect(screen.getByText("Delete Team")).toBeInTheDocument();
|
||||
|
|
@ -40,27 +40,27 @@ describe("DeleteTeamModal", () => {
|
|||
expect(screen.getByPlaceholderText("Enter team name exactly")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders Cancel and Force Delete buttons", () => {
|
||||
it("should render Cancel and Force Delete buttons", () => {
|
||||
renderModal();
|
||||
|
||||
expect(screen.getByRole("button", { name: /^cancel$/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole("button", { name: /force delete/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not show the warning banner when the team has no keys", () => {
|
||||
it("should not show the warning banner when the team has no keys", () => {
|
||||
renderModal({ teams: [makeTeam({ keys: [] })] });
|
||||
|
||||
expect(screen.queryByText(/Warning/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a warning with singular 'key' when the team has exactly 1 key", () => {
|
||||
it("should show a warning with singular 'key' when the team has exactly 1 key", () => {
|
||||
const team = makeTeam({ keys: [{ token: "tok-1" } as any] });
|
||||
renderModal({ teams: [team] });
|
||||
|
||||
expect(screen.getByText(/This team has 1 associated key\./)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("shows a warning with plural 'keys' when the team has multiple keys", () => {
|
||||
it("should show a warning with plural 'keys' when the team has multiple keys", () => {
|
||||
const team = makeTeam({
|
||||
keys: [{ token: "tok-1" } as any, { token: "tok-2" } as any, { token: "tok-3" } as any],
|
||||
});
|
||||
|
|
@ -69,20 +69,20 @@ describe("DeleteTeamModal", () => {
|
|||
expect(screen.getByText(/This team has 3 associated keys\./)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("notes that associated keys will also be deleted in the warning", () => {
|
||||
it("should note that associated keys will also be deleted in the warning", () => {
|
||||
const team = makeTeam({ keys: [{ token: "tok-1" } as any] });
|
||||
renderModal({ teams: [team] });
|
||||
|
||||
expect(screen.getByText(/Deleting the team will also delete all associated keys/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Force Delete button is disabled when the input is empty", () => {
|
||||
it("should disable Force Delete when the input is empty", () => {
|
||||
renderModal();
|
||||
|
||||
expect(screen.getByRole("button", { name: /force delete/i })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("Force Delete button remains disabled when the input does not exactly match the team name", async () => {
|
||||
it("should keep Force Delete disabled when the input does not exactly match the team name", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderModal();
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ describe("DeleteTeamModal", () => {
|
|||
expect(screen.getByRole("button", { name: /force delete/i })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("enables Force Delete only after typing the exact team name (case-sensitive)", async () => {
|
||||
it("should enable Force Delete only after typing the exact team name (case-sensitive)", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderModal();
|
||||
|
||||
|
|
@ -102,7 +102,7 @@ describe("DeleteTeamModal", () => {
|
|||
expect(screen.getByRole("button", { name: /force delete/i })).toBeEnabled();
|
||||
});
|
||||
|
||||
it("calls onConfirm when Force Delete is clicked with a valid input", async () => {
|
||||
it("should call onConfirm when Force Delete is clicked with a valid input", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onConfirm = vi.fn();
|
||||
renderModal({ onConfirm });
|
||||
|
|
@ -113,7 +113,7 @@ describe("DeleteTeamModal", () => {
|
|||
expect(onConfirm).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not call onConfirm when Force Delete is clicked with an invalid input", async () => {
|
||||
it("should not call onConfirm when Force Delete is clicked with an invalid input", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onConfirm = vi.fn();
|
||||
renderModal({ onConfirm });
|
||||
|
|
@ -124,7 +124,7 @@ describe("DeleteTeamModal", () => {
|
|||
expect(onConfirm).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("calls onCancel when the Cancel button is clicked", async () => {
|
||||
it("should call onCancel when the Cancel button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onCancel = vi.fn();
|
||||
renderModal({ onCancel });
|
||||
|
|
@ -134,20 +134,17 @@ describe("DeleteTeamModal", () => {
|
|||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("calls onCancel when the X close button is clicked", async () => {
|
||||
it("should call onCancel when the Close button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
const onCancel = vi.fn();
|
||||
renderModal({ onCancel });
|
||||
|
||||
// The first button in the header is the X close button (no accessible label)
|
||||
const allButtons = screen.getAllByRole("button");
|
||||
const xButton = allButtons[0];
|
||||
await user.click(xButton);
|
||||
await user.click(screen.getByRole("button", { name: /^close$/i }));
|
||||
|
||||
expect(onCancel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("resets the confirmation input when Cancel is clicked", async () => {
|
||||
it("should reset the confirmation input when Cancel is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderModal();
|
||||
|
||||
|
|
@ -160,15 +157,14 @@ describe("DeleteTeamModal", () => {
|
|||
expect(input).toHaveValue("");
|
||||
});
|
||||
|
||||
it("resets the confirmation input when the X close button is clicked", async () => {
|
||||
it("should reset the confirmation input when the Close button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderModal();
|
||||
|
||||
const input = screen.getByPlaceholderText("Enter team name exactly");
|
||||
await user.type(input, "Engineering");
|
||||
|
||||
const allButtons = screen.getAllByRole("button");
|
||||
await user.click(allButtons[0]);
|
||||
await user.click(screen.getByRole("button", { name: /^close$/i }));
|
||||
|
||||
expect(input).toHaveValue("");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const DeleteTeamModal = ({ teams, teamToDelete, onCancel, onConfirm }: DeleteTea
|
|||
<div className="flex items-center justify-between px-6 py-4 border-b border-gray-200">
|
||||
<h3 className="text-lg font-semibold text-gray-900">Delete Team</h3>
|
||||
<button
|
||||
aria-label="Close"
|
||||
onClick={() => {
|
||||
onCancel();
|
||||
setDeleteConfirmInput("");
|
||||
|
|
|
|||
|
|
@ -2,63 +2,63 @@ import { describe, expect, it } from "vitest";
|
|||
import { valueFormatter, valueFormatterSpend } from "./value_formatters";
|
||||
|
||||
describe("valueFormatter", () => {
|
||||
it("formats numbers >= 1,000,000 as millions with 2 decimal places", () => {
|
||||
it("should format numbers >= 1,000,000 as millions with 2 decimal places", () => {
|
||||
expect(valueFormatter(1_000_000)).toBe("1.00M");
|
||||
expect(valueFormatter(1_500_000)).toBe("1.50M");
|
||||
expect(valueFormatter(2_750_000)).toBe("2.75M");
|
||||
expect(valueFormatter(10_000_000)).toBe("10.00M");
|
||||
});
|
||||
|
||||
it("formats numbers in the thousands range as 'k' suffix", () => {
|
||||
it("should format numbers in the thousands range as 'k' suffix", () => {
|
||||
expect(valueFormatter(1_000)).toBe("1k");
|
||||
expect(valueFormatter(5_500)).toBe("5.5k");
|
||||
expect(valueFormatter(999_999)).toBe("999.999k");
|
||||
});
|
||||
|
||||
it("returns the plain string for numbers below 1,000", () => {
|
||||
it("should return the plain string for numbers below 1,000", () => {
|
||||
expect(valueFormatter(0)).toBe("0");
|
||||
expect(valueFormatter(1)).toBe("1");
|
||||
expect(valueFormatter(999)).toBe("999");
|
||||
expect(valueFormatter(42)).toBe("42");
|
||||
});
|
||||
|
||||
it("treats exactly 1,000,000 as millions boundary", () => {
|
||||
it("should treat exactly 1,000,000 as the millions boundary", () => {
|
||||
expect(valueFormatter(1_000_000)).toBe("1.00M");
|
||||
});
|
||||
|
||||
it("treats exactly 1,000 as thousands boundary", () => {
|
||||
it("should treat exactly 1,000 as the thousands boundary", () => {
|
||||
expect(valueFormatter(1_000)).toBe("1k");
|
||||
});
|
||||
});
|
||||
|
||||
describe("valueFormatterSpend", () => {
|
||||
it("returns '$0' when the value is exactly zero", () => {
|
||||
it("should return '$0' when the value is exactly zero", () => {
|
||||
expect(valueFormatterSpend(0)).toBe("$0");
|
||||
});
|
||||
|
||||
it("formats numbers >= 1,000,000 as dollar millions", () => {
|
||||
it("should format numbers >= 1,000,000 as dollar millions", () => {
|
||||
expect(valueFormatterSpend(1_000_000)).toBe("$1M");
|
||||
expect(valueFormatterSpend(2_500_000)).toBe("$2.5M");
|
||||
expect(valueFormatterSpend(10_000_000)).toBe("$10M");
|
||||
});
|
||||
|
||||
it("formats numbers >= 1,000 as dollar thousands", () => {
|
||||
it("should format numbers >= 1,000 as dollar thousands", () => {
|
||||
expect(valueFormatterSpend(1_000)).toBe("$1k");
|
||||
expect(valueFormatterSpend(5_500)).toBe("$5.5k");
|
||||
expect(valueFormatterSpend(999_999)).toBe("$999.999k");
|
||||
});
|
||||
|
||||
it("formats numbers below 1,000 as plain dollar amounts", () => {
|
||||
it("should format numbers below 1,000 as plain dollar amounts", () => {
|
||||
expect(valueFormatterSpend(1)).toBe("$1");
|
||||
expect(valueFormatterSpend(99.99)).toBe("$99.99");
|
||||
expect(valueFormatterSpend(999)).toBe("$999");
|
||||
});
|
||||
|
||||
it("treats exactly 1,000,000 as millions boundary", () => {
|
||||
it("should treat exactly 1,000,000 as the millions boundary", () => {
|
||||
expect(valueFormatterSpend(1_000_000)).toBe("$1M");
|
||||
});
|
||||
|
||||
it("treats exactly 1,000 as thousands boundary", () => {
|
||||
it("should treat exactly 1,000 as the thousands boundary", () => {
|
||||
expect(valueFormatterSpend(1_000)).toBe("$1k");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue