From dad4a8234fc77b045cd4f8728fa0e61ef1fc8615 Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Thu, 17 Sep 2026 18:09:56 +0300
Subject: [PATCH] Fix unit tests
---
...d.admin.registration.$tid.browser.test.tsx | 176 +++++++++++-------
...ration.$tid.captain-label.browser.test.tsx | 46 ++---
2 files changed, 135 insertions(+), 87 deletions(-)
diff --git a/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.browser.test.tsx b/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.browser.test.tsx
index 79489da08..c6a649908 100644
--- a/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.browser.test.tsx
+++ b/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.browser.test.tsx
@@ -1,39 +1,12 @@
+import type { LoaderFunctionArgs } from "react-router";
import { createMemoryRouter, RouterProvider } from "react-router";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { userEvent } from "vitest/browser";
import { render } from "vitest-browser-react";
+import { TournamentProvider } from "~/features/tournament/tournament-context";
+import type { Tournament } from "~/features/tournament-bracket/core/Tournament";
import TournamentAdminRegistrationPage from "./to.$id.admin.registration.$tid";
-const { mockTournament, mockLoaderData, submitMock, loadMock } = vi.hoisted(
- () => ({
- mockTournament: {
- ctx: { id: 1, settings: { requireInGameNames: false } },
- canEditTournamentNames: (): boolean => false,
- },
- mockLoaderData: { team: null as unknown },
- submitMock: vi.fn(),
- loadMock: vi.fn(),
- }),
-);
-
-vi.mock("react-router", async () => {
- const actual = await vi.importActual("react-router");
- return {
- ...actual,
- useLoaderData: () => mockLoaderData,
- useFetcher: () => ({
- data: undefined,
- state: "idle",
- submit: submitMock,
- load: loadMock,
- }),
- };
-});
-
-vi.mock("~/features/tournament/tournament-context", () => ({
- useTournament: () => mockTournament,
-}));
-
// stubbed so importing the route in a browser test doesn't pull in the database-backed action
vi.mock(
"~/features/tournament-admin/actions/to.$id.admin.registration.server",
@@ -45,10 +18,77 @@ vi.mock(
() => ({ loader: vi.fn() }),
);
+const ROUTE_ID = "registration";
+
+const tournament = {
+ ctx: { id: 1, settings: { requireInGameNames: false } },
+ canEditTournamentNames: (): boolean => false,
+} as unknown as Tournament;
+
+const SEARCHABLE_USERS = [
+ {
+ type: "user" as const,
+ id: 1,
+ name: "sanu",
+ inGameName: null,
+ avatarUrl: null,
+ discordId: "1",
+ discordAvatar: null,
+ customUrl: null,
+ plusTier: null,
+ },
+ {
+ type: "user" as const,
+ id: 2,
+ name: "Jolt",
+ inGameName: null,
+ avatarUrl: null,
+ discordId: "2",
+ discordAvatar: null,
+ customUrl: null,
+ plusTier: null,
+ },
+];
+
+let loaderData: { team: unknown };
+let actionCallCount = 0;
+
function renderPage() {
const router = createMemoryRouter(
- [{ path: "/", element: }],
- { initialEntries: ["/"] },
+ [
+ {
+ id: ROUTE_ID,
+ path: "/",
+ element: (
+
+
+
+ ),
+ loader: () => loaderData,
+ action: () => {
+ actionCallCount += 1;
+ return null;
+ },
+ },
+ {
+ path: "/search",
+ loader: ({ request }: LoaderFunctionArgs) => {
+ // biome-ignore lint/plugin: stub loader standing in for the real search route, reading the request the component built
+ const query = new URL(request.url).searchParams.get("q") ?? "";
+ return {
+ query,
+ type: "users",
+ results: SEARCHABLE_USERS.filter(
+ (user) => String(user.id) === query || user.name === query,
+ ),
+ };
+ },
+ },
+ ],
+ {
+ initialEntries: ["/"],
+ hydrationData: { loaderData: { [ROUTE_ID]: loaderData } },
+ },
);
return render();
@@ -56,24 +96,25 @@ function renderPage() {
const CAPTAIN_NOT_A_MEMBER_ERROR = "The captain must be one of the players";
-describe("tournament admin registration - captain field", () => {
- beforeEach(() => {
- submitMock.mockClear();
- loadMock.mockClear();
- });
+beforeEach(() => {
+ actionCallCount = 0;
+});
+describe("tournament admin registration - captain field", () => {
test("removing the captain's roster row does not leave a stale captain that fails validation", async () => {
// captain (OWNER) is the first roster member
- mockLoaderData.team = {
- id: 10,
- name: "low ink buddies",
- team: undefined,
- pickupAvatarUrl: null,
- avatarImgId: null,
- members: [
- { userId: 1, username: "sanu", inGameName: null, role: "OWNER" },
- { userId: 2, username: "Jolt", inGameName: null, role: "MEMBER" },
- ],
+ loaderData = {
+ team: {
+ id: 10,
+ name: "low ink buddies",
+ team: undefined,
+ pickupAvatarUrl: null,
+ avatarImgId: null,
+ members: [
+ { userId: 1, username: "sanu", inGameName: null, role: "OWNER" },
+ { userId: 2, username: "Jolt", inGameName: null, role: "MEMBER" },
+ ],
+ },
};
const screen = await renderPage();
@@ -90,7 +131,7 @@ describe("tournament admin registration - captain field", () => {
await screen.getByRole("button", { name: "Submit" }).click();
// the shown captain IS a current player, so "the captain must be one of the players" must not block the submit
- await expect.poll(() => submitMock.mock.calls.length).toBe(1);
+ await expect.poll(() => actionCallCount).toBe(1);
await expect
.element(screen.getByText(CAPTAIN_NOT_A_MEMBER_ERROR))
.not.toBeInTheDocument();
@@ -99,36 +140,41 @@ describe("tournament admin registration - captain field", () => {
describe("tournament admin registration - tournament name field", () => {
beforeEach(() => {
- mockLoaderData.team = {
- id: 10,
- name: "low ink buddies",
- team: undefined,
- pickupAvatarUrl: null,
- avatarImgId: null,
- members: [
- {
- userId: 1,
- username: "sanu",
- inGameName: null,
- tournamentName: "Sanu",
- role: "OWNER",
- },
- ],
+ loaderData = {
+ team: {
+ id: 10,
+ name: "low ink buddies",
+ team: undefined,
+ pickupAvatarUrl: null,
+ avatarImgId: null,
+ members: [
+ {
+ userId: 1,
+ username: "sanu",
+ inGameName: null,
+ tournamentName: "Sanu",
+ role: "OWNER",
+ },
+ ],
+ },
};
});
test("is not shown to organizers who can't edit tournament names", async () => {
- mockTournament.canEditTournamentNames = () => false;
+ tournament.canEditTournamentNames = () => false;
const screen = await renderPage();
+ await expect
+ .element(screen.getByRole("button", { name: "Submit" }))
+ .toBeInTheDocument();
await expect
.element(screen.getByLabelText("Tournament name"))
.not.toBeInTheDocument();
});
test("shows the player's current tournament name", async () => {
- mockTournament.canEditTournamentNames = () => true;
+ tournament.canEditTournamentNames = () => true;
const screen = await renderPage();
diff --git a/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.captain-label.browser.test.tsx b/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.captain-label.browser.test.tsx
index 535b8e963..a53386020 100644
--- a/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.captain-label.browser.test.tsx
+++ b/app/features/tournament-admin/routes/to.$id.admin.registration.$tid.captain-label.browser.test.tsx
@@ -3,28 +3,11 @@ import { createMemoryRouter, RouterProvider } from "react-router";
import { describe, expect, test, vi } from "vitest";
import { userEvent } from "vitest/browser";
import { render } from "vitest-browser-react";
+import { TournamentProvider } from "~/features/tournament/tournament-context";
+import type { Tournament } from "~/features/tournament-bracket/core/Tournament";
import TournamentAdminRegistrationPage from "./to.$id.admin.registration.$tid";
-const { mockTournament } = vi.hoisted(() => ({
- mockTournament: {
- ctx: { id: 1, settings: { requireInGameNames: false } },
- canEditTournamentNames: () => false,
- },
-}));
-
-vi.mock("react-router", async () => {
- const actual = await vi.importActual("react-router");
- return {
- ...actual,
- // no team -> "add new team" flow, where the roster is built via user search
- useLoaderData: () => ({ team: null }),
- };
-});
-
-vi.mock("~/features/tournament/tournament-context", () => ({
- useTournament: () => mockTournament,
-}));
-
+// stubbed so importing the route in a browser test doesn't pull in the database-backed action
vi.mock(
"~/features/tournament-admin/actions/to.$id.admin.registration.server",
() => ({ action: vi.fn() }),
@@ -35,6 +18,16 @@ vi.mock(
() => ({ loader: vi.fn() }),
);
+const ROUTE_ID = "registration";
+
+const tournament = {
+ ctx: { id: 1, settings: { requireInGameNames: false } },
+ canEditTournamentNames: (): boolean => false,
+} as unknown as Tournament;
+
+// no team -> "add new team" flow, where the roster is built via user search
+const LOADER_DATA = { team: null };
+
const GREY = {
type: "user" as const,
id: 5,
@@ -51,8 +44,14 @@ function renderPage() {
const router = createMemoryRouter(
[
{
+ id: ROUTE_ID,
path: "/",
- element: ,
+ element: (
+
+
+
+ ),
+ loader: () => LOADER_DATA,
action: () => null,
},
{
@@ -68,7 +67,10 @@ function renderPage() {
},
},
],
- { initialEntries: ["/"] },
+ {
+ initialEntries: ["/"],
+ hydrationData: { loaderData: { [ROUTE_ID]: LOADER_DATA } },
+ },
);
return render();