Tournament A/B divisions assigns 6A/6B, lists the division on the divisions page, starts bracket, renders 36 matches across 6 rounds and two standings tables Renders/to/:id /to/:id/admin /to/:id/admin/seeds /to/:id/brackets
Tournament seeds page Tournament brackets page Arrange
teamCount = TEAMS_PER_DIVISION * 2
const teamCount = TEAMS_PER_DIVISION * 2 ;Create teamCount * ROSTER_SIZE × user → players
const players = await factories.UserFactory .createMany (
teamCount * ROSTER_SIZE ,
);Create tournament author N-ZAP start times dateToDatabaseTimestamp (subMinutes (new Date (), 30 )) map picking style “AUTO” bracket progression type “round_robin” name “Groups stage” requires check in false settings has ab divisions true teams per group teamCount is league true → tournament
const tournament = await factories.TournamentFactory .create (
{
authorId: NZAP_TEST_ID ,
startTimes: [dateToDatabaseTimestamp (subMinutes (new Date (), 30 ))],
mapPickingStyle: "AUTO" ,
bracketProgression: [
{
type : "round_robin" ,
name: "Groups stage" ,
requiresCheckIn: false ,
settings: { hasAbDivisions: true , teamsPerGroup: teamCount },
},
],
},
{ isLeague: true },
);Repeat for (let i = 0 ; i < teamCount; i++)
for (let i = 0 ; i < teamCount; i++) {
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: players
.slice (i * ROSTER_SIZE , (i + 1 ) * ROSTER_SIZE )
.map ((player) => player.id),
},
{ isCheckedIn: true },
);
}Create tournament team tournament id tournament.id member user ids players .slice (i * ROSTER_SIZE , (i + 1 ) * ROSTER_SIZE ) .map ((player) => playe… is checked in true
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: players
.slice (i * ROSTER_SIZE , (i + 1 ) * ROSTER_SIZE )
.map ((player) => player.id),
},
{ isCheckedIn: true },
);Tournament seeds page Act
Open the tournament seeds page tournament.id
await seeds.goto (tournament.id);Open ab divisions dialog
await seeds.openAbDivisionsDialog ();Assert
ab division radio groups count is teamCount
await expect (seeds.locators.abDivisionRadioGroups).toHaveCount (teamCount);Act
Repeat for (let i = 0 ; i < TEAMS_PER_DIVISION ; i++)
for (let i = 0 ; i < TEAMS_PER_DIVISION ; i++) {
await seeds.assignAbDivision (i, "A" );
}Assign ab division i, “A”
await seeds.assignAbDivision (i, "A" );Repeat for (let i = TEAMS_PER_DIVISION ; i < teamCount; i++)
for (let i = TEAMS_PER_DIVISION ; i < teamCount; i++) {
await seeds.assignAbDivision (i, "B" );
}Assign ab division i, “B”
await seeds.assignAbDivision (i, "B" );Save ab divisions
await seeds.saveAbDivisions ();Tournament brackets page Act
Open the tournament brackets page tournament.id
await brackets.goto (tournament.id);Finalize
await brackets.finalize ();Assert
brackets viewer is visible
await expect (brackets.locators.bracketsViewer).toBeVisible ();matches count is TEAMS_PER_DIVISION * TEAMS_PER_DIVISION
await expect (brackets.locators.matches).toHaveCount (
TEAMS_PER_DIVISION * TEAMS_PER_DIVISION ,
);Repeat for ( let roundNumber = 1 ; roundNumber <= TEAMS_PER_DIVISION ; roundNumber++ )
for (
let roundNumber = 1 ;
roundNumber <= TEAMS_PER_DIVISION ;
roundNumber++
) {
await expect (brackets.roundLabel (roundNumber).first ()).toBeVisible ();
}first round label roundNumber is visible
await expect (brackets.roundLabel (roundNumber).first ()).toBeVisible ();rr standings tables count is 2
await expect (brackets.locators.rrStandingsTables).toHaveCount (2 );Source test ("assigns 6A/6B, lists the division on the divisions page, starts bracket, renders 36 matches across 6 rounds and two standings tables" , async ({
page,
factories,
}) => {
test.slow ();
const teamCount = TEAMS_PER_DIVISION * 2 ;
const players = await factories.UserFactory .createMany (
teamCount * ROSTER_SIZE ,
);
const tournament = await factories.TournamentFactory .create (
{
authorId: NZAP_TEST_ID ,
startTimes: [dateToDatabaseTimestamp (subMinutes (new Date (), 30 ))],
mapPickingStyle: "AUTO" ,
bracketProgression: [
{
type : "round_robin" ,
name: "Groups stage" ,
requiresCheckIn: false ,
settings: { hasAbDivisions: true , teamsPerGroup: teamCount },
},
],
},
{ isLeague: true },
);
for (let i = 0 ; i < teamCount; i++) {
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: players
.slice (i * ROSTER_SIZE , (i + 1 ) * ROSTER_SIZE )
.map ((player) => player.id),
},
{ isCheckedIn: true },
);
}
await impersonate (page, NZAP_TEST_ID );
const seeds = new TournamentSeedsPage (page);
await seeds.goto (tournament.id);
await seeds.openAbDivisionsDialog ();
await expect (seeds.locators.abDivisionRadioGroups).toHaveCount (teamCount);
for (let i = 0 ; i < TEAMS_PER_DIVISION ; i++) {
await seeds.assignAbDivision (i, "A" );
}
for (let i = TEAMS_PER_DIVISION ; i < teamCount; i++) {
await seeds.assignAbDivision (i, "B" );
}
await seeds.saveAbDivisions ();
// a one-division league has no divisions page, its brackets page is the one
const brackets = new TournamentBracketsPage (page);
await brackets.goto (tournament.id);
await brackets.finalize ();
await expect (brackets.locators.bracketsViewer).toBeVisible ();
await expect (brackets.locators.matches).toHaveCount (
TEAMS_PER_DIVISION * TEAMS_PER_DIVISION ,
);
for (
let roundNumber = 1 ;
roundNumber <= TEAMS_PER_DIVISION ;
roundNumber++
) {
await expect (brackets.roundLabel (roundNumber).first ()).toBeVisible ();
}
await expect (brackets.locators.rrStandingsTables).toHaveCount (2 );
})Invitational tournament team can't register on their own, the TO adds them instead Renders/to/:id /to/:id/admin /to/:id/admin /to/:id/admin/registration/:tid? /to/:id/register /to/:id/teams
Tournament register page Tournament admin registration page Tournament admin page Tournament teams page Tournament register page Arrange
Create invitational → tournament
const tournament = await createInvitational (factories);Create user discord name “Captain Carla” → captain
const captain = await factories.UserFactory .create ({
discordName: "Captain Carla" ,
});Tournament register page Act
Open the tournament register page tournament.id
await register.goto (tournament.id);Assert
registration closed alert is visible
await expect (register.locators.registrationClosedAlert).toBeVisible ();register tab count is 0
await expect (register.nav.locators.registerTab).toHaveCount (0 );Tournament admin registration page Act
Go to new tournament.id
await registration.gotoNew (tournament.id);Assert
add heading is visible
await expect (registration.locators.addHeading).toBeVisible ();Act
Fill Team name with “Invited Squad”
await registration.form.fill ("pickUpName" , "Invited Squad" );Select player “Captain Carla”
await registration.selectPlayer ("Captain Carla" );Select captain captain.id
await registration.selectCaptain (captain.id);Save
await registration.save ();Tournament admin page Assert
team name “Invited Squad” is visible
await expect (admin.teamName ("Invited Squad" )).toBeVisible ();Tournament teams page Act
Open the tournament teams page tournament.id
await teams.goto (tournament.id);Assert
team named “Invited Squad” is visible
await expect (teams.teamNamed ("Invited Squad" )).toBeVisible ();member named “Captain Carla” is visible
await expect (teams.memberNamed ("Captain Carla" )).toBeVisible ();Tournament register page Assert
as the captain of an invitational team they can now manage the registration
register tab is visible
await expect (register.nav.locators.registerTab).toBeVisible ();Source test ("team can't register on their own, the TO adds them instead" , async ({
page,
factories,
}) => {
const tournament = await createInvitational (factories);
const captain = await factories.UserFactory .create ({
discordName: "Captain Carla" ,
});
await impersonate (page, captain.id);
const register = new TournamentRegisterPage (page);
await register.goto (tournament.id);
await expect (register.locators.registrationClosedAlert).toBeVisible ();
await expect (register.nav.locators.registerTab).toHaveCount (0 );
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" , "Invited Squad" );
await registration.selectPlayer ("Captain Carla" );
await registration.selectCaptain (captain.id);
await registration.save ();
const admin = new TournamentAdminPage (page);
await expect (admin.teamName ("Invited Squad" )).toBeVisible ();
await impersonate (page, captain.id);
const teams = new TournamentTeamsPage (page);
await teams.goto (tournament.id);
await expect (teams.teamNamed ("Invited Squad" )).toBeVisible ();
await expect (teams.memberNamed ("Captain Carla" )).toBeVisible ();
// as the captain of an invitational team they can now manage the registration
await expect (register.nav.locators.registerTab).toBeVisible ();
})member added by the TO can't leave the team Renders/to/:id /to/:id/admin /to/:id/admin /to/:id/admin/registration/:tid? /to/:id/register
Tournament admin registration page Tournament admin page Tournament register page Tournament register page Arrange
Create invitational → tournament
const tournament = await createInvitational (factories);Create user discord name “Captain Carla” → captain
const captain = await factories.UserFactory .create ({
discordName: "Captain Carla" ,
});Create user discord name “Member Mia” → member
const member = await factories.UserFactory .create ({
discordName: "Member Mia" ,
});Tournament admin registration page Act
Go to new tournament.id
await registration.gotoNew (tournament.id);Assert
add heading is visible
await expect (registration.locators.addHeading).toBeVisible ();Act
Fill Team name with “Invited Squad”
await registration.form.fill ("pickUpName" , "Invited Squad" );Select player “Captain Carla”
await registration.selectPlayer ("Captain Carla" );Add member “Member Mia”
await registration.addMember ("Member Mia" );Select captain captain.id
await registration.selectCaptain (captain.id);Save
await registration.save ();Tournament admin page Assert
team name “Invited Squad” is visible
await expect (admin.teamName ("Invited Squad" )).toBeVisible ();Tournament register page Act
Open the tournament register page tournament.id
await register.goto (tournament.id);Click leave team button
await register.locators.leaveTeamButton.click ();Assert
organizer added leave explanation is visible
await expect (
register.locators.organizerAddedLeaveExplanation,
).toBeVisible ();Tournament register page Act
Open the tournament register page tournament.id
await register.goto (tournament.id);Assert
member 2 is visible
await expect (register.member (2 )).toBeVisible ();delete member button count is 0
await expect (register.locators.deleteMemberButton).toHaveCount (0 );unregister button count is 0
await expect (register.locators.unregisterButton).toHaveCount (0 );Source test ("member added by the TO can't leave the team" , async ({
page,
factories,
}) => {
const tournament = await createInvitational (factories);
const captain = await factories.UserFactory .create ({
discordName: "Captain Carla" ,
});
const member = await factories.UserFactory .create ({
discordName: "Member Mia" ,
});
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" , "Invited Squad" );
await registration.selectPlayer ("Captain Carla" );
await registration.addMember ("Member Mia" );
await registration.selectCaptain (captain.id);
await registration.save ();
const admin = new TournamentAdminPage (page);
await expect (admin.teamName ("Invited Squad" )).toBeVisible ();
await impersonate (page, member.id);
const register = new TournamentRegisterPage (page);
await register.goto (tournament.id);
await register.locators.leaveTeamButton.click ();
await expect (
register.locators.organizerAddedLeaveExplanation,
).toBeVisible ();
// nor can the captain kick them, the organizer owns the roster
await impersonate (page, captain.id);
await register.goto (tournament.id);
await expect (register.member (2 )).toBeVisible ();
await expect (register.locators.deleteMemberButton).toHaveCount (0 );
await expect (register.locators.unregisterButton).toHaveCount (0 );
})Tournament league teams agree on a time, decline a reschedule, the organizer overrides it and the set gets played Renders/to/:id /to/:id/matches /to/:id/matches/:mid
Tournament match page Tournament match page Tournament match page Tournament match page Tournament match page Tournament match page Arrange
Create league set → { tournament, opponentTeam, matchId }
const { tournament, opponentTeam, matchId } =
await createLeagueSet (factories);nzapTime = addHours (addDays (new Date (), 2 ), 1 )
const nzapTime = addHours (addDays (new Date (), 2 ), 1 );opponentTime = addHours (addDays (new Date (), 3 ), 1 )
const opponentTime = addHours (addDays (new Date (), 3 ), 1 );Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await matchPage.goto ({ tournamentId: tournament.id, matchId });Assert
unscheduled banner is visible
await expect (matchPage.locators.unscheduledBanner).toBeVisible ();stage banner is not visible
await isNotVisible (matchPage.locators.stageBanner);Act
Propose time nzapTime
await matchPage.proposeTime (nzapTime);Assert
own candidates › element “candidate-time” count is 1
await expect (
matchPage.locators.ownCandidates.getByTestId ("candidate-time" ),
).toHaveCount (1 );only the other team can pick a candidate
pick candidate buttons is not visible
await isNotVisible (matchPage.locators.pickCandidateButtons);Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await matchPage.goto ({ tournamentId: tournament.id, matchId });Assert
opponent candidates › element “candidate-time” count is 1
await expect (
matchPage.locators.opponentCandidates.getByTestId ("candidate-time" ),
).toHaveCount (1 );Act
Propose time opponentTime
await matchPage.proposeTime (opponentTime);Assert
candidate times count is 2
await expect (matchPage.locators.candidateTimes).toHaveCount (2 );Act
Pick candidate
await matchPage.pickCandidate ();Assert
agreed time is visible
await expect (matchPage.locators.agreedTime).toBeVisible ();candidate times count is 0
await expect (matchPage.locators.candidateTimes).toHaveCount (0 );agreed and playable: the stage banner is back and the set can be reported
stage banner is visible
await expect (matchPage.locators.stageBanner).toBeVisible ();Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await matchPage.goto ({ tournamentId: tournament.id, matchId });Open tab “schedule”
await matchPage.openTab ("schedule" );Propose time addHours (nzapTime, 2 )
await matchPage.proposeTime (addHours (nzapTime, 2 ));Assert
candidate times count is 1
await expect (matchPage.locators.candidateTimes).toHaveCount (1 );Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await matchPage.goto ({ tournamentId: tournament.id, matchId });Open tab “schedule”
await matchPage.openTab ("schedule" );Assert
reject reschedule button is visible
await expect (matchPage.locators.rejectRescheduleButton).toBeVisible ();Act
Click reject reschedule button
await matchPage.locators.rejectRescheduleButton.click ();Assert
candidate times count is 0
await expect (matchPage.locators.candidateTimes).toHaveCount (0 );agreed time is visible
await expect (matchPage.locators.agreedTime).toBeVisible ();Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await matchPage.goto ({ tournamentId: tournament.id, matchId });Open tab “admin”
await matchPage.openTab ("admin" );Organizer set time addHours (opponentTime, 3 )
await matchPage.organizerSetTime (addHours (opponentTime, 3 ));Open tab “schedule”
await matchPage.openTab ("schedule" );Assert
set by organizer text is visible
await expect (matchPage.locators.setByOrganizerText).toBeVisible ();propose times button is not visible
await isNotVisible (matchPage.locators.proposeTimesButton);Act
the scheduled set shows up on the matches page, and gets played like any other
Go to tournamentMatchesPage (tournament.id)
await navigate ({ page, url: tournamentMatchesPage (tournament.id) });Assert
element “scheduled-sets” is visible
await expect (page.getByTestId ("scheduled-sets" )).toBeVisible ();element `set-row-${matchId}` is visible
await expect (page.getByTestId (`set-row-${matchId}` )).toBeVisible ();Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await matchPage.goto ({ tournamentId: tournament.id, matchId });Open tab “action”
await matchPage.openTab ("action" );Report result maps to report 2
await matchPage.reportResult ({ mapsToReport: 2 });Assert
final banner is visible
await expect (matchPage.locators.finalBanner).toBeVisible ();Act
Go to tournamentMatchesPage (tournament.id)
await navigate ({ page, url: tournamentMatchesPage (tournament.id) });Click element “matches-tab-past”
await page.getByTestId ("matches-tab-past" ).click ();Assert
element “past-sets” › element `set-row-${matchId}` is visible
await expect (
page.getByTestId ("past-sets" ).getByTestId (`set-row-${matchId}` ),
).toBeVisible ();Source test ("teams agree on a time, decline a reschedule, the organizer overrides it and the set gets played" , async ({
page,
factories,
}) => {
test.slow ();
const { tournament, opponentTeam, matchId } =
await createLeagueSet (factories);
const matchPage = new TournamentMatchPage (page);
const nzapTime = addHours (addDays (new Date (), 2 ), 1 );
const opponentTime = addHours (addDays (new Date (), 3 ), 1 );
// N-ZAP proposes a time; the set has no time yet so the schedule tab opens by itself
await impersonate (page, NZAP_TEST_ID );
await matchPage.goto ({ tournamentId: tournament.id, matchId });
await expect (matchPage.locators.unscheduledBanner).toBeVisible ();
await isNotVisible (matchPage.locators.stageBanner);
await matchPage.proposeTime (nzapTime);
await expect (
matchPage.locators.ownCandidates.getByTestId ("candidate-time" ),
).toHaveCount (1 );
// only the other team can pick a candidate
await isNotVisible (matchPage.locators.pickCandidateButtons);
// the opponent answers with a time of their own, then picks N-ZAP's after all
await impersonate (page, opponentTeam.ownerUserId);
await matchPage.goto ({ tournamentId: tournament.id, matchId });
await expect (
matchPage.locators.opponentCandidates.getByTestId ("candidate-time" ),
).toHaveCount (1 );
await matchPage.proposeTime (opponentTime);
await expect (matchPage.locators.candidateTimes).toHaveCount (2 );
await matchPage.pickCandidate ();
await expect (matchPage.locators.agreedTime).toBeVisible ();
await expect (matchPage.locators.candidateTimes).toHaveCount (0 );
// agreed and playable: the stage banner is back and the set can be reported
await expect (matchPage.locators.stageBanner).toBeVisible ();
// N-ZAP asks for another time as a favor, the opponent keeps the agreed one
await impersonate (page, NZAP_TEST_ID );
await matchPage.goto ({ tournamentId: tournament.id, matchId });
await matchPage.openTab ("schedule" );
await matchPage.proposeTime (addHours (nzapTime, 2 ));
await expect (matchPage.locators.candidateTimes).toHaveCount (1 );
await impersonate (page, opponentTeam.ownerUserId);
await matchPage.goto ({ tournamentId: tournament.id, matchId });
await matchPage.openTab ("schedule" );
await expect (matchPage.locators.rejectRescheduleButton).toBeVisible ();
await matchPage.locators.rejectRescheduleButton.click ();
await expect (matchPage.locators.candidateTimes).toHaveCount (0 );
await expect (matchPage.locators.agreedTime).toBeVisible ();
// the organizer's time is final and closes the board
await impersonate (page, ADMIN_ID );
await matchPage.goto ({ tournamentId: tournament.id, matchId });
await matchPage.openTab ("admin" );
await matchPage.organizerSetTime (addHours (opponentTime, 3 ));
await matchPage.openTab ("schedule" );
await expect (matchPage.locators.setByOrganizerText).toBeVisible ();
await isNotVisible (matchPage.locators.proposeTimesButton);
// the scheduled set shows up on the matches page, and gets played like any other
await navigate ({ page, url: tournamentMatchesPage (tournament.id) });
await expect (page.getByTestId ("scheduled-sets" )).toBeVisible ();
await expect (page.getByTestId (`set-row-${matchId}` )).toBeVisible ();
await impersonate (page, NZAP_TEST_ID );
await matchPage.goto ({ tournamentId: tournament.id, matchId });
await matchPage.openTab ("action" );
await matchPage.reportResult ({ mapsToReport: 2 });
await expect (matchPage.locators.finalBanner).toBeVisible ();
await navigate ({ page, url: tournamentMatchesPage (tournament.id) });
await page.getByTestId ("matches-tab-past" ).click ();
await expect (
page.getByTestId ("past-sets" ).getByTestId (`set-row-${matchId}` ),
).toBeVisible ();
})a set can't be played before the teams agree on a time or the round is playable Renders/to/:id /to/:id/matches/:mid
Tournament match page Arrange
Create tournament author Sendou start times startedTournamentTimes () bracket progression ROUND_ROBIN map pool maps TO_MAP_POOL is league true → tournament
const tournament = await factories.TournamentFactory .create (
{
authorId: ADMIN_ID ,
startTimes: startedTournamentTimes (),
bracketProgression: ROUND_ROBIN ,
mapPoolMaps: TO_MAP_POOL ,
},
{ isLeague: true },
);Create teams tournament.id, members N-ZAP ,
await createTeams (factories, tournament.id, [
{ members: [NZAP_TEST_ID ] },
{},
]);playable in a week: the board opens a day before that
Start bracket tournament tournament.id is playable at () => dateToDatabaseTimestamp (addDays (new Date (), 7 )) → [match]
const [match] = await factories.TournamentFactory .startBracket (
tournament.id,
{
isPlayableAt: () => dateToDatabaseTimestamp (addDays (new Date (), 7 )),
},
);Tournament match page Act
Open the tournament match page tournament id tournament.id match id match.id
await matchPage.goto ({ tournamentId: tournament.id, matchId: match.id });Assert
element “league-not-open-banner” is visible
await expect (page.getByTestId ("league-not-open-banner" )).toBeVisible ();schedule tab is not visible
await isNotVisible (matchPage.locators.scheduleTab);stage banner is not visible
await isNotVisible (matchPage.locators.stageBanner);Source test ("a set can't be played before the teams agree on a time or the round is playable" , async ({
page,
factories,
}) => {
const tournament = await factories.TournamentFactory .create (
{
authorId: ADMIN_ID ,
startTimes: startedTournamentTimes (),
bracketProgression: ROUND_ROBIN ,
mapPoolMaps: TO_MAP_POOL ,
},
{ isLeague: true },
);
await createTeams (factories, tournament.id, [
{ members: [NZAP_TEST_ID ] },
{},
]);
// playable in a week: the board opens a day before that
const [match] = await factories.TournamentFactory .startBracket (
tournament.id,
{
isPlayableAt: () => dateToDatabaseTimestamp (addDays (new Date (), 7 )),
},
);
await impersonate (page, NZAP_TEST_ID );
const matchPage = new TournamentMatchPage (page);
await matchPage.goto ({ tournamentId: tournament.id, matchId: match.id });
await expect (page.getByTestId ("league-not-open-banner" )).toBeVisible ();
await isNotVisible (matchPage.locators.scheduleTab);
await isNotVisible (matchPage.locators.stageBanner);
})a league of several divisions lists them on the divisions page Renders/to/:id /to/:id/divisions
Tournament divisions page Arrange
Create tournament author Sendou start times startedTournamentTimes () bracket progression ...ROUND_ROBIN [0 ] name “Division 1” , ...ROUND_ROBIN [0 ] name “Division 2” map pool maps TO_MAP_POOL is league true → tournament
const tournament = await factories.TournamentFactory .create (
{
authorId: ADMIN_ID ,
startTimes: startedTournamentTimes (),
bracketProgression: [
{ ...ROUND_ROBIN [0 ], name: "Division 1" },
{ ...ROUND_ROBIN [0 ], name: "Division 2" },
],
mapPoolMaps: TO_MAP_POOL ,
},
{ isLeague: true },
);Create teams tournament.id, , , ,
await createTeams (factories, tournament.id, [{}, {}, {}, {}]);Tournament divisions page Act
Open the tournament divisions page tournament.id
await divisions.goto (tournament.id);Assert
division links count is 2
await expect (divisions.locators.divisionLinks).toHaveCount (2 );Source test ("a league of several divisions lists them on the divisions page" , async ({
page,
factories,
}) => {
const tournament = await factories.TournamentFactory .create (
{
authorId: ADMIN_ID ,
startTimes: startedTournamentTimes (),
bracketProgression: [
{ ...ROUND_ROBIN [0 ], name: "Division 1" },
{ ...ROUND_ROBIN [0 ], name: "Division 2" },
],
mapPoolMaps: TO_MAP_POOL ,
},
{ isLeague: true },
);
await createTeams (factories, tournament.id, [{}, {}, {}, {}]);
await impersonate (page, ADMIN_ID );
const divisions = new TournamentDivisionsPage (page);
await divisions.goto (tournament.id);
await expect (divisions.locators.divisionLinks).toHaveCount (2 );
})Tournament staff gives and takes away staff role Renders/to/:id /to/:id/admin /to/:id/admin /to/:id/admin/staff
Tournament admin page Tournament admin staff page Arrange
Create tournament author Sendou start times dateToDatabaseTimestamp (addHours (new Date (), 2 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});Tournament admin page Act
Open the tournament admin page tournament.id
await admin.goto (tournament.id);Open staff → staff
const staff = await admin.openStaff ();Tournament admin staff page Assert
the tournament author is always shown as an organizer (info only)
author row is visible
await expect (staff.locators.authorRow).toBeVisible ();Act
Add staffer “N-ZAP”
await staff.addStaffer ("N-ZAP" );Assert
staff row “N-ZAP” is visible
await expect (staff.staffRow ("N-ZAP" )).toBeVisible ();Act
Remove staffer
await staff.removeStaffer ();Assert
staff row “N-ZAP” is not visible
await isNotVisible (staff.staffRow ("N-ZAP" ));Source test ("gives and takes away staff role" , async ({ page, factories }) => {
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});
await impersonate (page, ADMIN_ID );
const admin = new TournamentAdminPage (page);
await admin.goto (tournament.id);
const staff = await admin.openStaff ();
// the tournament author is always shown as an organizer (info only)
await expect (staff.locators.authorRow).toBeVisible ();
await staff.addStaffer ("N-ZAP" );
await expect (staff.staffRow ("N-ZAP" )).toBeVisible ();
await staff.removeStaffer ();
await isNotVisible (staff.staffRow ("N-ZAP" ));
})gives organizer role which allows another user to TO Renders/to/:id /to/:id/admin /to/:id/admin /to/:id/admin/staff /to/:id/info
Tournament admin page Tournament admin page Tournament admin staff page Tournament admin page Arrange
Create tournament author Sendou start times dateToDatabaseTimestamp (addHours (new Date (), 2 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});Tournament admin page Act
Open the tournament admin page tournament.id
await admin.goto (tournament.id);no access yet, so redirected to info
Wait for URL “**/info”
await page.waitForURL ("**/info" );Tournament admin page Act
Open the tournament admin page tournament.id
await admin.goto (tournament.id);Open staff → staff
const staff = await admin.openStaff ();Tournament admin staff page Act
Add staffer “N-ZAP” , “ORGANIZER”
await staff.addStaffer ("N-ZAP" , "ORGANIZER" );Tournament admin page Act
Open the tournament admin page tournament.id
await admin.goto (tournament.id);Assert
an organizer gets admin page access
admin tab “Teams” is visible
await expect (admin.adminTab ("Teams" )).toBeVisible ();but an organizer has no perms to manage staff
admin tab “Staff” is not visible
await isNotVisible (admin.adminTab ("Staff" ));Source test ("gives organizer role which allows another user to TO" , async ({
page,
factories,
}) => {
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});
await impersonate (page, NZAP_TEST_ID );
const admin = new TournamentAdminPage (page);
await admin.goto (tournament.id);
// no access yet, so redirected to info
await page.waitForURL ("**/info" );
await impersonate (page, ADMIN_ID );
await admin.goto (tournament.id);
const staff = await admin.openStaff ();
await staff.addStaffer ("N-ZAP" , "ORGANIZER" );
await impersonate (page, NZAP_TEST_ID );
await admin.goto (tournament.id);
// an organizer gets admin page access
await expect (admin.adminTab ("Teams" )).toBeVisible ();
// but an organizer has no perms to manage staff
await isNotVisible (admin.adminTab ("Staff" ));
})gives staff role which allows another user to see limited info Renders/to/:id /to/:id/admin /to/:id/admin /to/:id/admin/staff /to/:id/matches/:mid
Tournament match page Tournament admin page Tournament admin staff page Tournament match page Arrange
Create tournament author Sendou start times dateToDatabaseTimestamp (subMinutes (new Date (), 30 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (subMinutes (new Date (), 30 ))],
});Create ROSTER_SIZE * 2 × user → players
const players = await factories.UserFactory .createMany (ROSTER_SIZE * 2 );For each roster of players.slice (0 , ROSTER_SIZE ), players.slice (ROSTER_SIZE )
for (const roster of [
players.slice (0 , ROSTER_SIZE ),
players.slice (ROSTER_SIZE ),
]) {
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: roster.map ((user) => user.id),
},
{ isCheckedIn: true },
);
}Create tournament team tournament id tournament.id member user ids roster.map ((user) => user.id) is checked in true
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: roster.map ((user) => user.id),
},
{ isCheckedIn: true },
);Start bracket tournament tournament.id → matches
const matches = await factories.TournamentFactory .startBracket (
tournament.id,
);matchId = matches[0 ].id
const matchId = matches[0 ].id;Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await match.goto ({ tournamentId: tournament.id, matchId });Assert
room pass is not visible
await isNotVisible (match.locators.roomPass);Tournament admin page Act
Open the tournament admin page tournament.id
await admin.goto (tournament.id);Open staff → staff
const staff = await admin.openStaff ();Tournament admin staff page Act
Add staffer “N-ZAP” , “STREAMER”
await staff.addStaffer ("N-ZAP" , "STREAMER" );Assert
staff row “N-ZAP” contains “streamer”
await expect (staff.staffRow ("N-ZAP" )).toContainText ("streamer" );Tournament match page Act
Open the tournament match page tournament id tournament.id matchId
await match.goto ({ tournamentId: tournament.id, matchId });Assert
room pass is visible
await expect (match.locators.roomPass).toBeVisible ();Source test ("gives staff role which allows another user to see limited info" , async ({
page,
factories,
}) => {
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (subMinutes (new Date (), 30 ))],
});
const players = await factories.UserFactory .createMany (ROSTER_SIZE * 2 );
for (const roster of [
players.slice (0 , ROSTER_SIZE ),
players.slice (ROSTER_SIZE ),
]) {
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: roster.map ((user) => user.id),
},
{ isCheckedIn: true },
);
}
const matches = await factories.TournamentFactory .startBracket (
tournament.id,
);
const matchId = matches[0 ].id;
await impersonate (page, NZAP_TEST_ID );
const match = new TournamentMatchPage (page);
await match.goto ({ tournamentId: tournament.id, matchId });
await isNotVisible (match.locators.roomPass);
await impersonate (page, ADMIN_ID );
const admin = new TournamentAdminPage (page);
await admin.goto (tournament.id);
const staff = await admin.openStaff ();
await staff.addStaffer ("N-ZAP" , "STREAMER" );
await expect (staff.staffRow ("N-ZAP" )).toContainText ("streamer" );
await impersonate (page, NZAP_TEST_ID );
await match.goto ({ tournamentId: tournament.id, matchId });
await expect (match.locators.roomPass).toBeVisible ();
})Tournament streams can set cast twitch accounts in admin Renders/to/:id /to/:id/admin /to/:id/admin /to/:id/admin/stream /to/:id/brackets
Tournament admin page Tournament admin stream page Tournament brackets page Tournament admin stream page Arrange
Create tournament author N-ZAP start times dateToDatabaseTimestamp (addHours (new Date (), 2 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: NZAP_TEST_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});Tournament admin page Act
Open the tournament admin page tournament.id
await admin.goto (tournament.id);Open stream → stream
const stream = await admin.openStream ();Tournament admin stream page Act
Fill account 0 , “test_cast_stream”
await stream.fillAccount (0 , CAST_ACCOUNT );Add account field
await stream.addAccountField ();Fill account 1 , “another_cast”
await stream.fillAccount (1 , "another_cast" );Save
await stream.save ();Tournament brackets page Act
Open the tournament brackets page tournament.id
await brackets.goto (tournament.id);Tournament admin stream page Act
Open the tournament admin stream page tournament.id
await stream.goto (tournament.id);Assert
account input 0 has value “test_cast_stream”
await expect (stream.accountInput (0 )).toHaveValue (CAST_ACCOUNT );account input 1 has value “another_cast”
await expect (stream.accountInput (1 )).toHaveValue ("another_cast" );Source test ("can set cast twitch accounts in admin" , async ({ page, factories }) => {
const tournament = await factories.TournamentFactory .create ({
authorId: NZAP_TEST_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});
await impersonate (page, NZAP_TEST_ID );
const admin = new TournamentAdminPage (page);
await admin.goto (tournament.id);
const stream = await admin.openStream ();
await stream.fillAccount (0 , CAST_ACCOUNT );
await stream.addAccountField ();
await stream.fillAccount (1 , "another_cast" );
await stream.save ();
const brackets = new TournamentBracketsPage (page);
await brackets.goto (tournament.id);
await stream.goto (tournament.id);
await expect (stream.accountInput (0 )).toHaveValue (CAST_ACCOUNT );
await expect (stream.accountInput (1 )).toHaveValue ("another_cast" );
})can view streams on bracket popover when match is in progress Renders/to/:id /to/:id/brackets /to/:id/matches/:mid
Tournament brackets page Tournament match page Tournament brackets page Arrange
Create startable tournament → { tournament, players }
const { tournament, players } = await createStartableTournament (factories);Replace all live stream user id players[0 ].id viewer count 150
await factories.LiveStreamFactory .replaceAll ([
{ userId: players[0 ].id, viewerCount: STREAM_VIEWER_COUNT },
]);Start bracket tournament tournament.id → matches
const matches = await factories.TournamentFactory .startBracket (
tournament.id,
);Tournament brackets page Act
Open the tournament brackets page tournament.id
await brackets.goto (tournament.id);Open match matches[0 ].id → match
const match = await brackets.openMatch (matches[0 ].id);Tournament match page Act
a partial score sets startedAt, making the match in progress
Open tab “action”
await match.openTab ("action" );Report result maps to report 1 set ends false
await match.reportResult ({ mapsToReport: 1 , setEnds: false });Back to bracket
await match.backToBracket ();Tournament brackets page Assert
a participant of an in progress match is streaming
first live badges is visible
await expect (brackets.locators.liveBadges.first ()).toBeVisible ();Act
Click first live badges
await brackets.locators.liveBadges.first ().click ();Assert
stream popover is visible
await expect (brackets.locators.streamPopover).toBeVisible ();first stream popover streams is visible
await expect (brackets.locators.streamPopoverStreams.first ()).toBeVisible ();Source test ("can view streams on bracket popover when match is in progress" , async ({
page,
factories,
}) => {
const { tournament, players } = await createStartableTournament (factories);
await factories.LiveStreamFactory .replaceAll ([
{ userId: players[0 ].id, viewerCount: STREAM_VIEWER_COUNT },
]);
const matches = await factories.TournamentFactory .startBracket (
tournament.id,
);
await impersonate (page, NZAP_TEST_ID );
const brackets = new TournamentBracketsPage (page);
await brackets.goto (tournament.id);
const match = await brackets.openMatch (matches[0 ].id);
// a partial score sets startedAt, making the match in progress
await match.openTab ("action" );
await match.reportResult ({ mapsToReport: 1 , setEnds: false });
await match.backToBracket ();
// a participant of an in progress match is streaming
await expect (brackets.locators.liveBadges.first ()).toBeVisible ();
await brackets.locators.liveBadges.first ().click ();
await expect (brackets.locators.streamPopover).toBeVisible ();
await expect (brackets.locators.streamPopoverStreams.first ()).toBeVisible ();
})can view streams on streams page Renders/to/:id /to/:id/streams
Tournament streams page Arrange
Create startable tournament → { tournament, players }
const { tournament, players } = await createStartableTournament (factories);Replace all live stream user id players[0 ].id viewer count 150
await factories.LiveStreamFactory .replaceAll ([
{ userId: players[0 ].id, viewerCount: STREAM_VIEWER_COUNT },
]);Start bracket tournament tournament.id
await factories.TournamentFactory .startBracket (tournament.id);Tournament streams page Act
Open the tournament streams page tournament.id
await streams.goto (tournament.id);Assert
first streams is visible
await expect (streams.locators.streams.first ()).toBeVisible ();first viewer count 150 is visible
await expect (
streams.viewerCount (STREAM_VIEWER_COUNT ).first (),
).toBeVisible ();Source test ("can view streams on streams page" , async ({ page, factories }) => {
const { tournament, players } = await createStartableTournament (factories);
await factories.LiveStreamFactory .replaceAll ([
{ userId: players[0 ].id, viewerCount: STREAM_VIEWER_COUNT },
]);
await factories.TournamentFactory .startBracket (tournament.id);
const streams = new TournamentStreamsPage (page);
await streams.goto (tournament.id);
await expect (streams.locators.streams.first ()).toBeVisible ();
await expect (
streams.viewerCount (STREAM_VIEWER_COUNT ).first (),
).toBeVisible ();
})Renders/to/:id /to/:id/streams
Tournament streams page Arrange
Create startable tournament → { tournament, players }
const { tournament, players } = await createStartableTournament (factories);Replace all live stream user id players[0 ].id account “both_platforms” viewer count 100 , user id players[0 ].id platform “YOUTUBE” account “UCbothPlatforms000000000” viewer count 50
await factories.LiveStreamFactory .replaceAll ([
{
userId: players[0 ].id,
account: "both_platforms" ,
viewerCount: 100 ,
},
{
userId: players[0 ].id,
platform: "YOUTUBE" ,
account: "UCbothPlatforms000000000" ,
viewerCount: 50 ,
},
]);Start bracket tournament tournament.id
await factories.TournamentFactory .startBracket (tournament.id);Tournament streams page Act
Open the tournament streams page tournament.id
await streams.goto (tournament.id);Assert
streams count is 1
await expect (streams.locators.streams).toHaveCount (1 );platform link “Twitch” has “href” = LiveStream .url ({ platform: "TWITCH" , account: "both_platforms" })
await expect (streams.platformLink ("Twitch" )).toHaveAttribute (
"href" ,
LiveStream .url ({ platform: "TWITCH" , account: "both_platforms" }),
);platform link “YouTube” has “href” = LiveStream .url ({ platform: "YOUTUBE" , account: "UCbothPlatforms000000000" , })
await expect (streams.platformLink ("YouTube" )).toHaveAttribute (
"href" ,
LiveStream .url ({
platform: "YOUTUBE" ,
account: "UCbothPlatforms000000000" ,
}),
);viewer count 150 is visible
await expect (streams.viewerCount (150 )).toBeVisible ();Act
Click help button
await streams.locators.helpButton.click ();Assert
help dialog is visible
await expect (streams.locators.helpDialog).toBeVisible ();Source test ("streamer live on both platforms shows as one card with a link per platform" , async ({
page,
factories,
}) => {
const { tournament, players } = await createStartableTournament (factories);
await factories.LiveStreamFactory .replaceAll ([
{
userId: players[0 ].id,
account: "both_platforms" ,
viewerCount: 100 ,
},
{
userId: players[0 ].id,
platform: "YOUTUBE" ,
account: "UCbothPlatforms000000000" ,
viewerCount: 50 ,
},
]);
await factories.TournamentFactory .startBracket (tournament.id);
const streams = new TournamentStreamsPage (page);
await streams.goto (tournament.id);
await expect (streams.locators.streams).toHaveCount (1 );
await expect (streams.platformLink ("Twitch" )).toHaveAttribute (
"href" ,
LiveStream .url ({ platform: "TWITCH" , account: "both_platforms" }),
);
await expect (streams.platformLink ("YouTube" )).toHaveAttribute (
"href" ,
LiveStream .url ({
platform: "YOUTUBE" ,
account: "UCbothPlatforms000000000" ,
}),
);
await expect (streams.viewerCount (150 )).toBeVisible ();
await streams.locators.helpButton.click ();
await expect (streams.locators.helpDialog).toBeVisible ();
})cast stream shows on bracket when match is set as casted Renders/to/:id /to/:id/admin /to/:id/admin /to/:id/admin/stream /to/:id/brackets /to/:id/matches/:mid
Tournament admin page Tournament admin stream page Tournament brackets page Tournament match page Tournament brackets page Arrange
Create startable tournament → { tournament }
const { tournament } = await createStartableTournament (factories);Replace all live stream user id null account “test_cast_stream”
await factories.LiveStreamFactory .replaceAll ([
{ userId: null , account: CAST_ACCOUNT },
]);Tournament admin page Act
Open the tournament admin page tournament.id
await admin.goto (tournament.id);Open stream → stream
const stream = await admin.openStream ();Tournament admin stream page Act
Fill account 0 , “test_cast_stream”
await stream.fillAccount (0 , CAST_ACCOUNT );Save
await stream.save ();Arrange
Start bracket tournament tournament.id → matches
const matches = await factories.TournamentFactory .startBracket (
tournament.id,
);Tournament brackets page Act
Open the tournament brackets page tournament.id
await brackets.goto (tournament.id);Open match matches[0 ].id → match
const match = await brackets.openMatch (matches[0 ].id);Tournament match page Act
Open tab “action”
await match.openTab ("action" );Report result maps to report 1 set ends false
await match.reportResult ({ mapsToReport: 1 , setEnds: false });Open tab “admin”
await match.openTab ("admin" );Set casted by “test_cast_stream”
await match.setCastedBy (CAST_ACCOUNT );Back to bracket
await match.backToBracket ();Tournament brackets page Assert
first live badges is visible
await expect (brackets.locators.liveBadges.first ()).toBeVisible ();Source test ("cast stream shows on bracket when match is set as casted" , async ({
page,
factories,
}) => {
const { tournament } = await createStartableTournament (factories);
await factories.LiveStreamFactory .replaceAll ([
{ userId: null , account: CAST_ACCOUNT },
]);
await impersonate (page, NZAP_TEST_ID );
const admin = new TournamentAdminPage (page);
await admin.goto (tournament.id);
const stream = await admin.openStream ();
await stream.fillAccount (0 , CAST_ACCOUNT );
await stream.save ();
const matches = await factories.TournamentFactory .startBracket (
tournament.id,
);
const brackets = new TournamentBracketsPage (page);
await brackets.goto (tournament.id);
const match = await brackets.openMatch (matches[0 ].id);
await match.openTab ("action" );
await match.reportResult ({ mapsToReport: 1 , setEnds: false });
await match.openTab ("admin" );
await match.setCastedBy (CAST_ACCOUNT );
await match.backToBracket ();
await expect (brackets.locators.liveBadges.first ()).toBeVisible ();
})Tournament tiers shows tentative tier before bracket starts and confirmed tier after Renders/calendar /to/:id /to/:id/brackets
Calendar page Tournament brackets page Calendar page Arrange
Create TEAM_COUNT * ROSTER_SIZE × user → players
const players = await factories.UserFactory .createMany (
TEAM_COUNT * ROSTER_SIZE ,
);rosters = Array .from ({ length: TEAM_COUNT }, (_, i) => players .slice (i * ROSTER_SIZE , …
const rosters = Array .from ({ length: TEAM_COUNT }, (_, i) =>
players
.slice (i * ROSTER_SIZE , (i + 1 ) * ROSTER_SIZE )
.map ((player) => player.id),
);Create tournament organization owner N-ZAP series name “PICNIC” description null show leaderboard false → org
const org = await factories.TournamentOrganizationFactory .create (
{ ownerId: NZAP_TEST_ID },
{
series: [{ name: "PICNIC" , description: null , showLeaderboard: false }],
},
);a finalized earlier edition: its tier seeds the series' tier history and playing it out gives every player the seeding skill the confirmed tier of the next edition is calculated from
Create played tournament name “PICNIC 1” author N-ZAP organization id org.id start times dateToDatabaseTimestamp (subHours (new Date (), 5 )) tags null is ranked false team rosters rosters tier 5 played out “all”
await factories.TournamentFactory .createPlayed (
{
name: "PICNIC 1" ,
authorId: NZAP_TEST_ID ,
organizationId: org.id,
startTimes: [dateToDatabaseTimestamp (subHours (new Date (), 5 ))],
tags: null ,
isRanked: false ,
},
{ teamRosters: rosters, tier: HISTORY_TIER , playedOut: "all" },
);Create tournament name “PICNIC 2” author N-ZAP organization id org.id start times dateToDatabaseTimestamp (subMinutes (new Date (), 30 )) tags null map picking style “AUTO” team pick modes mode “SZ” count 6 pool “SENDOUQ” → tournament
const tournament = await factories.TournamentFactory .create ({
name: "PICNIC 2" ,
authorId: NZAP_TEST_ID ,
organizationId: org.id,
startTimes: [dateToDatabaseTimestamp (subMinutes (new Date (), 30 ))],
tags: null ,
mapPickingStyle: "AUTO" ,
teamPick: { modes: [{ mode: "SZ" , count: 6 }], pool: "SENDOUQ" },
});For each roster of rosters
for (const roster of rosters) {
await factories.TournamentTeamFactory .create (
{ tournamentId: tournament.id, memberUserIds: roster },
{ isCheckedIn: true },
);
}Create tournament team tournament id tournament.id member user ids roster is checked in true
await factories.TournamentTeamFactory .create (
{ tournamentId: tournament.id, memberUserIds: roster },
{ isCheckedIn: true },
);Calendar page Act
Open the calendar page
await calendar.goto ();Assert
tentative tier pill “PICNIC 2” is visible
await expect (calendar.tentativeTierPill ("PICNIC 2" )).toBeVisible ();Tournament brackets page Act
Open the tournament brackets page tournament.id
await brackets.goto (tournament.id);Finalize
await brackets.finalize ();Calendar page Act
Open the calendar page
await calendar.goto ();Assert
confirmed tier pill “PICNIC 2” is visible
await expect (calendar.confirmedTierPill ("PICNIC 2" )).toBeVisible ();Source test ("shows tentative tier before bracket starts and confirmed tier after" , async ({
page,
factories,
}) => {
test.slow ();
const players = await factories.UserFactory .createMany (
TEAM_COUNT * ROSTER_SIZE ,
);
const rosters = Array .from ({ length: TEAM_COUNT }, (_, i) =>
players
.slice (i * ROSTER_SIZE , (i + 1 ) * ROSTER_SIZE )
.map ((player) => player.id),
);
const org = await factories.TournamentOrganizationFactory .create (
{ ownerId: NZAP_TEST_ID },
{
series: [{ name: "PICNIC" , description: null , showLeaderboard: false }],
},
);
// a finalized earlier edition: its tier seeds the series' tier history and
// playing it out gives every player the seeding skill the confirmed tier
// of the next edition is calculated from
await factories.TournamentFactory .createPlayed (
{
name: "PICNIC 1" ,
authorId: NZAP_TEST_ID ,
organizationId: org.id,
startTimes: [dateToDatabaseTimestamp (subHours (new Date (), 5 ))],
tags: null ,
isRanked: false ,
},
{ teamRosters: rosters, tier: HISTORY_TIER , playedOut: "all" },
);
const tournament = await factories.TournamentFactory .create ({
name: "PICNIC 2" ,
authorId: NZAP_TEST_ID ,
organizationId: org.id,
startTimes: [dateToDatabaseTimestamp (subMinutes (new Date (), 30 ))],
tags: null ,
mapPickingStyle: "AUTO" ,
teamPick: { modes: [{ mode: "SZ" , count: 6 }], pool: "SENDOUQ" },
});
for (const roster of rosters) {
await factories.TournamentTeamFactory .create (
{ tournamentId: tournament.id, memberUserIds: roster },
{ isCheckedIn: true },
);
}
const calendar = new CalendarPage (page);
await calendar.goto ();
await expect (calendar.tentativeTierPill ("PICNIC 2" )).toBeVisible ();
await impersonate (page, NZAP_TEST_ID );
const brackets = new TournamentBracketsPage (page);
await brackets.goto (tournament.id);
await brackets.finalize ();
await calendar.goto ();
await expect (calendar.confirmedTierPill ("PICNIC 2" )).toBeVisible ();
})Tournament Renders/ /to/:id /to/:id/info /to/:id/register
Tournament page Tournament register page Notification popover Arrange
Create 4 × user → [captain, ...friends]
const [captain, ...friends] =
await factories.UserFactory .createMany (ROSTER_SIZE );For each friend of friends
for (const friend of friends) {
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});
}Create friendship user one id captain.id user two id friend.id
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});Create tournament author Sendou start times dateToDatabaseTimestamp (addHours (new Date (), 2 )) map picking style “AUTO” → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
// teams pick their own counterpick maps
mapPickingStyle: "AUTO" ,
});Tournament page Act
Open the tournament page tournament.id
await tournamentPage.goto (tournament.id);Register → register
const register = await tournamentPage.register ();Tournament register page Act
Fill Pick-up name with “Chimera”
await register.form.fill ("pickUpName" , TEAM_NAME );Submit
await register.form.submit ();Assert
member 1 is visible
await expect (register.member (1 )).toBeVisible ();Act
Repeat for (let memberNumber = 2 ; memberNumber <= ROSTER_SIZE ; memberNumber++)
for (let memberNumber = 2 ; memberNumber <= ROSTER_SIZE ; memberNumber++) {
await register.addPlayer ();
await expect (register.member (memberNumber)).toBeVisible ();
}Add player
await register.addPlayer ();member memberNumber is visible
await expect (register.member (memberNumber)).toBeVisible ();Pick counterpick maps
await register.pickCounterpickMaps ();Save counterpick maps
await register.saveCounterpickMaps ();Assert
step checkmark 3 is visible
await expect (register.stepCheckmark (3 )).toBeVisible ();Act
Go to “/”
await navigate ({ page, url: "/" });Notification popover Act
Open
await notifications.open ();Assert
notification `Added to a team (${TEAM_NAME})` is visible
await expect (
notifications.notification (`Added to a team (${TEAM_NAME})` ),
).toBeVisible ();Source test ("registers for tournament" , async ({ page, factories }) => {
const [captain, ...friends] =
await factories.UserFactory .createMany (ROSTER_SIZE );
for (const friend of friends) {
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});
}
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
// teams pick their own counterpick maps
mapPickingStyle: "AUTO" ,
});
await impersonate (page, captain.id);
const tournamentPage = new TournamentPage (page);
await tournamentPage.goto (tournament.id);
const register = await tournamentPage.register ();
await register.form.fill ("pickUpName" , TEAM_NAME );
await register.form.submit ();
await expect (register.member (1 )).toBeVisible ();
for (let memberNumber = 2 ; memberNumber <= ROSTER_SIZE ; memberNumber++) {
await register.addPlayer ();
await expect (register.member (memberNumber)).toBeVisible ();
}
await register.pickCounterpickMaps ();
await register.saveCounterpickMaps ();
await expect (register.stepCheckmark (3 )).toBeVisible ();
// adding to the roster notified the added member
await impersonate (page, friends[0 ].id);
await navigate ({ page, url: "/" });
const notifications = new NotificationPopover (page);
await notifications.open ();
await expect (
notifications.notification (`Added to a team (${TEAM_NAME})` ),
).toBeVisible ();
})shows the estimated end time next to the start time Renders/to/:id /to/:id/info
Tournament page Arrange
startsAt = dateToDatabaseTimestamp (addHours (new Date (), 2 ))
const startsAt = dateToDatabaseTimestamp (addHours (new Date (), 2 ));Create tournament author Sendou start times startsAt → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [startsAt],
});Tournament page Act
Open the tournament page tournament.id
await tournamentPage.goto (tournament.id);Assert
a lone single elimination bracket is the estimator's two hour case
estimated end has “datetime” = databaseTimestampToDate (startsAt + 2 * HOUR_SECONDS ).toISOString ()
await expect (tournamentPage.locators.estimatedEnd).toHaveAttribute (
"datetime" ,
databaseTimestampToDate (startsAt + 2 * HOUR_SECONDS ).toISOString (),
);Source test ("shows the estimated end time next to the start time" , async ({
page,
factories,
}) => {
const startsAt = dateToDatabaseTimestamp (addHours (new Date (), 2 ));
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [startsAt],
});
const tournamentPage = new TournamentPage (page);
await tournamentPage.goto (tournament.id);
// a lone single elimination bracket is the estimator's two hour case
await expect (tournamentPage.locators.estimatedEnd).toHaveAttribute (
"datetime" ,
databaseTimestampToDate (startsAt + 2 * HOUR_SECONDS ).toISOString (),
);
})quick adds all of the team's players at once Renders/to/:id /to/:id/info /to/:id/register
Tournament page Tournament register page Arrange
Create 4 × user → [captain, slayer, support, coach]
const [captain, slayer, support, coach] =
await factories.UserFactory .createMany (4 );Create team member user ids captain.id, slayer.id, support.id, coach.id roles slayer.id “SLAYER” support.id “SUPPORT” coach.id “COACH” → team
const team = await factories.TeamFactory .create (
{ memberUserIds: [captain.id, slayer.id, support.id, coach.id] },
{
roles: {
[slayer.id]: "SLAYER" ,
[support.id]: "SUPPORT" ,
[coach.id]: "COACH" ,
},
},
);Create tournament author Sendou start times dateToDatabaseTimestamp (addHours (new Date (), 2 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});Tournament page Act
Open the tournament page tournament.id
await tournamentPage.goto (tournament.id);Register → register
const register = await tournamentPage.register ();Tournament register page Act
Fill Pick-up name with “Chimera”
await register.form.fill ("pickUpName" , TEAM_NAME );Submit
await register.form.submit ();Assert
member 1 is visible
await expect (register.member (1 )).toBeVisible ();Act
teammates are offered in the quick add, grouped under the team
Open quick add
await register.openQuickAdd ();Assert
availability row slayer.id is visible
await expect (register.availabilityRow (slayer.id)).toBeVisible ();availability row coach.id is visible
await expect (register.availabilityRow (coach.id)).toBeVisible ();Act
Press Escape
await page.keyboard.press ("Escape" );Add all team players team.id
await register.addAllTeamPlayers (team.id);Assert
member 2 is visible
await expect (register.member (2 )).toBeVisible ();member 3 is visible
await expect (register.member (3 )).toBeVisible ();the coach is not part of the competitive lineup
member 4 is not visible
await isNotVisible (register.member (4 ));Source test ("quick adds all of the team's players at once" , async ({
page,
factories,
}) => {
const [captain, slayer, support, coach] =
await factories.UserFactory .createMany (4 );
const team = await factories.TeamFactory .create (
{ memberUserIds: [captain.id, slayer.id, support.id, coach.id] },
{
roles: {
[slayer.id]: "SLAYER" ,
[support.id]: "SUPPORT" ,
[coach.id]: "COACH" ,
},
},
);
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});
await impersonate (page, captain.id);
const tournamentPage = new TournamentPage (page);
await tournamentPage.goto (tournament.id);
const register = await tournamentPage.register ();
await register.form.fill ("pickUpName" , TEAM_NAME );
await register.form.submit ();
await expect (register.member (1 )).toBeVisible ();
// teammates are offered in the quick add, grouped under the team
await register.openQuickAdd ();
await expect (register.availabilityRow (slayer.id)).toBeVisible ();
await expect (register.availabilityRow (coach.id)).toBeVisible ();
await page.keyboard.press ("Escape" );
await register.addAllTeamPlayers (team.id);
await expect (register.member (2 )).toBeVisible ();
await expect (register.member (3 )).toBeVisible ();
// the coach is not part of the competitive lineup
await isNotVisible (register.member (4 ));
})shows the roster's availability for the event window Renders/to/:id /to/:id/register
Tournament register page Arrange
Create 5 × user → [captain, partialMember, unknownMember, stranger, friend]
const [captain, partialMember, unknownMember, stranger, friend] =
await factories.UserFactory .createMany (5 );Create team member user ids captain.id, partialMember.id, unknownMember.id
await factories.TeamFactory .create ({
memberUserIds: [captain.id, partialMember.id, unknownMember.id],
});Create friendship user one id captain.id user two id friend.id
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});startsAt = addHours (new Date (), 2 )
const startsAt = addHours (new Date (), 2 );Create tournament author Sendou start times dateToDatabaseTimestamp (startsAt) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (startsAt)],
});Create tournament team tournament id tournament.id member user ids captain.id, partialMember.id, unknownMember.id, stranger.id
await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
memberUserIds: [
captain.id,
partialMember.id,
unknownMember.id,
stranger.id,
],
});{ startsAt: weekStartsAt } = Availability .weekRange ( startsAt, MACHINE_TIMEZONE , )
const { startsAt: weekStartsAt } = Availability .weekRange (
startsAt,
MACHINE_TIMEZONE ,
);coveringSlot = { startsAt: dateToDatabaseTimestamp (startsAt), endsAt: dateToDatabaseTimestam…
const coveringSlot = {
startsAt: dateToDatabaseTimestamp (startsAt),
endsAt: dateToDatabaseTimestamp (addHours (startsAt, 5 )),
};For each userId of captain.id, friend.id
for (const userId of [captain.id, friend.id]) {
await factories.AvailabilityWeekFactory .create ({
userId,
weekStartsAt,
timezone: MACHINE_TIMEZONE ,
slots: [coveringSlot],
});
}Create availability week userId weekStartsAt timezone MACHINE_TIMEZONE slots coveringSlot
await factories.AvailabilityWeekFactory .create ({
userId,
weekStartsAt,
timezone: MACHINE_TIMEZONE ,
slots: [coveringSlot],
});Create availability week user id partialMember.id weekStartsAt timezone MACHINE_TIMEZONE slots starts at dateToDatabaseTimestamp (addHours (startsAt, 1 )) ends at coveringSlot.endsAt
await factories.AvailabilityWeekFactory .create ({
userId: partialMember.id,
weekStartsAt,
timezone: MACHINE_TIMEZONE ,
slots: [
{
startsAt: dateToDatabaseTimestamp (addHours (startsAt, 1 )),
endsAt: coveringSlot.endsAt,
},
],
});Tournament register page Act
Open the tournament register page tournament.id
await register.goto (tournament.id);Assert
row = (userId: number ) => register.availabilityRow (userId)
const row = (userId: number ) => register.availabilityRow (userId);row (captain.id) has “data-status” = “available”
await expect (row (captain.id)).toHaveAttribute ("data-status" , "available" );row (partialMember.id) has “data-status” = “partial”
await expect (row (partialMember.id)).toHaveAttribute (
"data-status" ,
"partial" ,
);row (unknownMember.id) has “data-status” = “unknown”
await expect (row (unknownMember.id)).toHaveAttribute (
"data-status" ,
"unknown" ,
);on the tournament roster without being a teammate or a friend, so their schedule is not the viewer's to see
row (stranger.id) has “data-status” = “hidden”
await expect (row (stranger.id)).toHaveAttribute ("data-status" , "hidden" );Act
the friend with an overlapping submitted range is offered in quick add
Open quick add
await register.openQuickAdd ();Assert
row (friend.id) has “data-status” = “available”
await expect (row (friend.id)).toHaveAttribute ("data-status" , "available" );Source test ("shows the roster's availability for the event window" , async ({
page,
factories,
}) => {
const [captain, partialMember, unknownMember, stranger, friend] =
await factories.UserFactory .createMany (5 );
await factories.TeamFactory .create ({
memberUserIds: [captain.id, partialMember.id, unknownMember.id],
});
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});
const startsAt = addHours (new Date (), 2 );
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (startsAt)],
});
await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
memberUserIds: [
captain.id,
partialMember.id,
unknownMember.id,
stranger.id,
],
});
const { startsAt: weekStartsAt } = Availability .weekRange (
startsAt,
MACHINE_TIMEZONE ,
);
const coveringSlot = {
startsAt: dateToDatabaseTimestamp (startsAt),
endsAt: dateToDatabaseTimestamp (addHours (startsAt, 5 )),
};
for (const userId of [captain.id, friend.id]) {
await factories.AvailabilityWeekFactory .create ({
userId,
weekStartsAt,
timezone: MACHINE_TIMEZONE ,
slots: [coveringSlot],
});
}
await factories.AvailabilityWeekFactory .create ({
userId: partialMember.id,
weekStartsAt,
timezone: MACHINE_TIMEZONE ,
slots: [
{
startsAt: dateToDatabaseTimestamp (addHours (startsAt, 1 )),
endsAt: coveringSlot.endsAt,
},
],
});
await impersonate (page, captain.id);
await setTimezoneCookie (page);
const register = new TournamentRegisterPage (page);
await register.goto (tournament.id);
const row = (userId: number ) => register.availabilityRow (userId);
await expect (row (captain.id)).toHaveAttribute ("data-status" , "available" );
await expect (row (partialMember.id)).toHaveAttribute (
"data-status" ,
"partial" ,
);
await expect (row (unknownMember.id)).toHaveAttribute (
"data-status" ,
"unknown" ,
);
// on the tournament roster without being a teammate or a friend, so
// their schedule is not the viewer's to see
await expect (row (stranger.id)).toHaveAttribute ("data-status" , "hidden" );
// the friend with an overlapping submitted range is offered in quick add
await register.openQuickAdd ();
await expect (row (friend.id)).toHaveAttribute ("data-status" , "available" );
})registers a two player roster for a 2v2 tournament that takes no third member Renders/to/:id /to/:id/brackets /to/:id/info /to/:id/register
Tournament page Tournament register page Arrange
Create 3 × user → [captain, ...friends]
const [captain, ...friends] = await factories.UserFactory .createMany (3 );For each friend of friends
for (const friend of friends) {
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});
}Create friendship user one id captain.id user two id friend.id
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});Create tournament author Sendou start times dateToDatabaseTimestamp (addMinutes (new Date (), 30 )) min members per team 2 → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
// check-in opens an hour before the tournament starts
startTimes: [dateToDatabaseTimestamp (addMinutes (new Date (), 30 ))],
minMembersPerTeam: 2 ,
});the bracket preview needs an opponent to have anything to show
Create 2 × user → opponents
const opponents = await factories.UserFactory .createMany (2 );Create tournament team tournament id tournament.id team pickUpTeam ("Opponent" ) member user ids opponents.map ((user) => user.id) is checked in true
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
team: pickUpTeam ("Opponent" ),
memberUserIds: opponents.map ((user) => user.id),
},
{ isCheckedIn: true },
);Tournament page Act
Open the tournament page tournament.id
await tournamentPage.goto (tournament.id);Register → register
const register = await tournamentPage.register ();Tournament register page Act
Fill Pick-up name with “Chimera”
await register.form.fill ("pickUpName" , TEAM_NAME );Submit
await register.form.submit ();Assert
member 1 is visible
await expect (register.member (1 )).toBeVisible ();no subs footer “2v2” is visible
await expect (register.noSubsFooter ("2v2" )).toBeVisible ();Act
Add player
await register.addPlayer ();Assert
member 2 is visible
await expect (register.member (2 )).toBeVisible ();a 2v2 roster is full at two: no room for the second friend or anyone coming in through the invite link
add player button is not visible
await isNotVisible (register.locators.addPlayerButton);copy invite link button is not visible
await isNotVisible (register.locators.copyInviteLinkButton);Act
Check in
await register.checkIn ();Open brackets → brackets
const brackets = await register.openBrackets ();Assert
brackets.teamName (TEAM_NAME ).first () is visible
await expect (brackets.teamName (TEAM_NAME ).first ()).toBeVisible ();Source test ("registers a two player roster for a 2v2 tournament that takes no third member" , async ({
page,
factories,
}) => {
const [captain, ...friends] = await factories.UserFactory .createMany (3 );
for (const friend of friends) {
await factories.FriendshipFactory .create ({
userOneId: captain.id,
userTwoId: friend.id,
});
}
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
// check-in opens an hour before the tournament starts
startTimes: [dateToDatabaseTimestamp (addMinutes (new Date (), 30 ))],
minMembersPerTeam: 2 ,
});
// the bracket preview needs an opponent to have anything to show
const opponents = await factories.UserFactory .createMany (2 );
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
team: pickUpTeam ("Opponent" ),
memberUserIds: opponents.map ((user) => user.id),
},
{ isCheckedIn: true },
);
await impersonate (page, captain.id);
const tournamentPage = new TournamentPage (page);
await tournamentPage.goto (tournament.id);
const register = await tournamentPage.register ();
await register.form.fill ("pickUpName" , TEAM_NAME );
await register.form.submit ();
await expect (register.member (1 )).toBeVisible ();
await expect (register.noSubsFooter ("2v2" )).toBeVisible ();
await register.addPlayer ();
await expect (register.member (2 )).toBeVisible ();
// a 2v2 roster is full at two: no room for the second friend or anyone
// coming in through the invite link
await isNotVisible (register.locators.addPlayerButton);
await isNotVisible (register.locators.copyInviteLinkButton);
await register.checkIn ();
const brackets = await register.openBrackets ();
await expect (brackets.teamName (TEAM_NAME ).first ()).toBeVisible ();
})checks in and appears on the bracket Renders/to/:id /to/:id/brackets /to/:id/register
Tournament brackets page Tournament register page Notification popover Tournament register page Notification popover Tournament register page Arrange
Create tournament author Sendou start times dateToDatabaseTimestamp (addMinutes (new Date (), 30 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
// check-in opens an hour before the tournament starts
startTimes: [dateToDatabaseTimestamp (addMinutes (new Date (), 30 ))],
});Create 4 × user → roster
const roster = await factories.UserFactory .createMany (ROSTER_SIZE );Create tournament team tournament id tournament.id team pickUpTeam (TEAM_NAME ) member user ids roster.map ((user) => user.id)
await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
team: pickUpTeam (TEAM_NAME ),
memberUserIds: roster.map ((user) => user.id),
});Create notification notification type “TO_CHECK_IN_OPENED” meta tournament id tournament.id tournament name “In The Zone” users roster.map ((user) => ({ userId: user.id }))
await factories.NotificationFactory .create ({
notification: {
type : "TO_CHECK_IN_OPENED" ,
meta: { tournamentId: tournament.id, tournamentName: "In The Zone" },
},
users: roster.map ((user) => ({ userId: user.id })),
});Create 2 × user → opponents
const opponents = await factories.UserFactory .createMany (2 );For each [i, opponent] of opponents.entries ()
for (const [i, opponent] of opponents.entries ()) {
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
team: pickUpTeam (`Opponent ${i + 1}` ),
memberUserIds: [opponent.id],
},
{ isCheckedIn: true },
);
}Create tournament team tournament id tournament.id team pickUpTeam (`Opponent ${i + 1}` ) member user ids opponent.id is checked in true
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
team: pickUpTeam (`Opponent ${i + 1}` ),
memberUserIds: [opponent.id],
},
{ isCheckedIn: true },
);Tournament brackets page Act
Open the tournament brackets page tournament.id
await brackets.goto (tournament.id);Assert
team name “Chimera” is not visible
await isNotVisible (brackets.teamName (TEAM_NAME ));Tournament register page Act
Open the tournament register page tournament.id
await register.goto (tournament.id);Notification popover Assert
bell dot is visible
await expect (notifications.locators.bellDot).toBeVisible ();Tournament register page Act
Check in
await register.checkIn ();Notification popover Assert
checking in resolved the check-in notification without the bell having been opened
bell dot is hidden
await expect (notifications.locators.bellDot).toBeHidden ();Tournament register page Act
Open brackets → bracketsAfterCheckIn
const bracketsAfterCheckIn = await register.openBrackets ();Assert
bracketsAfterCheckIn.locators.bracketsViewer is visible
await expect (bracketsAfterCheckIn.locators.bracketsViewer).toBeVisible ();bracketsAfterCheckIn.teamName (TEAM_NAME ).first () is visible
await expect (
bracketsAfterCheckIn.teamName (TEAM_NAME ).first (),
).toBeVisible ();Source test ("checks in and appears on the bracket" , async ({ page, factories }) => {
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
// check-in opens an hour before the tournament starts
startTimes: [dateToDatabaseTimestamp (addMinutes (new Date (), 30 ))],
});
const roster = await factories.UserFactory .createMany (ROSTER_SIZE );
await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
team: pickUpTeam (TEAM_NAME ),
memberUserIds: roster.map ((user) => user.id),
});
await factories.NotificationFactory .create ({
notification: {
type : "TO_CHECK_IN_OPENED" ,
meta: { tournamentId: tournament.id, tournamentName: "In The Zone" },
},
users: roster.map ((user) => ({ userId: user.id })),
});
const opponents = await factories.UserFactory .createMany (2 );
for (const [i, opponent] of opponents.entries ()) {
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
team: pickUpTeam (`Opponent ${i + 1}` ),
memberUserIds: [opponent.id],
},
{ isCheckedIn: true },
);
}
await impersonate (page, roster[0 ].id);
const brackets = new TournamentBracketsPage (page);
await brackets.goto (tournament.id);
await isNotVisible (brackets.teamName (TEAM_NAME ));
const register = new TournamentRegisterPage (page);
await register.goto (tournament.id);
const notifications = new NotificationPopover (page);
await expect (notifications.locators.bellDot).toBeVisible ();
await register.checkIn ();
// checking in resolved the check-in notification without the bell
// having been opened
await expect (notifications.locators.bellDot).toBeHidden ();
const bracketsAfterCheckIn = await register.openBrackets ();
await expect (bracketsAfterCheckIn.locators.bracketsViewer).toBeVisible ();
await expect (
bracketsAfterCheckIn.teamName (TEAM_NAME ).first (),
).toBeVisible ();
})Renders/to/:id /to/:id/admin /to/:id/admin/seeds /to/:id/teams
Tournament seeds page Tournament teams page Arrange
Create tournament author Sendou start times dateToDatabaseTimestamp (addHours (new Date (), 2 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});Create 8 × user → captains
const captains = await factories.UserFactory .createMany (SEEDED_TEAM_COUNT );teams: Awaited < ReturnType <typeof factories.TournamentTeamFactory .create> >[] = []
const teams: Awaited <
ReturnType <typeof factories.TournamentTeamFactory .create>
>[] = [];Act
For each [i, captain] of captains.entries ()
for (const [i, captain] of captains.entries ()) {
teams.push (
await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
team: pickUpTeam (teamNameForSeed (i + 1 )),
memberUserIds: [captain.id],
}),
);
}Push factories.TournamentTeamFactory .create ({ tournamentId: tournament.id, team: p…
teams.push (
await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
team: pickUpTeam (teamNameForSeed (i + 1 )),
memberUserIds: [captain.id],
}),
); Tournament seeds page Act
Open the tournament seeds page tournament.id
await seeds.goto (tournament.id);Drag team down teams[0 ].id
await seeds.dragTeamDown (teams[0 ].id);Save
await seeds.save ();Tournament teams page Act
Open the tournament teams page tournament.id
await teamsPage.goto (tournament.id);Assert
first team names does not read teamNameForSeed (1 )
await expect (teamsPage.locators.teamNames.first ()).not.toHaveText (
teamNameForSeed (1 ),
);Source test ("adjusts seeds" , async ({ page, factories }) => {
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});
const captains = await factories.UserFactory .createMany (SEEDED_TEAM_COUNT );
const teams: Awaited <
ReturnType <typeof factories.TournamentTeamFactory .create>
>[] = [];
for (const [i, captain] of captains.entries ()) {
teams.push (
await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
team: pickUpTeam (teamNameForSeed (i + 1 )),
memberUserIds: [captain.id],
}),
);
}
await impersonate (page);
const seeds = new TournamentSeedsPage (page);
await seeds.goto (tournament.id);
await seeds.dragTeamDown (teams[0 ].id);
await seeds.save ();
const teamsPage = new TournamentTeamsPage (page);
await teamsPage.goto (tournament.id);
await expect (teamsPage.locators.teamNames.first ()).not.toHaveText (
teamNameForSeed (1 ),
);
})hides a draft tournament from non-organizers, including its loaders Renders/
Arrange
Create tournament author Sendou is draft true start times dateToDatabaseTimestamp (addHours (new Date (), 2 )) → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
isDraft: true ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});Create user → captain
const captain = await factories.UserFactory .create ();Create tournament team tournament id tournament.id team pickUpTeam (TEAM_NAME ) member user ids captain.id → team
const team = await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
team: pickUpTeam (TEAM_NAME ),
memberUserIds: [captain.id],
});Act
the draft's own pages never render, so flush the factory writes elsewhere
Go to “/”
await navigate ({ page, url: "/" });For each view of ...TOURNAMENT_TEAM_VIEWS , `teams/${team.id}`
for (const view of [...TOURNAMENT_TEAM_VIEWS , `teams/${team.id}` ]) {
const url = `/to/${tournament.id}/${view}` ;
const pageResponse = await page.request.fetch (url);
expect (pageResponse.status (), `${url} page` ).toBe (404 );
// each view's loader is also reachable on its own via single fetch
const dataResponse = await page.request.fetch (`${url}.data` );
expect (await dataResponse.text (), `${url}.data` ).not.toContain (TEAM_NAME );
}url = `/to/${tournament.id}/${view}`
const url = `/to/${tournament.id}/${view}` ;Fetch url → pageResponse
const pageResponse = await page.request.fetch (url);pageResponse.status () is 404
expect (pageResponse.status (), `${url} page` ).toBe (404 );each view's loader is also reachable on its own via single fetch
Fetch `${url}.data` → dataResponse
const dataResponse = await page.request.fetch (`${url}.data` );dataResponse.text () does not contain “Chimera”
expect (await dataResponse.text (), `${url}.data` ).not.toContain (TEAM_NAME );Fetch `/to/${tournament.id}/teams.data?_routes=features/tournament/routes/to.$id.te… → scopedToTeamsLoader
const scopedToTeamsLoader = await page.request.fetch (
`/to/${tournament.id}/teams.data?_routes=features/tournament/routes/to.$id.teams` ,
);Assert
scopedToTeamsLoader.text () does not contain “Chimera”
expect (await scopedToTeamsLoader.text ()).not.toContain (TEAM_NAME );Source test ("hides a draft tournament from non-organizers, including its loaders" , async ({
page,
factories,
}) => {
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
isDraft: true ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});
const captain = await factories.UserFactory .create ();
const team = await factories.TournamentTeamFactory .create ({
tournamentId: tournament.id,
team: pickUpTeam (TEAM_NAME ),
memberUserIds: [captain.id],
});
// the draft's own pages never render, so flush the factory writes elsewhere
await navigate ({ page, url: "/" });
for (const view of [...TOURNAMENT_TEAM_VIEWS , `teams/${team.id}` ]) {
const url = `/to/${tournament.id}/${view}` ;
const pageResponse = await page.request.fetch (url);
expect (pageResponse.status (), `${url} page` ).toBe (404 );
// each view's loader is also reachable on its own via single fetch
const dataResponse = await page.request.fetch (`${url}.data` );
expect (await dataResponse.text (), `${url}.data` ).not.toContain (TEAM_NAME );
}
const scopedToTeamsLoader = await page.request.fetch (
`/to/${tournament.id}/teams.data?_routes=features/tournament/routes/to.$id.teams` ,
);
expect (await scopedToTeamsLoader.text ()).not.toContain (TEAM_NAME );
})TournamentSeedsPage.gotoe2e/pages/tournament/tournament-seeds-page.ts:23
No JSDoc
async goto (tournamentId: number ) {
await navigate ({
page: this .page,
url: `${tournamentAdminPage(tournamentId)}/seeds` ,
});
}TournamentSeedsPage.openAbDivisionsDialoge2e/pages/tournament/tournament-seeds-page.ts:48
No JSDoc
async openAbDivisionsDialog () {
await this .locators.setAbDivisionsButton.click ();
}TournamentSeedsPage.locators.abDivisionRadioGroupse2e/pages/tournament/tournament-seeds-page.ts:19
No JSDoc
page.getByTestId ("ab-division-radio-group" ) TournamentSeedsPage.assignAbDivisione2e/pages/tournament/tournament-seeds-page.ts:67
No JSDoc
async assignAbDivision (nth: number , division: "A" | "B" ) {
await this .locators.abDivisionRadioGroups
.nth (nth)
.getByText (division, { exact: true })
.click ();
}TournamentSeedsPage.saveAbDivisionse2e/pages/tournament/tournament-seeds-page.ts:74
No JSDoc
saveAbDivisions () {
return submit (this .page, "set-ab-divisions-submit-button" );
}TournamentBracketsPage.gotoe2e/pages/tournament/tournament-brackets-page.ts:45
No JSDoc
async goto (tournamentId: number , bracketIdx?: number ) {
await navigate ({
page: this .page,
url: tournamentBracketsPage ({ tournamentId, bracketIdx }),
});
}TournamentBracketsPage.finalizee2e/pages/tournament/tournament-brackets-page.ts:184
async finalize () {
await this .page.getByTestId ("finalize-bracket-button" ).click ();
await submit (this .page, "confirm-finalize-bracket-button" );
}TournamentBracketsPage.locators.bracketsViewere2e/pages/tournament/tournament-brackets-page.ts:23
No JSDoc
page.getByTestId ("brackets-viewer" ) TournamentBracketsPage.locators.matchese2e/pages/tournament/tournament-brackets-page.ts:27
No JSDoc
page.locator ("[data-match-id]" ) TournamentBracketsPage.roundLabele2e/pages/tournament/tournament-brackets-page.ts:78
No JSDoc
roundLabel (roundNumber: number ) {
return this .page.getByText (`Round ${roundNumber}` , { exact: true });
}TournamentBracketsPage.locators.rrStandingsTablese2e/pages/tournament/tournament-brackets-page.ts:28
No JSDoc
page.getByTestId ("rr-standings-table" ) TournamentSeedsPagee2e/pages/tournament/tournament-seeds-page.ts:11
No JSDoc
TournamentBracketsPagee2e/pages/tournament/tournament-brackets-page.ts:14
No JSDoc
createInvitationale2e/tournament-invitational.spec.ts:98
No JSDoc
function createInvitational (factories: Factories ) {
return factories.TournamentFactory .create ({
authorId: NZAP_TEST_ID ,
isInvitational: true ,
startTimes: [dateToDatabaseTimestamp (addHours (new Date (), 2 ))],
});
}TournamentRegisterPage.gotoe2e/pages/tournament/tournament-register-page.ts:48
No JSDoc
async goto (tournamentId: number ) {
await navigate ({
page: this .page,
url: tournamentRegisterPage (tournamentId),
});
}TournamentRegisterPage.locators.registrationClosedAlerte2e/pages/tournament/tournament-register-page.ts:31
No JSDoc
page.getByText (
"Registration for this tournament has closed" ,
) TournamentAdminRegistrationPage.gotoNewe2e/pages/tournament/tournament-admin-registration-page.ts:40
No JSDoc
async gotoNew (tournamentId: number ) {
await navigate ({
page: this .page,
url: tournamentAdminRegistrationPage (tournamentId),
});
}TournamentAdminRegistrationPage.locators.addHeadinge2e/pages/tournament/tournament-admin-registration-page.ts:32
No JSDoc
page.getByRole ("heading" , { name: "Add new team" }) adminRegistrationFormSchema.pickUpNameapp/features/tournament-admin/tournament-admin-registration-schemas.ts:46
pickUpName: textFieldOptional ({
label: "labels.regTeamName" ,
maxLength: TOURNAMENT .TEAM_NAME_MAX_LENGTH ,
}) TournamentAdminRegistrationPage.selectPlayere2e/pages/tournament/tournament-admin-registration-page.ts:55
async selectPlayer (userName: string ) {
await selectUser ({ page: this .page, userName, labelName: "Player" });
}TournamentAdminRegistrationPage.selectCaptaine2e/pages/tournament/tournament-admin-registration-page.ts:78
No JSDoc
async selectCaptain (userId: number ) {
await this .page
.getByLabel ("Captain" , { exact: true })
.selectOption (String (userId));
}TournamentAdminRegistrationPage.savee2e/pages/tournament/tournament-admin-registration-page.ts:84
No JSDoc
save () {
return submit (this .page);
}TournamentAdminPage.teamNamee2e/pages/tournament/tournament-admin-page.ts:73
No JSDoc
teamName (name: string ) {
return this .locators.teamNames.filter ({ hasText: name });
}TournamentTeamsPage.gotoe2e/pages/tournament/tournament-teams-page.ts:17
No JSDoc
async goto (tournamentId: number ) {
await navigate ({ page: this .page, url: tournamentTeamsPage (tournamentId) });
}TournamentTeamsPage.teamNamede2e/pages/tournament/tournament-teams-page.ts:25
No JSDoc
teamNamed (name: string ) {
return this .locators.teamNames.getByText (name);
}TournamentTeamsPage.memberNamede2e/pages/tournament/tournament-teams-page.ts:21
No JSDoc
memberNamed (name: string ) {
return this .locators.teamMemberNames.getByText (name);
}TournamentRegisterPagee2e/pages/tournament/tournament-register-page.ts:17
No JSDoc
TournamentAdminRegistrationPagee2e/pages/tournament/tournament-admin-registration-page.ts:22
TournamentAdminPagee2e/pages/tournament/tournament-admin-page.ts:17
TournamentTeamsPagee2e/pages/tournament/tournament-teams-page.ts:5
No JSDoc
TournamentAdminRegistrationPage.addMembere2e/pages/tournament/tournament-admin-registration-page.ts:60
async addMember (userName: string ) {
await this .page.getByRole ("button" , { name: "Add" , exact: true }).click ();
await this .page.getByLabel ("Player" ).last ().click ();
await this .page.getByTestId ("user-search-input" ).fill (userName);
// clicking rather than pressing Enter: a result is in the DOM one commit
// before the effect that registers it and focuses it for Enter has run
await this .page
.getByRole ("listbox" )
.getByTestId ("user-search-item" )
.first ()
.click ();
}TournamentRegisterPage.locators.leaveTeamButtone2e/pages/tournament/tournament-register-page.ts:34
No JSDoc
page.getByRole ("button" , { name: "Leave the team" }) TournamentRegisterPage.locators.organizerAddedLeaveExplanatione2e/pages/tournament/tournament-register-page.ts:37
No JSDoc
page.getByText (
"You were added to the team by the organizer. Contact the TO to leave the team." ,
) TournamentRegisterPage.membere2e/pages/tournament/tournament-register-page.ts:55
No JSDoc
member (number : number ) {
return this .page.getByTestId (`member-num-${number}` );
}TournamentRegisterPage.locators.deleteMemberButtone2e/pages/tournament/tournament-register-page.ts:35
No JSDoc
page.getByRole ("button" , { name: "Delete member" }) TournamentRegisterPage.locators.unregisterButtone2e/pages/tournament/tournament-register-page.ts:36
No JSDoc
page.getByRole ("button" , { name: "Unregister" }) createLeagueSete2e/tournament-league.spec.ts:23
async function createLeagueSet (
factories: Parameters <Parameters <typeof test>[2 ]>[0 ]["factories" ],
) {
const tournament = await factories.TournamentFactory .create (
{
authorId: ADMIN_ID ,
startTimes: startedTournamentTimes (),
bracketProgression: ROUND_ROBIN ,
mapPoolMaps: TO_MAP_POOL ,
},
{ isLeague: true },
);
const [nzapTeam, opponentTeam] = await createTeams (factories, tournament.id, [
{ members: [NZAP_TEST_ID ] },
// … TournamentMatchPage.gotoe2e/pages/tournament/tournament-match-page.ts:108
No JSDoc
async goto ({
tournamentId,
matchId,
}: {
tournamentId: number ;
matchId: number ;
}) {
await navigate ({
page: this .page,
url: tournamentMatchPage ({ tournamentId, matchId }),
});
}TournamentMatchPage.locators.unscheduledBannere2e/pages/tournament/tournament-match-page.ts:67
No JSDoc
page.getByTestId ("league-unscheduled-banner" ) TournamentMatchPage.locators.stageBannere2e/pages/tournament/tournament-match-page.ts:42
No JSDoc
page.getByTestId ("stage-banner" ) isNotVisiblee2e/helpers/playwright.ts:745
No JSDoc
export function isNotVisible (locator: Locator ) {
return expect (locator).toHaveCount (0 );
}TournamentMatchPage.proposeTimee2e/pages/tournament/tournament-match-page.ts:82
async proposeTime (date: Date ) {
await expect (this .locators.scheduleTab).toBeVisible ();
await fillDateTimeField ({
scope: this .locators.scheduleTab,
label: "Time" ,
date,
});
await submit (this .page, "propose-times-button" );
}TournamentMatchPage.locators.ownCandidatese2e/pages/tournament/tournament-match-page.ts:70
No JSDoc
page.getByTestId ("own-candidates" ) TournamentMatchPage.locators.pickCandidateButtonse2e/pages/tournament/tournament-match-page.ts:73
No JSDoc
page.getByTestId ("pick-candidate-button" ) TournamentMatchPage.locators.opponentCandidatese2e/pages/tournament/tournament-match-page.ts:71
No JSDoc
page.getByTestId ("opponent-candidates" ) TournamentMatchPage.locators.candidateTimese2e/pages/tournament/tournament-match-page.ts:72
No JSDoc
page.getByTestId ("candidate-time" ) TournamentMatchPage.pickCandidatee2e/pages/tournament/tournament-match-page.ts:93
async pickCandidate (nth = 0 ) {
await this .locators.pickCandidateButtons.nth (nth).click ();
await submit (this .page, "confirm-button" );
}TournamentMatchPage.locators.agreedTimee2e/pages/tournament/tournament-match-page.ts:69
No JSDoc
page.getByTestId ("agreed-time" ) TournamentMatchPage.openTabe2e/pages/tournament/tournament-match-page.ts:121
No JSDoc
async openTab (tab: Tab ) {
// with more members than the minimum, the action tab is hidden until each
// team's active roster is locked in via the rosters tab
if (tab === "action" ) {
await this .ensureActiveRostersSet ();
}
await this .page.getByRole ("tab" , { name: TAB_LABELS [tab] }).click ();
}TournamentMatchPage.locators.rejectRescheduleButtone2e/pages/tournament/tournament-match-page.ts:74
No JSDoc
page.getByTestId ("reject-reschedule-button" ) TournamentMatchPage.organizerSetTimee2e/pages/tournament/tournament-match-page.ts:99
async organizerSetTime (date: Date ) {
await fillDateTimeField ({
scope: this .page.getByRole ("tabpanel" , { name: TAB_LABELS .admin }),
label: "Set time" ,
date,
});
await submit (this .page, "organizer-set-time-button" );
}TournamentMatchPage.locators.setByOrganizerTexte2e/pages/tournament/tournament-match-page.ts:77
No JSDoc
page.getByText ("Set by the organizer" ) TournamentMatchPage.locators.proposeTimesButtone2e/pages/tournament/tournament-match-page.ts:75
No JSDoc
page.getByTestId ("propose-times-button" ) TournamentMatchPage.reportResulte2e/pages/tournament/tournament-match-page.ts:154
async reportResult ({
mapsToReport,
winner = 1 ,
setEnds = true ,
}: {
mapsToReport: number ;
winner?: Side ;
setEnds?: boolean ;
}) {
for (let i = 0 ; i < mapsToReport; i++) {
const isFinal = setEnds && i === mapsToReport - 1 ;
// the loader revalidation swapping in the next map runs after the POST, so a
// previous winner can still be selected; clicking the about-to-unmount label
// loses the selection on remount
// … TournamentMatchPage.locators.finalBannere2e/pages/tournament/tournament-match-page.ts:43
No JSDoc
page.getByTestId ("match-final" ) TournamentMatchPagee2e/pages/tournament/tournament-match-page.ts:28
createTeamse2e/helpers/tournament.ts:232
export async function createTeams (
factories: Factories ,
tournamentId: number ,
seeds: TeamSeed [],
) {
const teams: Awaited <
ReturnType <typeof factories.TournamentTeamFactory .create>
>[] = [];
for (const [i, seed] of seeds.entries ()) {
const presetMembers = seed.members ?? [];
const rosterSize = seed.rosterSize ?? ROSTER_SIZE ;
const fillerUsers = await factories.UserFactory .createMany (
rosterSize - presetMembers.length,
);
// … TournamentMatchPage.locators.scheduleTabe2e/pages/tournament/tournament-match-page.ts:68
No JSDoc
page.getByTestId ("schedule-tab" ) TournamentDivisionsPage.gotoe2e/pages/tournament/tournament-divisions-page.ts:18
No JSDoc
async goto (tournamentId: number ) {
await navigate ({
page: this .page,
url: tournamentDivisionsPage (tournamentId),
});
}TournamentDivisionsPage.locators.divisionLinkse2e/pages/tournament/tournament-divisions-page.ts:14
No JSDoc
page.getByTestId ("division-link" ) TournamentDivisionsPagee2e/pages/tournament/tournament-divisions-page.ts:7
TournamentAdminPage.gotoe2e/pages/tournament/tournament-admin-page.ts:50
No JSDoc
async goto (tournamentId: number ) {
await navigate ({ page: this .page, url: tournamentAdminPage (tournamentId) });
}TournamentAdminPage.openStaffe2e/pages/tournament/tournament-admin-page.ts:63
No JSDoc
async openStaff () {
await this .adminTab ("Staff" ).click ();
return new TournamentAdminStaffPage (this .page);
}TournamentAdminStaffPage.locators.authorRowe2e/pages/tournament/tournament-admin-staff-page.ts:17
No JSDoc
page.getByTestId ("staff-author" ) TournamentAdminStaffPage.addStaffere2e/pages/tournament/tournament-admin-staff-page.ts:46
async addStaffer (username: string , role?: string ) {
await this .edit ();
await selectUser ({
page: this .page,
userName: username,
labelName: "User" ,
});
if (role) {
await this .page.getByLabel ("Role" , { exact: true }).selectOption (role);
}
await this .save ();
}TournamentAdminStaffPage.staffRowe2e/pages/tournament/tournament-admin-staff-page.ts:29
No JSDoc
staffRow (username: string ) {
return this .page.getByTestId (`staff-row-${username}` );
}TournamentAdminStaffPage.removeStaffere2e/pages/tournament/tournament-admin-staff-page.ts:59
No JSDoc
async removeStaffer () {
await this .edit ();
await this .page.getByRole ("button" , { name: "Remove item" }).click ();
await this .save ();
}TournamentAdminStaffPagee2e/pages/tournament/tournament-admin-staff-page.ts:10
TournamentAdminPage.adminTabe2e/pages/tournament/tournament-admin-page.ts:59
No JSDoc
adminTab (name: string ) {
return this .page.getByRole ("tab" , { name });
}TournamentMatchPage.locators.roomPasse2e/pages/tournament/tournament-match-page.ts:37
No JSDoc
page.getByTestId ("room-pass" ) TournamentAdminPage.openStreame2e/pages/tournament/tournament-admin-page.ts:68
No JSDoc
async openStream () {
await this .adminTab ("Stream" ).click ();
return new TournamentAdminStreamPage (this .page);
}TournamentAdminStreamPage.fillAccounte2e/pages/tournament/tournament-admin-stream-page.ts:30
async fillAccount (nth: number , account: string ) {
await this .accountInput (nth).fill (account);
}TournamentAdminStreamPage.addAccountFielde2e/pages/tournament/tournament-admin-stream-page.ts:34
No JSDoc
async addAccountField () {
await this .locators.addAccountButton.click ();
}TournamentAdminStreamPage.savee2e/pages/tournament/tournament-admin-stream-page.ts:38
No JSDoc
save () {
return submit (this .page, "save-cast-accounts-button" );
}TournamentAdminStreamPage.gotoe2e/pages/tournament/tournament-admin-stream-page.ts:18
No JSDoc
async goto (tournamentId: number ) {
await navigate ({
page: this .page,
url: `${tournamentAdminPage(tournamentId)}/stream` ,
});
}TournamentAdminStreamPage.accountInpute2e/pages/tournament/tournament-admin-stream-page.ts:25
No JSDoc
accountInput (nth: number ) {
return this .locators.accountInputs.nth (nth);
}TournamentAdminStreamPagee2e/pages/tournament/tournament-admin-stream-page.ts:6
createStartableTournamente2e/tournament-streams.spec.ts:168
async function createStartableTournament (factories: Factories ) {
const tournament = await factories.TournamentFactory .create ({
authorId: NZAP_TEST_ID ,
startTimes: [dateToDatabaseTimestamp (subMinutes (new Date (), 30 ))],
});
const players = await factories.UserFactory .createMany (ROSTER_SIZE * 2 );
for (const roster of [
players.slice (0 , ROSTER_SIZE ),
players.slice (ROSTER_SIZE ),
]) {
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: roster.map ((user) => user.id),
// … TournamentBracketsPage.openMatche2e/pages/tournament/tournament-brackets-page.ts:207
No JSDoc
async openMatch (matchId: number ) {
await expect (async () => {
await this .match (matchId).click ();
await expect (
this .page.getByTestId ("back-to-bracket-button" ),
).toBeVisible ();
}).toPass ();
// a click that beat hydration loads the match page as a new document
await expectIsHydrated (this .page);
return new TournamentMatchPage (this .page);
}TournamentMatchPage.backToBrackete2e/pages/tournament/tournament-match-page.ts:316
No JSDoc
async backToBracket () {
await this .locators.backToBracketButton.click ();
await expect (this .page.getByTestId ("brackets-viewer" )).toBeVisible ();
return new TournamentBracketsPage (this .page);
}TournamentBracketsPage.locators.liveBadgese2e/pages/tournament/tournament-brackets-page.ts:29
No JSDoc
page.getByTestId ("brackets-viewer" ).getByText ("LIVE" ) TournamentBracketsPage.locators.streamPopovere2e/pages/tournament/tournament-brackets-page.ts:31
No JSDoc
page.getByTestId ("stream-popover" ) TournamentBracketsPage.locators.streamPopoverStreamse2e/pages/tournament/tournament-brackets-page.ts:32
No JSDoc
page.getByTestId ("tournament-stream" ) TournamentStreamsPage.gotoe2e/pages/tournament/tournament-streams-page.ts:21
No JSDoc
async goto (tournamentId: number ) {
await navigate ({
page: this .page,
url: tournamentStreamsPage (tournamentId),
});
}TournamentStreamsPage.locators.streamse2e/pages/tournament/tournament-streams-page.ts:13
No JSDoc
page.getByTestId ("tournament-stream" ) TournamentStreamsPage.viewerCounte2e/pages/tournament/tournament-streams-page.ts:32
No JSDoc
viewerCount (count: number ) {
return this .page.getByText (String (count), { exact: true });
}TournamentStreamsPagee2e/pages/tournament/tournament-streams-page.ts:6
TournamentStreamsPage.platformLinke2e/pages/tournament/tournament-streams-page.ts:28
No JSDoc
platformLink (platformName: "Twitch" | "YouTube" ) {
return this .page.getByRole ("link" , { name: platformName, exact: true });
}TournamentStreamsPage.locators.helpButtone2e/pages/tournament/tournament-streams-page.ts:14
No JSDoc
page.getByRole ("button" , { name: "Stream not showing up?" }) TournamentStreamsPage.locators.helpDialoge2e/pages/tournament/tournament-streams-page.ts:15
No JSDoc
page.getByRole ("dialog" , {
name: "Getting your stream shown" ,
}) TournamentMatchPage.setCastedBye2e/pages/tournament/tournament-match-page.ts:299
setCastedBy (twitchAccount: string ) {
return waitForPOSTResponse (this .page, async () => {
await this .page.locator (`label[for$="-TWITCH:${twitchAccount}"]` ).click ();
});
}CalendarPage.gotoe2e/pages/calendar/calendar-page.ts:35
async goto (dayMonthYear?: DayMonthYear ) {
await navigate ({ page: this .page, url: calendarPage ({ dayMonthYear }) });
}CalendarPage.tentativeTierPille2e/pages/calendar/calendar-page.ts:43
No JSDoc
tentativeTierPill (tournamentName: string ) {
return this .tournamentCard (tournamentName).getByTestId ("tentative-tier" );
}CalendarPage.confirmedTierPille2e/pages/calendar/calendar-page.ts:47
No JSDoc
confirmedTierPill (tournamentName: string ) {
return this .tournamentCard (tournamentName).getByTestId ("confirmed-tier" );
}CalendarPagee2e/pages/calendar/calendar-page.ts:14
TournamentPage.gotoe2e/pages/tournament/tournament-page.ts:21
No JSDoc
async goto (tournamentId: number ) {
await navigate ({ page: this .page, url: tournamentPage (tournamentId) });
}TournamentPage.registere2e/pages/tournament/tournament-page.ts:33
No JSDoc
async register () {
await this .locators.registerCta.click ();
return new TournamentRegisterPage (this .page);
}registerTeamFormSchema.pickUpNameapp/features/tournament/tournament-register-schemas.ts:17
pickUpName: textFieldOptional ({
label: "labels.regPickUpName" ,
maxLength: TOURNAMENT .TEAM_NAME_MAX_LENGTH ,
}) TournamentRegisterPage.addPlayere2e/pages/tournament/tournament-register-page.ts:81
No JSDoc
addPlayer () {
return submit (this .page, "add-player-button" );
}TournamentRegisterPage.pickCounterpickMapse2e/pages/tournament/tournament-register-page.ts:92
pickCounterpickMaps () {
return pickCounterpickMaps (this .page);
}TournamentRegisterPage.saveCounterpickMapse2e/pages/tournament/tournament-register-page.ts:96
No JSDoc
saveCounterpickMaps () {
return submit (this .page, "save-map-list-button" );
}TournamentRegisterPage.stepCheckmarke2e/pages/tournament/tournament-register-page.ts:73
No JSDoc
stepCheckmark (number : number ) {
return this .page.getByTestId (`checkmark-icon-num-${number}` );
}NotificationPopover.opene2e/pages/layout/notification-popover.ts:37
No JSDoc
async open () {
// the popover is closed on navigation, which would also close one opened
// after a click on a notification navigated but before the page rendered
await waitForNavigationRendered (this .page);
await expect (this .locators.shownPopover).toHaveCount (0 );
await this .locators.openButton.click ();
await expect (this .locators.shownPopover).toHaveCount (1 );
}NotificationPopover.notificatione2e/pages/layout/notification-popover.ts:50
No JSDoc
notification (text: string ) {
return this .locators.items.filter ({ hasText: text });
}TournamentPagee2e/pages/tournament/tournament-page.ts:7
No JSDoc
NotificationPopovere2e/pages/layout/notification-popover.ts:6
TournamentPage.locators.estimatedEnde2e/pages/tournament/tournament-page.ts:17
No JSDoc
page.getByTestId ("estimated-end" ) TournamentRegisterPage.openQuickAdde2e/pages/tournament/tournament-register-page.ts:64
async openQuickAdd () {
await this .page.getByTestId ("quick-add-select" ).getByRole ("button" ).click ();
}TournamentRegisterPage.availabilityRowe2e/pages/tournament/tournament-register-page.ts:59
No JSDoc
availabilityRow (userId: number ) {
return this .page.getByTestId (`availability-row-${userId}` );
}TournamentRegisterPage.addAllTeamPlayerse2e/pages/tournament/tournament-register-page.ts:86
async addAllTeamPlayers (teamId: number ) {
await this .page.getByTestId (`add-team-players-button-${teamId}` ).click ();
await modalClickConfirmButton (this .page);
}TournamentRegisterPage.noSubsFootere2e/pages/tournament/tournament-register-page.ts:69
noSubsFooter (format: string ) {
return this .page.getByText (`Format is ${format}. No subs allowed.` );
}TournamentRegisterPage.locators.addPlayerButtone2e/pages/tournament/tournament-register-page.ts:40
No JSDoc
page.getByTestId ("add-player-button" ) TournamentRegisterPage.locators.copyInviteLinkButtone2e/pages/tournament/tournament-register-page.ts:42
No JSDoc
page.getByRole ("button" , {
name: "Copy to clipboard" ,
}) TournamentRegisterPage.checkIne2e/pages/tournament/tournament-register-page.ts:100
No JSDoc
checkIn () {
return submit (this .page, "check-in-button" );
}TournamentRegisterPage.openBracketse2e/pages/tournament/tournament-register-page.ts:104
No JSDoc
openBrackets () {
return this .nav.openBrackets ();
}TournamentBracketsPage.teamNamee2e/pages/tournament/tournament-brackets-page.ts:57
No JSDoc
teamName (name: string ) {
return this .page.getByText (name);
}NotificationPopover.locators.bellDote2e/pages/layout/notification-popover.ts:29
this .page.getByTestId ("notifications-bell-dot" )TournamentSeedsPage.dragTeamDowne2e/pages/tournament/tournament-seeds-page.ts:35
async dragTeamDown (tournamentTeamId: number ) {
await this .teamHandle (tournamentTeamId).hover ();
await this .page.mouse.down ();
// the drag & drop library only registers the drop when moved in steps
await this .page.mouse.move (0 , DRAG_TARGET_Y , { steps: 10 });
await this .page.mouse.up ();
await waitForDropToSettle (this .page);
}TournamentSeedsPage.savee2e/pages/tournament/tournament-seeds-page.ts:44
No JSDoc
save () {
return submit (this .page);
}TournamentTeamsPage.locators.teamNamese2e/pages/tournament/tournament-teams-page.ts:12
No JSDoc
page.getByTestId ("team-name" )