sendou.ink/e2e/tournament-admin.spec.ts

196 lines
7.0 KiB
TypeScript

import * as fs from "node:fs/promises";
import { addMinutes, subDays } from "date-fns";
import { NZAP_TEST_ID } from "~/db/seed/constants";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import type { Factories } from "./helpers/factories";
import { expect, impersonate, test } from "./helpers/playwright";
import { TournamentAdminAuditPage } from "./pages/tournament/tournament-admin-audit-page";
import { TournamentAdminPage } from "./pages/tournament/tournament-admin-page";
import { TournamentAdminRegistrationPage } from "./pages/tournament/tournament-admin-registration-page";
const ROSTER_SIZE = 4;
const CAPTAIN_DISCORD_ID = "1234567890123456789";
test.describe("Tournament admin team management", () => {
test("edits a registration, checks a team in and out, unregisters it and records it in the audit log", async ({
page,
factories,
}) => {
const tournament = await createTournament(factories);
const roster = await factories.UserFactory.createMany(ROSTER_SIZE);
const team = await factories.TournamentTeamFactory.create({
tournamentId: tournament.id,
team: pickUpTeam("Original Team"),
memberUserIds: roster.map((user) => user.id),
});
await impersonate(page, NZAP_TEST_ID);
const registration = new TournamentAdminRegistrationPage(page);
await registration.gotoEdit(tournament.id, team.id);
await expect(registration.locators.editHeading).toBeVisible();
await registration.form.fill("pickUpName", "Renamed Team");
await registration.save();
// back on the team list, the rename is reflected
const admin = new TournamentAdminPage(page);
await expect(admin.locators.searchInput).toBeVisible();
await expect(admin.teamName("Renamed Team")).toBeVisible();
await admin.checkTeamIn(0);
await admin.checkTeamOut(0);
await admin.unregisterTeam(0);
await expect(admin.locators.unregisterDialogHeading).toBeVisible();
await admin.confirmUnregister();
const audit = new TournamentAdminAuditPage(page);
await audit.goto(tournament.id);
await expect(audit.eventCell("Team checked in")).toBeVisible();
await expect(audit.eventCell("Team checked out")).toBeVisible();
await expect(audit.eventCell("Team unregistered")).toBeVisible();
});
test("adds a new team and records it in the audit log", async ({
page,
factories,
}) => {
const tournament = await createTournament(factories);
const player = await factories.UserFactory.create({
discordName: "Rostered Riko",
});
await impersonate(page, NZAP_TEST_ID);
const registration = new TournamentAdminRegistrationPage(page);
await registration.gotoNew(tournament.id);
await expect(registration.locators.addHeading).toBeVisible();
await registration.form.fill("pickUpName", "Panda Squad");
await registration.selectPlayer("Rostered Riko");
await registration.selectCaptain(player.id);
await registration.save();
const admin = new TournamentAdminPage(page);
await expect(admin.teamName("Panda Squad")).toBeVisible();
const audit = new TournamentAdminAuditPage(page);
await audit.goto(tournament.id);
await expect(audit.eventCell("Team registered")).toBeVisible();
});
test("imports a roster from another tournament into the registration form", async ({
page,
factories,
}) => {
const tournament = await createTournament(factories);
// the tournament search of the import dialog only finds past tournaments
const pastTournament = await factories.TournamentFactory.create({
name: "Paddling Pool 253",
authorId: NZAP_TEST_ID,
startTimes: [dateToDatabaseTimestamp(subDays(new Date(), 2))],
});
const importedRoster = await factories.UserFactory.createMany(ROSTER_SIZE);
await factories.TournamentTeamFactory.create({
tournamentId: pastTournament.id,
team: pickUpTeam("Imported Legends"),
memberUserIds: importedRoster.map((user) => user.id),
});
await impersonate(page, NZAP_TEST_ID);
const registration = new TournamentAdminRegistrationPage(page);
await registration.gotoNew(tournament.id);
await expect(registration.locators.addHeading).toBeVisible();
await registration.openImportDialog();
await expect(registration.locators.importDialogHeading).toBeVisible();
await registration.importFirstTeamFrom("Paddling Pool");
// the dialog closes and the imported roster's name prefills the form
await expect(registration.locators.importDialogHeading).toHaveCount(0);
await expect(registration.locators.teamNameInput).toHaveValue(
"Imported Legends",
);
});
test("exports the team list", async ({ page, factories }) => {
const tournament = await createTournament(factories);
const roster = await factories.UserFactory.createMany(ROSTER_SIZE);
await factories.TournamentTeamFactory.create({
tournamentId: tournament.id,
team: pickUpTeam("Alpha Squad"),
memberUserIds: roster.map((user) => user.id),
});
await impersonate(page, NZAP_TEST_ID);
const admin = new TournamentAdminPage(page);
await admin.goto(tournament.id);
await admin.openExportDialog();
await expect(admin.locators.exportDialogHeading).toBeVisible();
const download = await admin.downloadExport();
expect(download.suggestedFilename()).toBe("participants.txt");
const content = await fs.readFile(await download.path(), "utf-8");
expect(content).toContain("Alpha Squad");
});
test("filters the team list by name and by captain Discord id", async ({
page,
factories,
}) => {
const tournament = await createTournament(factories);
const captain = await factories.UserFactory.create({
discordId: CAPTAIN_DISCORD_ID,
});
const alphaRest = await factories.UserFactory.createMany(ROSTER_SIZE - 1);
await factories.TournamentTeamFactory.create({
tournamentId: tournament.id,
team: pickUpTeam("Alpha Squad"),
memberUserIds: [captain.id, ...alphaRest.map((user) => user.id)],
});
const bravoRoster = await factories.UserFactory.createMany(ROSTER_SIZE);
await factories.TournamentTeamFactory.create({
tournamentId: tournament.id,
team: pickUpTeam("Bravo Squad"),
memberUserIds: bravoRoster.map((user) => user.id),
});
await impersonate(page, NZAP_TEST_ID);
const admin = new TournamentAdminPage(page);
await admin.goto(tournament.id);
// the captain is on no other team, so their team name + discord id both single it out
await admin.searchTeams("Alpha Squad");
await expect(admin.locators.teamRows).toHaveCount(1);
await expect(admin.locators.teamNames.first()).toHaveText("Alpha Squad");
await admin.searchTeams(CAPTAIN_DISCORD_ID);
await expect(admin.locators.teamRows).toHaveCount(1);
await expect(admin.locators.teamNames.first()).toHaveText("Alpha Squad");
await admin.searchTeams("zzz-no-such-team-zzz");
await expect(admin.locators.noSearchResultsText).toBeVisible();
});
});
/** A tournament whose check-in window is open but that has not started. */
function createTournament(factories: Factories) {
return factories.TournamentFactory.create({
authorId: NZAP_TEST_ID,
startTimes: [dateToDatabaseTimestamp(addMinutes(new Date(), 30))],
});
}
function pickUpTeam(name: string) {
return { name, prefersNotToHost: 0 as const, teamId: null };
}