Skip to content

Tournament organization · E2E tests

2 modules · 2 exports · 1 repository · 5 e2e tests · 4 routes

  • NN-ZAP
  • SSendou
  • Users a test creates itself are coloured per test
  • Arrange data setup
  • Act what the user does
  • Assert what is expected

Tournament Organization

can create a new organization

Renders//org/new/org/:slug

Setup
Arrange
  1. Create user null roles “TOURNAMENT_ORGANIZER” → organizer
    const organizer = await factories.UserFactory.create(null, {
    	roles: ["TOURNAMENT_ORGANIZER"],
    });
Oorganizer
Act
  1. Go to “/”
    await navigate({ page, url: "/" });
Anything adder
Act
  1. Add “organization”
    await new AnythingAdder(page).add("organization");
New organization page
Act
  1. Fill name with “Test Organization”
    await newOrganization.form.fill("name", "Test Organization");
  2. Save
    await newOrganization.save();
Organization page
Assert
  1. edit button is visible
    await expect(organization.locators.editButton).toBeVisible();
Source
test("can create a new organization", async ({ page, factories }) => {
	const organizer = await factories.UserFactory.create(null, {
		roles: ["TOURNAMENT_ORGANIZER"],
	});

	await impersonate(page, organizer.id);
	await navigate({ page, url: "/" });

	await new AnythingAdder(page).add("organization");

	const newOrganization = new NewOrganizationPage(page);
	await newOrganization.form.fill("name", "Test Organization");
	await newOrganization.save();

	const organization = new OrganizationPage(page);
	await expect(organization.locators.editButton).toBeVisible();
})

user can be promoted to admin gaining org controls and can edit tournaments

N-ZAPSendouorg.spec.ts:43

Renders/calendar/new/org/:slug/org/:slug/edit/to/:id/to/:id/admin/to/:id/admin/to/:id/info

Setup
Arrange
  1. Create tournament organization owner Sendou name “Sendou.ink” members user N-ZAP role “MEMBER” → org
    const org = await factories.TournamentOrganizationFactory.create(
    	{ ownerId: ADMIN_ID, name: ORGANIZATION_NAME },
    	{ members: [{ userId: NZAP_TEST_ID, role: "MEMBER" }] },
    );
  2. Create tournament author Sendou name “PICNIC #2” organization id org.id start times dateToDatabaseTimestamp(addHours(new Date(), 2)) → tournament
    const tournament = await factories.TournamentFactory.create({
    	authorId: ADMIN_ID,
    	name: TOURNAMENT_NAME,
    	organizationId: org.id,
    	startTimes: [dateToDatabaseTimestamp(addHours(new Date(), 2))],
    });
NN-ZAP
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
Assert
  1. edit button is not visible
    await isNotVisible(organization.locators.editButton);
SSendou
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
  2. Open edit → organizationEdit
    const organizationEdit = await organization.openEdit();
Organization edit page
Act
  1. Set member role “N-ZAP”, “ADMIN”
    await organizationEdit.setMemberRole("N-ZAP", "ADMIN");
  2. Save
    await organizationEdit.save();
Organization page
Act
  1. Establish the organization so its admins can edit tournament event info

    Open the organization page org.slug
    await organization.goto(org.slug);
  2. Establish
    await organization.establish();
NN-ZAP
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
  2. Open edit
    await organization.openEdit();
Organization edit page
Assert
  1. title is visible
    await expect(organizationEdit.locators.title).toBeVisible();
Tournament page
Act
  1. Open the tournament page tournament.id
    await tournamentPage.goto(tournament.id);
  2. Open admin → tournamentAdmin
    const tournamentAdmin = await tournamentPage.nav.openAdmin();
  3. Edit event info → calendarEvent
    const calendarEvent = await tournamentAdmin.editEventInfo();
Assert
  1. calendarEvent.locators.nameInput has value “PICNIC #2”
    await expect(calendarEvent.locators.nameInput).toHaveValue(TOURNAMENT_NAME);
Source
test("user can be promoted to admin gaining org controls and can edit tournaments", async ({
	page,
	factories,
}) => {
	const org = await factories.TournamentOrganizationFactory.create(
		{ ownerId: ADMIN_ID, name: ORGANIZATION_NAME },
		{ members: [{ userId: NZAP_TEST_ID, role: "MEMBER" }] },
	);
	const tournament = await factories.TournamentFactory.create({
		authorId: ADMIN_ID,
		name: TOURNAMENT_NAME,
		organizationId: org.id,
		startTimes: [dateToDatabaseTimestamp(addHours(new Date(), 2))],
	});

	const organization = new OrganizationPage(page);

	await impersonate(page, NZAP_TEST_ID);
	await organization.goto(org.slug);
	await isNotVisible(organization.locators.editButton);

	await impersonate(page, ADMIN_ID);
	await organization.goto(org.slug);
	const organizationEdit = await organization.openEdit();
	await organizationEdit.setMemberRole("N-ZAP", "ADMIN");
	await organizationEdit.save();

	// Establish the organization so its admins can edit tournament event info
	await organization.goto(org.slug);
	await organization.establish();

	await impersonate(page, NZAP_TEST_ID);
	await organization.goto(org.slug);
	await organization.openEdit();
	await expect(organizationEdit.locators.title).toBeVisible();

	const tournamentPage = new TournamentPage(page);
	await tournamentPage.goto(tournament.id);

	const tournamentAdmin = await tournamentPage.nav.openAdmin();
	const calendarEvent = await tournamentAdmin.editEventInfo();
	await expect(calendarEvent.locators.nameInput).toHaveValue(TOURNAMENT_NAME);
})

banned player cannot join a tournament of that organization

Sendouplayerorg.spec.ts:87

Renders/org/:slug/to/:id/to/:id/info/to/:id/register

LOLogged out
Act
  1. a long flow: runs past two thirds of the default timeout under load

    Slow
    test.slow();
Arrange
  1. Create user discord name “Banned Bob” → player
    const player = await factories.UserFactory.create({
    	discordName: PLAYER_NAME,
    });
  2. Create tournament organization owner Sendou name “Sendou.ink” → org
    const org = await factories.TournamentOrganizationFactory.create({
    	ownerId: ADMIN_ID,
    	name: ORGANIZATION_NAME,
    });
  3. Create tournament author Sendou name “PICNIC #2” organization id org.id start times dateToDatabaseTimestamp(addHours(new Date(), 2)) → tournament
    const tournament = await factories.TournamentFactory.create({
    	authorId: ADMIN_ID,
    	name: TOURNAMENT_NAME,
    	organizationId: org.id,
    	startTimes: [dateToDatabaseTimestamp(addHours(new Date(), 2))],
    });
SSendou
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
  2. Open banned users
    await organization.openBannedUsers();
  3. Open ban modal → banModal
    const banModal = await organization.openBanModal();
Ban user dialog
Assert
  1. dialog is visible
    await expect(banModal.locators.dialog).toBeVisible();
Act
  1. Select user “Banned Bob”
    await banModal.selectUser(PLAYER_NAME);
  2. Fill Private note with “Test reason”
    await banModal.form.fill("privateNote", "Test reason");
  3. a future expiration date to avoid validation issues

    Set Ban expiration date to addHours(new Date(), 24)
    await banModal.form.setDateTime("expiresAt", addHours(new Date(), 24));
  4. Save
    await banModal.save();
Organization page
Assert
  1. banned users table contains “Test reason”
    await expect(organization.locators.bannedUsersTable).toContainText(
    	"Test reason",
    );
Pplayer
Tournament page
Act
  1. Open the tournament page tournament.id
    await tournamentPage.goto(tournament.id);
  2. Register → register
    const register = await tournamentPage.register();
Tournament register page
Act
  1. Fill Pick-up name with “Banned Team”
    await register.form.fill("pickUpName", "Banned Team");
  2. Submit
    await waitForPOSTResponse(page, () => register.form.submit());
Assert
  1. "Fill roster" only appears after a successful registration

    fill roster heading is not visible
    await expect(register.locators.fillRosterHeading).not.toBeVisible();
SSendou
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
  2. Open banned users
    await organization.openBannedUsers();
  3. Unban
    await organization.unban();
Pplayer
Tournament page
Act
  1. Open the tournament page tournament.id
    await tournamentPage.goto(tournament.id);
  2. Register → registerAgain
    const registerAgain = await tournamentPage.register();
Assert
  1. teamsTab = teams tab
    const teamsTab = tournamentPage.nav.locators.teamsTab;
  2. teamsTab contains “Teams (0)”
    await expect(teamsTab).toContainText("Teams (0)");
Tournament register page
Act
  1. Fill Pick-up name with “Unbanned Team”
    await registerAgain.form.fill("pickUpName", "Unbanned Team");
  2. Submit
    await registerAgain.form.submit();
Assert
  1. teamsTab contains “Teams (1)”
    await expect(teamsTab).toContainText("Teams (1)");
SSendou
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
  2. Open banned users
    await organization.openBannedUsers();
  3. Open ban modal → permanentBanModal
    const permanentBanModal = await organization.openBanModal();
Ban user dialog
Assert
  1. dialog is visible
    await expect(permanentBanModal.locators.dialog).toBeVisible();
Act
  1. Select user “Banned Bob”
    await permanentBanModal.selectUser(PLAYER_NAME);
  2. Fill Private note with “Does not expire”
    await permanentBanModal.form.fill("privateNote", "Does not expire");
  3. expiresAt left empty makes the ban permanent

    Save
    await permanentBanModal.save();
Organization page
Assert
  1. banned users table contains “Permanent”
    await expect(organization.locators.bannedUsersTable).toContainText(
    	"Permanent",
    );
Source
test("banned player cannot join a tournament of that organization", async ({
	page,
	factories,
}) => {
	// a long flow: runs past two thirds of the default timeout under load
	test.slow();
	const player = await factories.UserFactory.create({
		discordName: PLAYER_NAME,
	});
	const org = await factories.TournamentOrganizationFactory.create({
		ownerId: ADMIN_ID,
		name: ORGANIZATION_NAME,
	});
	const tournament = await factories.TournamentFactory.create({
		authorId: ADMIN_ID,
		name: TOURNAMENT_NAME,
		organizationId: org.id,
		startTimes: [dateToDatabaseTimestamp(addHours(new Date(), 2))],
	});

	const organization = new OrganizationPage(page);
	const tournamentPage = new TournamentPage(page);

	await impersonate(page, ADMIN_ID);
	await organization.goto(org.slug);
	await organization.openBannedUsers();

	const banModal = await organization.openBanModal();
	await expect(banModal.locators.dialog).toBeVisible();
	await banModal.selectUser(PLAYER_NAME);
	await banModal.form.fill("privateNote", "Test reason");
	// a future expiration date to avoid validation issues
	await banModal.form.setDateTime("expiresAt", addHours(new Date(), 24));
	await banModal.save();

	await expect(organization.locators.bannedUsersTable).toContainText(
		"Test reason",
	);

	await impersonate(page, player.id);
	await tournamentPage.goto(tournament.id);

	const register = await tournamentPage.register();
	await register.form.fill("pickUpName", "Banned Team");
	await waitForPOSTResponse(page, () => register.form.submit());

	// "Fill roster" only appears after a successful registration
	await expect(register.locators.fillRosterHeading).not.toBeVisible();

	await impersonate(page, ADMIN_ID);
	await organization.goto(org.slug);
	await organization.openBannedUsers();
	await organization.unban();

	await impersonate(page, player.id);
	await tournamentPage.goto(tournament.id);
	const registerAgain = await tournamentPage.register();

	const teamsTab = tournamentPage.nav.locators.teamsTab;
	await expect(teamsTab).toContainText("Teams (0)");

	await registerAgain.form.fill("pickUpName", "Unbanned Team");
	await registerAgain.form.submit();

	await expect(teamsTab).toContainText("Teams (1)");

	await impersonate(page, ADMIN_ID);
	await organization.goto(org.slug);
	await organization.openBannedUsers();

	const permanentBanModal = await organization.openBanModal();
	await expect(permanentBanModal.locators.dialog).toBeVisible();
	await permanentBanModal.selectUser(PLAYER_NAME);
	await permanentBanModal.form.fill("privateNote", "Does not expire");
	// expiresAt left empty makes the ban permanent
	await permanentBanModal.save();

	await expect(organization.locators.bannedUsersTable).toContainText(
		"Permanent",
	);
})

shows org admin the stats counting a played tournament's participants

N-ZAPSendouorg.spec.ts:169

Renders/org/:slug/org/:slug/stats

Setup
Arrange
  1. PLAYER_COUNT = 8
    const PLAYER_COUNT = 8;
  2. Create tournament organization owner Sendou name “Sendou.ink” → org
    const org = await factories.TournamentOrganizationFactory.create({
    	ownerId: ADMIN_ID,
    	name: ORGANIZATION_NAME,
    });
  3. Create PLAYER_COUNT - 1 × user → players
    const players = await factories.UserFactory.createMany(PLAYER_COUNT - 1);
  4. playerIds = [ADMIN_ID, ...players.map((player) => player.id)]
    const playerIds = [ADMIN_ID, ...players.map((player) => player.id)];
  5. teamRosters = [playerIds.slice(0, 4), playerIds.slice(4, 8)]
    const teamRosters = [playerIds.slice(0, 4), playerIds.slice(4, 8)];
  6. only full months before the current one count towards the stats

    lastMonth = subMonths(new Date(), 1)
    const lastMonth = subMonths(new Date(), 1);
  7. Create played tournament author Sendou name “PICNIC #2” organization id org.id start times dateToDatabaseTimestamp(lastMonth) teamRosters
    await factories.TournamentFactory.createPlayed(
    	{
    		authorId: ADMIN_ID,
    		name: TOURNAMENT_NAME,
    		organizationId: org.id,
    		startTimes: [dateToDatabaseTimestamp(lastMonth)],
    	},
    	{ teamRosters },
    );
NN-ZAP
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
Assert
  1. stats button is not visible
    await isNotVisible(organization.locators.statsButton);
SSendou
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
  2. Open stats → stats
    const stats = await organization.openStats();
Assert
  1. expectedAverage = ( PLAYER_COUNT / ESTABLISHED_ORG.MONTHS_CONSIDERED ).toFixed(1)
    const expectedAverage = (
    	PLAYER_COUNT / ESTABLISHED_ORG.MONTHS_CONSIDERED
    ).toFixed(1);
Organization stats page
Assert
  1. established status contains expectedAverage
    await expect(stats.locators.establishedStatus).toContainText(
    	expectedAverage,
    );
  2. established status contains `/ ${ESTABLISHED_ORG.GAIN_THRESHOLD}`
    await expect(stats.locators.establishedStatus).toContainText(
    	`/ ${ESTABLISHED_ORG.GAIN_THRESHOLD}`,
    );
  3. month row lastMonth has “aria-valuenow” = String(PLAYER_COUNT)
    await expect(stats.monthRow(lastMonth)).toHaveAttribute(
    	"aria-valuenow",
    	String(PLAYER_COUNT),
    );
Source
test("shows org admin the stats counting a played tournament's participants", async ({
	page,
	factories,
}) => {
	const PLAYER_COUNT = 8;

	const org = await factories.TournamentOrganizationFactory.create({
		ownerId: ADMIN_ID,
		name: ORGANIZATION_NAME,
	});

	const players = await factories.UserFactory.createMany(PLAYER_COUNT - 1);
	const playerIds = [ADMIN_ID, ...players.map((player) => player.id)];
	const teamRosters = [playerIds.slice(0, 4), playerIds.slice(4, 8)];

	// only full months before the current one count towards the stats
	const lastMonth = subMonths(new Date(), 1);
	await factories.TournamentFactory.createPlayed(
		{
			authorId: ADMIN_ID,
			name: TOURNAMENT_NAME,
			organizationId: org.id,
			startTimes: [dateToDatabaseTimestamp(lastMonth)],
		},
		{ teamRosters },
	);

	const organization = new OrganizationPage(page);

	// a non-member sees no stats button
	await impersonate(page, NZAP_TEST_ID);
	await organization.goto(org.slug);
	await isNotVisible(organization.locators.statsButton);

	await impersonate(page, ADMIN_ID);
	await organization.goto(org.slug);
	const stats = await organization.openStats();

	const expectedAverage = (
		PLAYER_COUNT / ESTABLISHED_ORG.MONTHS_CONSIDERED
	).toFixed(1);
	await expect(stats.locators.establishedStatus).toContainText(
		expectedAverage,
	);
	await expect(stats.locators.establishedStatus).toContainText(
		`/ ${ESTABLISHED_ORG.GAIN_THRESHOLD}`,
	);
	await expect(stats.monthRow(lastMonth)).toHaveAttribute(
		"aria-valuenow",
		String(PLAYER_COUNT),
	);
})

allows member of established org to create tournament

org adminSendouorg.spec.ts:222

Renders/calendar/new/org/:slug

Setup
Arrange
  1. Create user → orgAdmin
    const orgAdmin = await factories.UserFactory.create();
  2. Create tournament organization owner Sendou name “Sendou.ink” members user id orgAdmin.id role “ADMIN” → org
    const org = await factories.TournamentOrganizationFactory.create(
    	{ ownerId: ADMIN_ID, name: ORGANIZATION_NAME },
    	{ members: [{ userId: orgAdmin.id, role: "ADMIN" }] },
    );
OAorg admin
Calendar new event page
Act
  1. Go to new tournament
    await newTournament.gotoNewTournament();
Assert
  1. no tournament permissions alert is visible
    await expect(
    	newTournament.locators.noTournamentPermissionsAlert,
    ).toBeVisible();
  2. new tournament heading is not visible
    await expect(newTournament.locators.newTournamentHeading).not.toBeVisible();
SSendou
Organization page
Act
  1. Open the organization page org.slug
    await organization.goto(org.slug);
  2. Establish
    await organization.establish();
OAorg admin
Calendar new event page
Act
  1. Go to new tournament
    await newTournament.gotoNewTournament();
Assert
  1. no tournament permissions alert is not visible
    await expect(
    	newTournament.locators.noTournamentPermissionsAlert,
    ).not.toBeVisible();
  2. new tournament heading is visible
    await expect(newTournament.locators.newTournamentHeading).toBeVisible();
Source
test("allows member of established org to create tournament", async ({
	page,
	factories,
}) => {
	const orgAdmin = await factories.UserFactory.create();
	const org = await factories.TournamentOrganizationFactory.create(
		{ ownerId: ADMIN_ID, name: ORGANIZATION_NAME },
		{ members: [{ userId: orgAdmin.id, role: "ADMIN" }] },
	);

	const newTournament = new CalendarNewEventPage(page);

	await impersonate(page, orgAdmin.id);
	await newTournament.gotoNewTournament();

	await expect(
		newTournament.locators.noTournamentPermissionsAlert,
	).toBeVisible();
	await expect(newTournament.locators.newTournamentHeading).not.toBeVisible();

	await impersonate(page, ADMIN_ID);
	const organization = new OrganizationPage(page);
	await organization.goto(org.slug);
	await organization.establish();

	await impersonate(page, orgAdmin.id);
	await newTournament.gotoNewTournament();

	await expect(
		newTournament.locators.noTournamentPermissionsAlert,
	).not.toBeVisible();
	await expect(newTournament.locators.newTournamentHeading).toBeVisible();
})