sendou.ink/e2e/ban.spec.ts
Kalle fef1ffc955
Design refresh + a bunch of stuff (#2864)
Co-authored-by: hfcRed <hfcred@gmx.net>
2026-03-19 17:51:42 +02:00

137 lines
4.3 KiB
TypeScript

import type { Page } from "@playwright/test";
import { NZAP_TEST_ID } from "~/db/seed/constants";
import { ADMIN_ID } from "~/features/admin/admin-constants";
import {
expect,
impersonate,
navigate,
seed,
test,
waitForPOSTResponse,
} from "~/utils/playwright";
import { ADMIN_PAGE, SUSPENDED_PAGE } from "~/utils/urls";
async function banUser(
page: Page,
options: { duration?: string; reason?: string },
) {
const banForm = page
.locator("form")
.filter({ has: page.locator("h2", { hasText: /^Ban user$/ }) });
const comboboxButton = banForm.getByLabel("User");
await expect(comboboxButton).not.toBeDisabled();
await comboboxButton.click();
const searchInput = page.getByTestId("user-search-input");
await searchInput.fill("N-ZAP");
const option = page.getByTestId("user-search-item").first();
await option.click();
if (options.duration) {
await banForm.locator('input[name="duration"]').fill(options.duration);
}
if (options.reason) {
await banForm.locator('input[name="reason"]').fill(options.reason);
}
await waitForPOSTResponse(page, () =>
banForm.getByRole("button", { name: "Save" }).click(),
);
}
async function unbanUser(page: Page) {
const unbanForm = page
.locator("form")
.filter({ has: page.locator("h2", { hasText: /^Unban user$/ }) });
const comboboxButton = unbanForm.getByLabel("User");
await expect(comboboxButton).not.toBeDisabled();
await comboboxButton.click();
const searchInput = page.getByTestId("user-search-input");
await searchInput.fill("N-ZAP");
const option = page.getByTestId("user-search-item").first();
await option.click();
await waitForPOSTResponse(page, () =>
unbanForm.getByRole("button", { name: "Save" }).click(),
);
}
test.describe("User banning", () => {
test("banned user is redirected to suspended page and cannot access site", async ({
page,
}) => {
await seed(page);
// 1. As admin, ban NZAP user
await impersonate(page, ADMIN_ID);
await navigate({ page, url: ADMIN_PAGE });
await banUser(page, { reason: "Test ban reason" });
// 2. As the banned user, try to access the site
await impersonate(page, NZAP_TEST_ID);
await navigate({ page, url: "/" });
// Should be redirected to suspended page
await expect(page).toHaveURL(SUSPENDED_PAGE);
await expect(page.getByText("Account suspended")).toBeVisible();
await expect(page.getByText("Reason: Test ban reason")).toBeVisible();
await expect(page.getByText("no end time set")).toBeVisible();
// 3. Verify user cannot navigate to other pages
await navigate({ page, url: "/builds" });
await expect(page).toHaveURL(SUSPENDED_PAGE);
await navigate({ page, url: "/calendar" });
await expect(page).toHaveURL(SUSPENDED_PAGE);
// 4. As admin, unban the user
await impersonate(page, ADMIN_ID);
await navigate({ page, url: ADMIN_PAGE });
await unbanUser(page);
// 5. As the unbanned user, verify they can access the site
await impersonate(page, NZAP_TEST_ID);
await navigate({ page, url: "/" });
// Should not be redirected to suspended page
await expect(page).not.toHaveURL(SUSPENDED_PAGE);
});
test("timed ban shows expiration date on suspended page", async ({
page,
}) => {
await seed(page);
// 1. As admin, ban NZAP user with a future duration
await impersonate(page, ADMIN_ID);
await navigate({ page, url: ADMIN_PAGE });
// Set ban to expire tomorrow (well in the future)
const tomorrow = new Date(Date.now() + 24 * 60 * 60 * 1000);
const year = tomorrow.getFullYear();
const month = String(tomorrow.getMonth() + 1).padStart(2, "0");
const day = String(tomorrow.getDate()).padStart(2, "0");
const formattedDate = `${year}-${month}-${day}T12:00`;
await banUser(page, { duration: formattedDate, reason: "Temporary ban" });
// 2. As the banned user, verify redirected to suspended page
await impersonate(page, NZAP_TEST_ID);
await navigate({ page, url: "/" });
await expect(page).toHaveURL(SUSPENDED_PAGE);
await expect(page.getByText("Account suspended")).toBeVisible();
await expect(page.getByText("Reason: Temporary ban")).toBeVisible();
// Should show expiration time, not "no end time set"
await expect(page.getByText("no end time set")).not.toBeVisible();
await expect(page.getByText("Ends:")).toBeVisible();
// Note: The actual time-based expiration logic is tested in unit tests
// (see app/features/ban/core/banned.test.ts)
});
});