mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-06-21 12:03:31 -05:00
264 lines
7.3 KiB
TypeScript
264 lines
7.3 KiB
TypeScript
import type { Page } from "@playwright/test";
|
|
import {
|
|
clockFormatSchema,
|
|
disableBuildAbilitySortingSchema,
|
|
spoilerFreeModeSchema,
|
|
} from "~/features/settings/settings-schemas";
|
|
import {
|
|
CALENDAR_PAGE,
|
|
SETTINGS_PAGE,
|
|
tournamentBracketsPage,
|
|
tournamentResultsPage,
|
|
} from "~/utils/urls";
|
|
import {
|
|
expect,
|
|
impersonate,
|
|
isNotVisible,
|
|
navigate,
|
|
seed,
|
|
submit,
|
|
test,
|
|
waitForPOSTResponse,
|
|
} from "./helpers/playwright";
|
|
import { createFormHelpers } from "./helpers/playwright-form";
|
|
|
|
test.describe("Settings", () => {
|
|
test("updates 'disableBuildAbilitySorting'", async ({ page }) => {
|
|
await seed(page);
|
|
await impersonate(page);
|
|
|
|
await navigate({
|
|
page,
|
|
url: "/builds/luna-blaster",
|
|
});
|
|
|
|
const oldContents = await page
|
|
.getByTestId("build-card")
|
|
.first()
|
|
.innerHTML();
|
|
|
|
await navigate({
|
|
page,
|
|
url: `${SETTINGS_PAGE}?tab=preferences`,
|
|
});
|
|
|
|
const form = createFormHelpers(page, disableBuildAbilitySortingSchema);
|
|
await waitForPOSTResponse(page, () => form.check("newValue"));
|
|
|
|
await navigate({
|
|
page,
|
|
url: "/builds/luna-blaster",
|
|
});
|
|
|
|
const newContents = await page
|
|
.getByTestId("build-card")
|
|
.first()
|
|
.innerHTML();
|
|
|
|
expect(newContents).not.toBe(oldContents);
|
|
});
|
|
|
|
test("updates clock format preference", async ({ page }) => {
|
|
await seed(page);
|
|
await impersonate(page);
|
|
|
|
await navigate({
|
|
page,
|
|
url: CALENDAR_PAGE,
|
|
});
|
|
|
|
const clockTime = page
|
|
.locator("[class*='clockHeader'] [class*='reserve-one-lb']")
|
|
.first();
|
|
const initialTime = await clockTime.textContent();
|
|
|
|
expect(initialTime).toMatch(/AM|PM/);
|
|
|
|
await navigate({
|
|
page,
|
|
url: `${SETTINGS_PAGE}?tab=locale`,
|
|
});
|
|
|
|
const form = createFormHelpers(page, clockFormatSchema);
|
|
await waitForPOSTResponse(page, () => form.select("newValue", "24h"));
|
|
|
|
await navigate({
|
|
page,
|
|
url: CALENDAR_PAGE,
|
|
});
|
|
|
|
const newTime = await clockTime.textContent();
|
|
|
|
expect(newTime).not.toMatch(/AM|PM/);
|
|
expect(newTime).not.toBe(initialTime);
|
|
expect(newTime).toContain(":");
|
|
});
|
|
});
|
|
|
|
const AVOIDED_MODE_POOL_ERROR =
|
|
"Can't have map pool for a mode that was avoided";
|
|
|
|
const setModePreference = (
|
|
page: Page,
|
|
mode: string,
|
|
preference: "Avoid" | "Neutral" | "Prefer",
|
|
) => {
|
|
const name =
|
|
preference === "Neutral"
|
|
? "Neutral towards the mode"
|
|
: `${preference} the mode`;
|
|
return page
|
|
.getByRole("radiogroup", { name: `Select preference towards ${mode}` })
|
|
.getByRole("radio", { name })
|
|
.click({ force: true });
|
|
};
|
|
|
|
const mapButton = (page: Page, mode: string, stageId: number) =>
|
|
page.getByTestId(`map-pool-${mode}-${stageId}`);
|
|
|
|
const selectModeTab = (page: Page, mode: string) =>
|
|
page.getByTestId(`map-pool-mode-tab-${mode}`).click();
|
|
|
|
const SELECTED_MAP_CLASS = /mapButtonGreyedOut/;
|
|
|
|
// The seeded user already has random map pools, so empty the mode's pool to get
|
|
// a known starting state. Only currently selected (greyed, non-banned) stages
|
|
// are clickable to deselect.
|
|
const clearMapPool = async (page: Page, mode: string) => {
|
|
const picked = page.locator(
|
|
`[data-testid^="map-pool-${mode}-"][class*="mapButtonGreyedOut"]:not([disabled])`,
|
|
);
|
|
for (let count = await picked.count(); count > 0; count--) {
|
|
// the selected-state check icon overlays the button and intercepts clicks
|
|
await picked.first().click({ force: true });
|
|
await expect(picked).toHaveCount(count - 1);
|
|
}
|
|
};
|
|
|
|
test.describe("Match profile map preferences", () => {
|
|
test("retains map selection when toggling a mode prefer -> avoid -> prefer", async ({
|
|
page,
|
|
}) => {
|
|
await seed(page);
|
|
await impersonate(page);
|
|
await navigate({ page, url: SETTINGS_PAGE });
|
|
|
|
await setModePreference(page, "SZ", "Prefer");
|
|
await selectModeTab(page, "SZ");
|
|
await clearMapPool(page, "SZ");
|
|
await mapButton(page, "SZ", 1).click();
|
|
await expect(mapButton(page, "SZ", 1)).toHaveClass(SELECTED_MAP_CLASS);
|
|
|
|
// avoiding hides the picker, but the selection should be remembered
|
|
await setModePreference(page, "SZ", "Avoid");
|
|
await isNotVisible(mapButton(page, "SZ", 1));
|
|
|
|
await setModePreference(page, "SZ", "Prefer");
|
|
await selectModeTab(page, "SZ");
|
|
await expect(mapButton(page, "SZ", 1)).toHaveClass(SELECTED_MAP_CLASS);
|
|
});
|
|
|
|
test("can save 'zones only' after a now-avoided mode previously had a map pool", async ({
|
|
page,
|
|
}) => {
|
|
await seed(page);
|
|
await impersonate(page);
|
|
await navigate({ page, url: SETTINGS_PAGE });
|
|
|
|
// Save a map pool for both SZ and TC (stage 2 is not banned in TC).
|
|
await setModePreference(page, "SZ", "Prefer");
|
|
await selectModeTab(page, "SZ");
|
|
await clearMapPool(page, "SZ");
|
|
await mapButton(page, "SZ", 1).click();
|
|
await setModePreference(page, "TC", "Prefer");
|
|
await selectModeTab(page, "TC");
|
|
await clearMapPool(page, "TC");
|
|
await mapButton(page, "TC", 2).click();
|
|
await submit(page);
|
|
|
|
// Switch to "zones only" by avoiding every mode except SZ, then save.
|
|
await setModePreference(page, "TW", "Avoid");
|
|
await setModePreference(page, "TC", "Avoid");
|
|
await setModePreference(page, "RM", "Avoid");
|
|
await setModePreference(page, "CB", "Avoid");
|
|
await submit(page);
|
|
|
|
// Reload so the form loads the persisted preferences. The previously saved
|
|
// TC pool must not resurface as an invalid "pool for an avoided mode".
|
|
await navigate({ page, url: SETTINGS_PAGE });
|
|
|
|
await submit(page);
|
|
await isNotVisible(page.getByText(AVOIDED_MODE_POOL_ERROR));
|
|
});
|
|
});
|
|
|
|
const enableSpoilerFreeMode = async (page: Page) => {
|
|
await navigate({ page, url: `${SETTINGS_PAGE}?tab=preferences` });
|
|
const form = createFormHelpers(page, spoilerFreeModeSchema);
|
|
await waitForPOSTResponse(page, () => form.check("newValue"));
|
|
};
|
|
|
|
test.describe("Spoiler-free mode", () => {
|
|
const FINALIZED_TOURNAMENT_ID = 7;
|
|
|
|
test("censors bracket and reveals on click", async ({ page }) => {
|
|
await seed(page, "FINALIZED_BRACKET");
|
|
await impersonate(page);
|
|
await enableSpoilerFreeMode(page);
|
|
|
|
await navigate({
|
|
page,
|
|
url: tournamentBracketsPage({ tournamentId: FINALIZED_TOURNAMENT_ID }),
|
|
});
|
|
|
|
// bracket is censored — "Show results" button visible
|
|
const showResultsButton = page.getByRole("button", {
|
|
name: "Show results",
|
|
});
|
|
await expect(showResultsButton).toBeVisible();
|
|
|
|
// later rounds (SF/Finals) show "???" for team names
|
|
await expect(page.getByText("???").first()).toBeVisible();
|
|
|
|
// click "Show results" to reveal
|
|
await showResultsButton.click();
|
|
|
|
// after reveal, "Hide results" button appears
|
|
await expect(
|
|
page.getByRole("button", { name: "Hide results" }),
|
|
).toBeVisible();
|
|
|
|
// "???" no longer present
|
|
await isNotVisible(page.getByText("???"));
|
|
|
|
// navigate to results page — sessionStorage reveal carries over
|
|
await navigate({
|
|
page,
|
|
url: tournamentResultsPage(FINALIZED_TOURNAMENT_ID),
|
|
});
|
|
await expect(page.getByTestId("result-team-name").first()).toBeVisible();
|
|
});
|
|
|
|
test("results page is censored and can be revealed", async ({ page }) => {
|
|
await seed(page, "FINALIZED_BRACKET");
|
|
await impersonate(page);
|
|
await enableSpoilerFreeMode(page);
|
|
|
|
await navigate({
|
|
page,
|
|
url: tournamentResultsPage(FINALIZED_TOURNAMENT_ID),
|
|
});
|
|
|
|
// results are censored
|
|
const showResultsButton = page.getByRole("button", {
|
|
name: "Show results",
|
|
});
|
|
await expect(showResultsButton).toBeVisible();
|
|
await isNotVisible(page.getByTestId("result-team-name"));
|
|
|
|
// reveal
|
|
await showResultsButton.click();
|
|
await expect(page.getByTestId("result-team-name").first()).toBeVisible();
|
|
});
|
|
});
|