From daa099a120f0804fb7f9baa1f908add55019ba8d Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sat, 2 May 2026 15:57:43 +0300 Subject: [PATCH] Fix E2E tests --- .claude/skills/e2e/SKILL.md | 6 +- app/components/elements/Button.tsx | 3 + .../match-page/MatchActionPickBanTab.tsx | 3 + app/components/match-page/MatchActionTab.tsx | 8 + app/components/match-page/MatchBanner.tsx | 11 +- .../match-page/MatchBannerBottomRow.tsx | 17 +- .../match-page/MatchBannerTopRow.tsx | 7 +- app/components/match-page/MatchJoinTab.tsx | 20 +- app/components/match-page/MatchRosterTab.tsx | 5 +- .../components/TournamentMatchActionTab.tsx | 1 + .../components/TournamentMatchAdminTab.tsx | 3 +- .../components/TournamentMatchBanner.tsx | 1 + .../components/TournamentMatchTabs.tsx | 11 +- .../loaders/to.$id.matches.$mid.server.ts | 2 +- .../routes/to.$id.matches.$mid.tsx | 1 + docs/dev/forms.md | 8 +- e2e/analyzer.spec.ts | 4 +- e2e/api-public.spec.ts | 8 +- e2e/art.spec.ts | 8 +- e2e/associations.spec.ts | 4 +- e2e/badges.spec.ts | 6 +- e2e/ban.spec.ts | 4 +- e2e/builds.spec.ts | 10 +- e2e/calendar.spec.ts | 4 +- e2e/comp-analyzer.spec.ts | 2 +- e2e/events.spec.ts | 8 +- e2e/friends.spec.ts | 4 +- e2e/global-search.spec.ts | 8 +- e2e/global-setup.ts | 2 +- {app/utils => e2e/helpers}/playwright-form.ts | 0 {app/utils => e2e/helpers}/playwright.ts | 2 +- e2e/helpers/tournament-match.ts | 108 ++++ e2e/lfg.spec.ts | 4 +- e2e/navigation.spec.ts | 8 +- e2e/object-damage-calculator.spec.ts | 2 +- e2e/org.spec.ts | 14 +- e2e/scrims.spec.ts | 6 +- e2e/seeds/db-seed-AB_RR.sqlite3 | Bin 7151616 -> 7757824 bytes e2e/seeds/db-seed-DEFAULT.sqlite3 | Bin 7147520 -> 7737344 bytes e2e/seeds/db-seed-FINALIZED_BRACKET.sqlite3 | Bin 7131136 -> 7766016 bytes e2e/seeds/db-seed-IN_SQ_MATCH.sqlite3 | Bin 7147520 -> 7753728 bytes e2e/seeds/db-seed-NO_SCRIMS.sqlite3 | Bin 7151616 -> 7745536 bytes e2e/seeds/db-seed-NO_SQ_GROUPS.sqlite3 | Bin 7127040 -> 7737344 bytes e2e/seeds/db-seed-NO_TOURNAMENT_TEAMS.sqlite3 | Bin 7081984 -> 7696384 bytes e2e/seeds/db-seed-NZAP_IN_TEAM.sqlite3 | Bin 7151616 -> 7753728 bytes e2e/seeds/db-seed-REG_OPEN.sqlite3 | Bin 7114752 -> 7716864 bytes e2e/seeds/db-seed-SMALL_SOS.sqlite3 | Bin 7127040 -> 7733248 bytes e2e/seeds/db-seed-TEAM_MAP_PREFS.sqlite3 | Bin 7004160 -> 7602176 bytes e2e/sendouq-match.spec.ts | 14 +- e2e/sendouq.spec.ts | 14 +- e2e/settings.spec.ts | 16 +- e2e/team.spec.ts | 6 +- e2e/tier-list-maker.spec.ts | 2 +- e2e/top-search.spec.ts | 2 +- e2e/tournament-ab-divisions.spec.ts | 4 +- e2e/tournament-bracket.spec.ts | 531 ++++++------------ e2e/tournament-staff.spec.ts | 12 +- e2e/tournament-streams.spec.ts | 66 +-- e2e/tournament-tiers.spec.ts | 4 +- e2e/tournament.spec.ts | 12 +- e2e/user-page.spec.ts | 6 +- e2e/vods.spec.ts | 6 +- 62 files changed, 520 insertions(+), 498 deletions(-) rename {app/utils => e2e/helpers}/playwright-form.ts (100%) rename {app/utils => e2e/helpers}/playwright.ts (98%) create mode 100644 e2e/helpers/tournament-match.ts diff --git a/.claude/skills/e2e/SKILL.md b/.claude/skills/e2e/SKILL.md index ed87ec4c6..4afe22829 100644 --- a/.claude/skills/e2e/SKILL.md +++ b/.claude/skills/e2e/SKILL.md @@ -82,10 +82,10 @@ pnpm exec playwright show-trace test-results//trace.zip ## Test pattern reference -Every test follows this pattern — use these imports from `~/utils/playwright`, NOT raw Playwright APIs: +Every test follows this pattern — use these imports from `./helpers/playwright`, NOT raw Playwright APIs: ```typescript -import { expect, impersonate, navigate, seed, test } from "~/utils/playwright"; +import { expect, impersonate, navigate, seed, test } from "./helpers/playwright"; test.describe("Feature", () => { test("does something", async ({ page }) => { @@ -104,7 +104,7 @@ Key rules: - Use `seed(page, variation?)` to reset the database. Available variations: DEFAULT, NO_TOURNAMENT_TEAMS, REG_OPEN, SMALL_SOS, NZAP_IN_TEAM, NO_SCRIMS, NO_SQ_GROUPS - Use `impersonate(page, userId?)` to authenticate. Default is admin (ADMIN_ID) - Avoid `page.waitForTimeout` — use assertions or `waitFor` patterns instead -- Import `test` from `~/utils/playwright` (not from `@playwright/test`) — it includes worker port fixtures +- Import `test` from `./helpers/playwright` (not from `@playwright/test`) — it includes worker port fixtures ## Environment variables diff --git a/app/components/elements/Button.tsx b/app/components/elements/Button.tsx index 56fbdb9a1..1744c06bd 100644 --- a/app/components/elements/Button.tsx +++ b/app/components/elements/Button.tsx @@ -28,6 +28,7 @@ export interface SendouButtonProps shape?: "circle" | "square"; icon?: JSX.Element; children?: React.ReactNode; + testId?: string; } export function SendouButton({ @@ -37,10 +38,12 @@ export function SendouButton({ shape, className, icon, + testId, ...rest }: SendouButtonProps) { return ( diff --git a/app/components/match-page/MatchActionPickBanTab.tsx b/app/components/match-page/MatchActionPickBanTab.tsx index 32f1116ce..8ac69a0c7 100644 --- a/app/components/match-page/MatchActionPickBanTab.tsx +++ b/app/components/match-page/MatchActionPickBanTab.tsx @@ -148,6 +148,7 @@ export function MatchActionPickBanTab({ if (!selected) return; onSubmit?.({ type, map: selected }); }} + testId="pick-ban-submit-button" > {t("common:actions.submit")} @@ -294,6 +295,7 @@ function StageTile({ }} onClick={onSelect} disabled={disabled} + data-testid="pick-ban-button" /> {isSelected ? ( type === "PICK" ? ( @@ -353,6 +355,7 @@ function ModeTile({ })} onClick={onSelect} disabled={disabled} + data-testid="pick-ban-button" > diff --git a/app/components/match-page/MatchActionTab.tsx b/app/components/match-page/MatchActionTab.tsx index 1930e88f9..60719b9a5 100644 --- a/app/components/match-page/MatchActionTab.tsx +++ b/app/components/match-page/MatchActionTab.tsx @@ -134,6 +134,7 @@ export function MatchActionTab({ isOwnTeam={teams[0].id === ownTeamId} hideLabel={ownTeamId == null} className={styles.alpha} + testId="winner-radio-1" /> @@ -160,6 +162,7 @@ export function MatchActionTab({ type="checkbox" checked={isKo} onChange={(e) => setIsKo(e.target.checked)} + data-testid="ko-checkbox" /> {t("q:match.action.ko")} @@ -178,6 +181,7 @@ export function MatchActionTab({ } }} className={styles.submit} + testId="report-score-button" > {t("common:actions.submit")} @@ -248,6 +252,7 @@ function SetEndingConfirmation({ variant="primary" isDisabled={isSubmitting} onPress={onConfirm} + testId="confirm-set-end-button" > {t("common:actions.confirm")} @@ -264,11 +269,13 @@ function TeamRadioOption({ isOwnTeam, hideLabel, className, + testId, }: { team: ActionTabTeam; isOwnTeam: boolean; hideLabel?: boolean; className?: string; + testId?: string; }) { const { t } = useTranslation(["q"]); @@ -279,6 +286,7 @@ function TeamRadioOption({ value={String(team.id)} aria-label={team.name} className={clsx(styles.teamRadioContainer, className)} + data-testid={testId} > {({ isSelected, isFocusVisible }) => (
@@ -73,6 +74,7 @@ interface IconBannerProps { subtitle?: string; screenLegal?: boolean; topRight?: React.ReactNode; + testId?: string; } export function IconBanner({ @@ -81,9 +83,10 @@ export function IconBanner({ subtitle, screenLegal, topRight, + testId, }: IconBannerProps) { return ( -
+
{icon}
{header}
{subtitle ? ( @@ -109,7 +112,11 @@ function ScreenNotice({ screenLegal }: { screenLegal: boolean }) { return ( + ) { if (allSameMode) { return (
-
+
×{games.length}
@@ -49,11 +52,19 @@ function ModeProgress({ games }: Pick) {
{games.map((game, i) => game.mode ? ( -
+
) : ( -
+
), diff --git a/app/components/match-page/MatchBannerTopRow.tsx b/app/components/match-page/MatchBannerTopRow.tsx index 3d33921b9..ac7c4db43 100644 --- a/app/components/match-page/MatchBannerTopRow.tsx +++ b/app/components/match-page/MatchBannerTopRow.tsx @@ -33,7 +33,10 @@ function Score({ score }: { score: MatchBannerTopRowProps["score"] }) {
{score.alpha}-{score.bravo}
-
+
{score.isFinal ? t("q:match.banner.final") : score.bestOf @@ -73,7 +76,7 @@ function Timer({ : minuteFormatter.format(minutes); return ( -
+
diff --git a/app/components/match-page/MatchJoinTab.tsx b/app/components/match-page/MatchJoinTab.tsx index 4d0d984bf..c6c1ad01e 100644 --- a/app/components/match-page/MatchJoinTab.tsx +++ b/app/components/match-page/MatchJoinTab.tsx @@ -86,7 +86,11 @@ export function MatchJoinTab({ ) : null} - +
@@ -122,11 +126,21 @@ function StaleRoomPrompt({ ); } -function InfoWithHeader({ header, value }: { header: string; value: string }) { +function InfoWithHeader({ + header, + value, + testId, +}: { + header: string; + value: string; + testId?: string; +}) { return (
{header}
-
{value}
+
+ {value} +
); } diff --git a/app/components/match-page/MatchRosterTab.tsx b/app/components/match-page/MatchRosterTab.tsx index bd47d9d59..956f55fb9 100644 --- a/app/components/match-page/MatchRosterTab.tsx +++ b/app/components/match-page/MatchRosterTab.tsx @@ -138,13 +138,14 @@ function TeamRoster({ {team.members.length > 0 ? (