Calendar applies filters and operates hidden events toggle Renders/calendar
Calendar page Arrange
all of them at the same time so they share one clock header and its toggle
startTimes = [databaseTimestampNow ()]
const startTimes = [databaseTimestampNow ()];Repeat for (let i = 0 ; i < SENDOU_INK_TOURNAMENTS_COUNT ; i++)
for (let i = 0 ; i < SENDOU_INK_TOURNAMENTS_COUNT ; i++) {
await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes,
});
}Create tournament author Sendou startTimes
await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes,
});Repeat for (let i = 0 ; i < EXTERNAL_EVENTS_COUNT ; i++)
for (let i = 0 ; i < EXTERNAL_EVENTS_COUNT ; i++) {
await factories.CalendarEventFactory .create ({
authorId: ADMIN_ID ,
startTimes,
});
}Create calendar event author Sendou startTimes
await factories.CalendarEventFactory .create ({
authorId: ADMIN_ID ,
startTimes,
});Calendar page Act
Open the calendar page
await calendar.goto ();Toggle event type filter “isSendou”
await calendar.toggleEventTypeFilter ("isSendou" );Assert
tournament cards count is 3
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT ,
);Act
Reload
await calendar.reload ();Assert
remembers selection via search params
tournament cards count is 3
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT ,
);Act
Toggle hidden events
await calendar.toggleHiddenEvents ();Assert
tournament cards count is SENDOU_INK_TOURNAMENTS_COUNT + EXTERNAL_EVENTS_COUNT
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT + EXTERNAL_EVENTS_COUNT ,
);Act
Toggle hidden events
await calendar.toggleHiddenEvents ();Assert
tournament cards count is 3
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT ,
);Source test ("applies filters and operates hidden events toggle" , async ({
page,
factories,
}) => {
// all of them at the same time so they share one clock header and its toggle
const startTimes = [databaseTimestampNow ()];
for (let i = 0 ; i < SENDOU_INK_TOURNAMENTS_COUNT ; i++) {
await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
startTimes,
});
}
for (let i = 0 ; i < EXTERNAL_EVENTS_COUNT ; i++) {
await factories.CalendarEventFactory .create ({
authorId: ADMIN_ID ,
startTimes,
});
}
await impersonate (page, NZAP_TEST_ID );
const calendar = new CalendarPage (page);
await calendar.goto ();
await calendar.toggleEventTypeFilter ("isSendou" );
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT ,
);
await calendar.reload ();
// remembers selection via search params
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT ,
);
await calendar.toggleHiddenEvents ();
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT + EXTERNAL_EVENTS_COUNT ,
);
await calendar.toggleHiddenEvents ();
await expect (calendar.locators.tournamentCards).toHaveCount (
SENDOU_INK_TOURNAMENTS_COUNT ,
);
})Renders/calendar
Calendar page Arrange
not hosted on sendou.ink, so the ranked filter hides it
Create calendar event author Sendou
await factories.CalendarEventFactory .create ({ authorId: ADMIN_ID });Calendar page Act
Open the calendar page
await calendar.goto ();Assert
hidden events buttons is not visible
await isNotVisible (calendar.locators.hiddenEventsButtons);Act
Toggle event type filter “isRanked”
await calendar.toggleEventTypeFilter ("isRanked" );Save filters as default
await calendar.saveFiltersAsDefault ();Assert
first hidden events buttons is visible
await expect (calendar.locators.hiddenEventsButtons.first ()).toBeVisible ();Act
Open the calendar page
await calendar.goto ();Assert
remembers selection via user preferences
first hidden events buttons is visible
await expect (calendar.locators.hiddenEventsButtons.first ()).toBeVisible ();Act
Remove event type filter
await calendar.removeEventTypeFilter ();Assert
removing the filter sticks instead of falling back to the saved default
hidden events buttons is not visible
await isNotVisible (calendar.locators.hiddenEventsButtons);Act
Reload
await calendar.reload ();Assert
hidden events buttons is not visible
await isNotVisible (calendar.locators.hiddenEventsButtons);Source test ("sets default filters" , async ({ page, factories }) => {
// not hosted on sendou.ink, so the ranked filter hides it
await factories.CalendarEventFactory .create ({ authorId: ADMIN_ID });
await impersonate (page, NZAP_TEST_ID );
const calendar = new CalendarPage (page);
await calendar.goto ();
await isNotVisible (calendar.locators.hiddenEventsButtons);
await calendar.toggleEventTypeFilter ("isRanked" );
await calendar.saveFiltersAsDefault ();
await expect (calendar.locators.hiddenEventsButtons.first ()).toBeVisible ();
await calendar.goto ();
// remembers selection via user preferences
await expect (calendar.locators.hiddenEventsButtons.first ()).toBeVisible ();
await calendar.removeEventTypeFilter ();
// removing the filter sticks instead of falling back to the saved default
await isNotVisible (calendar.locators.hiddenEventsButtons);
await calendar.reload ();
await isNotVisible (calendar.locators.hiddenEventsButtons);
})Renders/calendar
Calendar page Calendar page Act
Open the calendar page
await calendar.goto ();Navigate previous
await calendar.navigatePrevious ();Assert
today header is not visible
await isNotVisible (calendar.locators.todayHeader);Act
Navigate next
await calendar.navigateNext ();Assert
today header is visible
await expect (calendar.locators.todayHeader).toBeVisible ();Source test ("navigates view more buttons" , async ({ page }) => {
const calendar = new CalendarPage (page);
await calendar.goto ();
await calendar.navigatePrevious ();
await isNotVisible (calendar.locators.todayHeader);
await calendar.navigateNext ();
await expect (calendar.locators.todayHeader).toBeVisible ();
})Arrange
Create calendar event author Sendou
await factories.CalendarEventFactory .create ({ authorId: ADMIN_ID });openWith = async (locale: string ) => { const context = await browser.newContext ({ locale…
const openWith = async (locale: string ) => {
const context = await browser.newContext ({
locale,
baseURL: workerBaseURL,
});
const page = await context.newPage ();
return { context, page };
};Act
Open with “en-CA” → ca
const ca = await openWith ("en-CA" );Open with “en-GB” → gb
const gb = await openWith ("en-GB" );Try
try {
const caCalendar = new CalendarPage (ca.page);
const gbCalendar = new CalendarPage (gb.page);
await caCalendar.goto ();
await gbCalendar.goto ();
const caTime = await caCalendar.locators.clockHeaderTimes
.first ()
.textContent ();
const gbTime = await gbCalendar.locators.clockHeaderTimes
.first ()
.textContent ();
expect (caTime).toMatch (/AM |PM |a\.m\.|p\.m\./i);
expect (gbTime).not.toMatch (/AM |PM |a\.m\.|p\.m\./i);
expect (caTime).not.toBe (gbTime);
} finally {
await ca.context.close ();
await gb.context.close ();
}Open the calendar page
await caCalendar.goto ();Open the calendar page
await gbCalendar.goto ();Text content → caTime
const caTime = await caCalendar.locators.clockHeaderTimes
.first ()
.textContent ();Text content → gbTime
const gbTime = await gbCalendar.locators.clockHeaderTimes
.first ()
.textContent ();caTime match /AM |PM |a\.m\.|p\.m\./i
expect (caTime).toMatch (/AM |PM |a\.m\.|p\.m\./i);gbTime not match /AM |PM |a\.m\.|p\.m\./i
expect (gbTime).not.toMatch (/AM |PM |a\.m\.|p\.m\./i);caTime is not gbTime
expect (caTime).not.toBe (gbTime);Source test ("renders clock header times in the browser locale" , async ({
browser,
workerBaseURL,
factories,
}) => {
await factories.CalendarEventFactory .create ({ authorId: ADMIN_ID });
const openWith = async (locale: string ) => {
const context = await browser.newContext ({
locale,
baseURL: workerBaseURL,
});
const page = await context.newPage ();
return { context, page };
};
const ca = await openWith ("en-CA" );
const gb = await openWith ("en-GB" );
try {
const caCalendar = new CalendarPage (ca.page);
const gbCalendar = new CalendarPage (gb.page);
await caCalendar.goto ();
await gbCalendar.goto ();
const caTime = await caCalendar.locators.clockHeaderTimes
.first ()
.textContent ();
const gbTime = await gbCalendar.locators.clockHeaderTimes
.first ()
.textContent ();
expect (caTime).toMatch (/AM |PM |a\.m\.|p\.m\./i);
expect (gbTime).not.toMatch (/AM |PM |a\.m\.|p\.m\./i);
expect (caTime).not.toBe (gbTime);
} finally {
await ca.context.close ();
await gb.context.close ();
}
})creates, edits and deletes a calendar event Renders/calendar /calendar/:id /calendar/new
Calendar new event page Calendar event page Calendar new event page Calendar event page Calendar page Calendar event page Calendar page Calendar new event page Act
Open the calendar new event page
await newEvent.goto ();Fill Name with “Test Calendar Event”
await newEvent.form.fill ("name" , "Test Calendar Event" );Set first date new Date (2027 , 0 , 15 , 17 , 0 )
await newEvent.setFirstDate (new Date (2027 , 0 , 15 , 17 , 0 ));Fill Bracket URL with “https://sendou.ink/test-bracket”
await newEvent.form.fill ("bracketUrl" , "https://sendou.ink/test-bracket" );Submit
await newEvent.form.submit ();Assert
the page URL is /\/calendar\/\d+/
await expect (page).toHaveURL (/\/calendar\/\d+/);Act
eventId = Number (page.url ().match (/\/calendar\/(\d+)/)?.[1 ])
const eventId = Number (page.url ().match (/\/calendar\/(\d+)/)?.[1 ]);editedDate = new Date (2027 , 0 , 20 , 18 , 0 )
const editedDate = new Date (2027 , 0 , 20 , 18 , 0 );Calendar event page Act
Open edit → editEvent
const editEvent = await calendarEvent.openEdit ();Calendar new event page Act
Fill Name with “Renamed Calendar Event”
await editEvent.form.fill ("name" , "Renamed Calendar Event" );Set first date editedDate
await editEvent.setFirstDate (editedDate);Save
await editEvent.save ();Assert
the page URL is calendarEventPage (eventId)
await expect (page).toHaveURL (calendarEventPage (eventId));Calendar event page Assert
start time editedDate is visible
await expect (calendarEvent.startTime (editedDate)).toBeVisible ();Act
editedDateWeek = { day: 20 , month: 0 , year: 2027 }
const editedDateWeek = { day: 20 , month: 0 , year: 2027 };Calendar page Act
Open the calendar page editedDateWeek
await calendar.goto (editedDateWeek);Assert
tournament card “Renamed Calendar Event” is visible
await expect (
calendar.tournamentCard ("Renamed Calendar Event" ),
).toBeVisible ();Calendar event page Act
Open the calendar event page eventId
await calendarEvent.goto (eventId);Delete
await calendarEvent.delete ();Assert
the page URL is /\/calendar$/
await expect (page).toHaveURL (/\/calendar$/);Calendar page Act
Open the calendar page editedDateWeek
await calendar.goto (editedDateWeek);Assert
tournament card “Renamed Calendar Event” is not visible
await isNotVisible (calendar.tournamentCard ("Renamed Calendar Event" ));Source test ("creates, edits and deletes a calendar event" , async ({ page }) => {
await impersonate (page);
const newEvent = new CalendarNewEventPage (page);
await newEvent.goto ();
await newEvent.form.fill ("name" , "Test Calendar Event" );
await newEvent.setFirstDate (new Date (2027 , 0 , 15 , 17 , 0 ));
await newEvent.form.fill ("bracketUrl" , "https://sendou.ink/test-bracket" );
await newEvent.form.submit ();
await expect (page).toHaveURL (/\/calendar\/\d+/);
const eventId = Number (page.url ().match (/\/calendar\/(\d+)/)?.[1 ]);
const calendarEvent = new CalendarEventPage (page);
const editedDate = new Date (2027 , 0 , 20 , 18 , 0 );
const editEvent = await calendarEvent.openEdit ();
await editEvent.form.fill ("name" , "Renamed Calendar Event" );
await editEvent.setFirstDate (editedDate);
await editEvent.save ();
await expect (page).toHaveURL (calendarEventPage (eventId));
await expect (calendarEvent.startTime (editedDate)).toBeVisible ();
const calendar = new CalendarPage (page);
const editedDateWeek = { day: 20 , month: 0 , year: 2027 };
await calendar.goto (editedDateWeek);
await expect (
calendar.tournamentCard ("Renamed Calendar Event" ),
).toBeVisible ();
await calendarEvent.goto (eventId);
await calendarEvent.delete ();
await expect (page).toHaveURL (/\/calendar$/);
await calendar.goto (editedDateWeek);
await isNotVisible (calendar.tournamentCard ("Renamed Calendar Event" ));
})creates a new tournament with a map pool and follow-up bracket Renders/calendar/new /to/:id /to/:id/brackets /to/:id/info /to/:id/rules
Calendar new event page Tournament info page Tournament brackets page Tournament rules page Arrange
tournaments can only be added by an organizer, unlike calendar events
Create user null roles “TOURNAMENT_ORGANIZER” → organizer
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});Calendar new event page Act
Go to new tournament
await newTournament.gotoNewTournament ();startTime = new Date (2027 , 0 , 15 , 17 , 0 )
const startTime = new Date (2027 , 0 , 15 , 17 , 0 );mapPool = [ { stage: "Scorch Gorge" , mode: "Splat Zones" }, { stage: "Eeltail Alley" , m…
const mapPool = [
{ stage: "Scorch Gorge" , mode: "Splat Zones" },
{ stage: "Eeltail Alley" , mode: "Tower Control" },
{ stage: "Hagglefish Market" , mode: "Rainmaker" },
];Fill Name with “Test Tournament”
await newTournament.form.fill ("name" , "Test Tournament" );Fill Description with “An automated test tournament”
await newTournament.form.fill (
"description" ,
"An automated test tournament" ,
);Set first date startTime
await newTournament.setFirstDate (startTime);Fill Discord server invite URL with “test-invite”
await newTournament.form.fill ("discordInviteCode" , "test-invite" );flip a tournament setting away from its default
Go to step “teams”
await newTournament.goToStep ("teams" );Check Require in-game names
await newTournament.form.check ("requireInGameNames" );"Organizer picked" allows an arbitrary map pool, unlike the validated team pick pools
Go to step “maps”
await newTournament.goToStep ("maps" );Check items map picking style “TO”
await newTournament.form.checkItems ("mapPickingStyle" , ["TO" ]);Pick map pool mapPool
await newTournament.pickMapPool (mapPool);Add follow up bracket name “Underground bracket” format “Single elimination” placements -1
await newTournament.addFollowUpBracket ({
name: "Underground bracket" ,
format: "Single elimination" ,
placements: [-1 ],
});Save
await newTournament.save ();Assert
the page URL is /\/to\/\d+/
await expect (page).toHaveURL (/\/to\/\d+/);Act
tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ])
const tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ]);Tournament info page Act
Open the tournament info page tournamentId
await tournamentInfo.goto (tournamentId);Assert
first start time startTime is visible
await expect (tournamentInfo.startTime (startTime).first ()).toBeVisible ();Tournament brackets page Act
Open the tournament brackets page tournamentId
await brackets.goto (tournamentId);Assert
bracket tab “Underground” is visible
await expect (brackets.bracketTab ("Underground" )).toBeVisible ();Tournament rules page Act
Open the tournament rules page tournamentId
await rules.goto (tournamentId);Assert
For each { stage, mode } of mapPool
for (const { stage, mode } of mapPool) {
await expect (rules.stageName (stage)).toBeVisible ();
await expect (rules.modeImage (mode).first ()).toBeVisible ();
}stage name stage is visible
await expect (rules.stageName (stage)).toBeVisible ();first mode image mode is visible
await expect (rules.modeImage (mode).first ()).toBeVisible ();Source test ("creates a new tournament with a map pool and follow-up bracket" , async ({
page,
factories,
}) => {
// tournaments can only be added by an organizer, unlike calendar events
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});
await impersonate (page, organizer.id);
const newTournament = new CalendarNewEventPage (page);
await newTournament.gotoNewTournament ();
const startTime = new Date (2027 , 0 , 15 , 17 , 0 );
const mapPool = [
{ stage: "Scorch Gorge" , mode: "Splat Zones" },
{ stage: "Eeltail Alley" , mode: "Tower Control" },
{ stage: "Hagglefish Market" , mode: "Rainmaker" },
];
await newTournament.form.fill ("name" , "Test Tournament" );
await newTournament.form.fill (
"description" ,
"An automated test tournament" ,
);
await newTournament.setFirstDate (startTime);
await newTournament.form.fill ("discordInviteCode" , "test-invite" );
// flip a tournament setting away from its default
await newTournament.goToStep ("teams" );
await newTournament.form.check ("requireInGameNames" );
// "Organizer picked" allows an arbitrary map pool, unlike the validated team pick pools
await newTournament.goToStep ("maps" );
await newTournament.form.checkItems ("mapPickingStyle" , ["TO" ]);
await newTournament.pickMapPool (mapPool);
await newTournament.addFollowUpBracket ({
name: "Underground bracket" ,
format: "Single elimination" ,
placements: [-1 ],
});
await newTournament.save ();
await expect (page).toHaveURL (/\/to\/\d+/);
const tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ]);
const tournamentInfo = new TournamentInfoPage (page);
await tournamentInfo.goto (tournamentId);
await expect (tournamentInfo.startTime (startTime).first ()).toBeVisible ();
const brackets = new TournamentBracketsPage (page);
await brackets.goto (tournamentId);
await expect (brackets.bracketTab ("Underground" )).toBeVisible ();
// the TO map pool round-trips onto the rules page
const rules = new TournamentRulesPage (page);
await rules.goto (tournamentId);
for (const { stage, mode } of mapPool) {
await expect (rules.stageName (stage)).toBeVisible ();
await expect (rules.modeImage (mode).first ()).toBeVisible ();
}
})Renders/calendar/new
Calendar new event page Arrange
Create user null roles “TOURNAMENT_ORGANIZER” → organizer
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});Calendar new event page Act
Go to new tournament
await newTournament.gotoNewTournament ();Fill Name with “Test Tournament”
await newTournament.form.fill ("name" , "Test Tournament" );Fill Discord server invite URL with “test-invite”
await newTournament.form.fill ("discordInviteCode" , "test-invite" );Go to step “teams”
await newTournament.goToStep ("teams" );Assert
step button “teams” has “aria-current” = “step”
await expect (newTournament.stepButton ("teams" )).toHaveAttribute (
"aria-current" ,
"step" ,
);the page URL is /step=teams/
await expect (page).toHaveURL (/step=teams/);Act
Go back
await page.goBack ();Assert
step button “basics” has “aria-current” = “step”
await expect (newTournament.stepButton ("basics" )).toHaveAttribute (
"aria-current" ,
"step" ,
);name input has value “Test Tournament”
await expect (newTournament.locators.nameInput).toHaveValue (
"Test Tournament" ,
);discard changes button is not visible
await isNotVisible (newTournament.locators.discardChangesButton);Source test ("browser back returns to the previous step of the tournament form" , async ({
page,
factories,
}) => {
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});
await impersonate (page, organizer.id);
const newTournament = new CalendarNewEventPage (page);
await newTournament.gotoNewTournament ();
await newTournament.form.fill ("name" , "Test Tournament" );
await newTournament.form.fill ("discordInviteCode" , "test-invite" );
await newTournament.goToStep ("teams" );
await expect (newTournament.stepButton ("teams" )).toHaveAttribute (
"aria-current" ,
"step" ,
);
await expect (page).toHaveURL (/step=teams/);
await page.goBack ();
await expect (newTournament.stepButton ("basics" )).toHaveAttribute (
"aria-current" ,
"step" ,
);
await expect (newTournament.locators.nameInput).toHaveValue (
"Test Tournament" ,
);
await isNotVisible (newTournament.locators.discardChangesButton);
})creates a team picked tournament with a custom map pool Renders/calendar/new /to/:id /to/:id/info /to/:id/rules
Calendar new event page Tournament rules page Arrange
Create user null roles “TOURNAMENT_ORGANIZER” → organizer
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});Calendar new event page Act
Go to new tournament
await newTournament.gotoNewTournament ();Fill Name with “Team Pick Tournament”
await newTournament.form.fill ("name" , "Team Pick Tournament" );Set first date new Date (2027 , 0 , 15 , 17 , 0 )
await newTournament.setFirstDate (new Date (2027 , 0 , 15 , 17 , 0 ));Go to step “maps”
await newTournament.goToStep ("maps" );Check items map picking style “AUTO”
await newTournament.form.checkItems ("mapPickingStyle" , ["AUTO" ]);Set team pick modes “Splat Zones” , “Tower Control”
await newTournament.setTeamPickModes (["Splat Zones" , "Tower Control" ]);Fill team pick count input “Splat Zones” with “2”
await newTournament.teamPickCountInput ("Splat Zones" ).fill ("2" );Fill team pick count input “Tower Control” with “2”
await newTournament.teamPickCountInput ("Tower Control" ).fill ("2" );Check items team pick pool “CUSTOM”
await newTournament.form.checkItems ("teamPickPool" , ["CUSTOM" ]);stages = ["Scorch Gorge" , "Eeltail Alley" , "Hagglefish Market" ]
const stages = ["Scorch Gorge" , "Eeltail Alley" , "Hagglefish Market" ];customPool = stages.flatMap ((stage) => [ { stage, mode: "Splat Zones" }, { stage, mode: "T …
const customPool = stages.flatMap ((stage) => [
{ stage, mode: "Splat Zones" },
{ stage, mode: "Tower Control" },
]);every mode needs one stage more than the teams pick in it
Pick map pool customPool.filter ((map) => map.stage !== "Hagglefish Market" )
await newTournament.pickMapPool (
customPool.filter ((map) => map.stage !== "Hagglefish Market" ),
);Assert
team pick pool status “SZ needs at least 3 stages (has 2)” is visible
await expect (
newTournament.teamPickPoolStatus ("SZ needs at least 3 stages (has 2)" ),
).toBeVisible ();Act
Pick map pool customPool.filter ((map) => map.stage === "Hagglefish Market" )
await newTournament.pickMapPool (
customPool.filter ((map) => map.stage === "Hagglefish Market" ),
);Assert
team pick pool status “The map pool has enough stages in every mode” is visible
await expect (
newTournament.teamPickPoolStatus (
"The map pool has enough stages in every mode" ,
),
).toBeVisible ();Act
Save
await newTournament.save ();Assert
the page URL is /\/to\/\d+/
await expect (page).toHaveURL (/\/to\/\d+/);Act
tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ])
const tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ]);Tournament rules page Act
Open the tournament rules page tournamentId
await rules.goto (tournamentId);Assert
rule “each team picking 2× SZ, 2× TC” is visible
await expect (rules.rule ("each team picking 2× SZ, 2× TC" )).toBeVisible ();rule “Maps are limited to the map pool above” is visible
await expect (
rules.rule ("Maps are limited to the map pool above" ),
).toBeVisible ();For each stage of stages
for (const stage of stages) {
await expect (rules.stageName (stage)).toBeVisible ();
}stage name stage is visible
await expect (rules.stageName (stage)).toBeVisible ();Source test ("creates a team picked tournament with a custom map pool" , async ({
page,
factories,
}) => {
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});
await impersonate (page, organizer.id);
const newTournament = new CalendarNewEventPage (page);
await newTournament.gotoNewTournament ();
await newTournament.form.fill ("name" , "Team Pick Tournament" );
await newTournament.setFirstDate (new Date (2027 , 0 , 15 , 17 , 0 ));
await newTournament.goToStep ("maps" );
await newTournament.form.checkItems ("mapPickingStyle" , ["AUTO" ]);
await newTournament.setTeamPickModes (["Splat Zones" , "Tower Control" ]);
await newTournament.teamPickCountInput ("Splat Zones" ).fill ("2" );
await newTournament.teamPickCountInput ("Tower Control" ).fill ("2" );
await newTournament.form.checkItems ("teamPickPool" , ["CUSTOM" ]);
const stages = ["Scorch Gorge" , "Eeltail Alley" , "Hagglefish Market" ];
const customPool = stages.flatMap ((stage) => [
{ stage, mode: "Splat Zones" },
{ stage, mode: "Tower Control" },
]);
// every mode needs one stage more than the teams pick in it
await newTournament.pickMapPool (
customPool.filter ((map) => map.stage !== "Hagglefish Market" ),
);
await expect (
newTournament.teamPickPoolStatus ("SZ needs at least 3 stages (has 2)" ),
).toBeVisible ();
await newTournament.pickMapPool (
customPool.filter ((map) => map.stage === "Hagglefish Market" ),
);
await expect (
newTournament.teamPickPoolStatus (
"The map pool has enough stages in every mode" ,
),
).toBeVisible ();
await newTournament.save ();
await expect (page).toHaveURL (/\/to\/\d+/);
const tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ]);
// the picked modes, counts and custom pool round-trip onto the rules page
const rules = new TournamentRulesPage (page);
await rules.goto (tournamentId);
await expect (rules.rule ("each team picking 2× SZ, 2× TC" )).toBeVisible ();
await expect (
rules.rule ("Maps are limited to the map pool above" ),
).toBeVisible ();
for (const stage of stages) {
await expect (rules.stageName (stage)).toBeVisible ();
}
})copies a tournament keeping its follow-up bracket the same time after the start Renders/calendar/new /to/:id /to/:id/info
Calendar new event page Arrange
Create user null roles “TOURNAMENT_ORGANIZER” → organizer
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});copiedStartTime = startOfHour (subDays (new Date (), 7 ))
const copiedStartTime = startOfHour (subDays (new Date (), 7 ));Create tournament author id organizer.id start times dateToDatabaseTimestamp (copiedStartTime) map picking style “AUTO” bracket progression name “Groups” type “round_robin” requires check in false settings teams per group 4 , name “Final Stage” type “single_elimination” requires check in true start time dateToDatabaseTimestamp (addDays (copiedStartTime, 1 )) settings sources bracket idx 0 placements 1 , 2 → copied
const copied = await factories.TournamentFactory .create ({
authorId: organizer.id,
startTimes: [dateToDatabaseTimestamp (copiedStartTime)],
mapPickingStyle: "AUTO" ,
bracketProgression: [
{
name: "Groups" ,
type : "round_robin" ,
requiresCheckIn: false ,
settings: { teamsPerGroup: 4 },
},
{
name: "Final Stage" ,
type : "single_elimination" ,
requiresCheckIn: true ,
startTime: dateToDatabaseTimestamp (addDays (copiedStartTime, 1 )),
settings: {},
sources: [{ bracketIdx: 0 , placements: [1 , 2 ] }],
},
],
});Calendar new event page Act
Go to new tournament
await newTournament.gotoNewTournament ();Copy tournament copied.eventId
await newTournament.copyTournament (copied.eventId);Fill Name with “Copied Tournament”
await newTournament.form.fill ("name" , "Copied Tournament" );Tournament start time → startTime
const startTime = await newTournament.tournamentStartTime ();followUpStartTime = addDays (startTime, 1 )
const followUpStartTime = addDays (startTime, 1 );starting on another day than the tournament, the card shows the date too
Go to step “format”
await newTournament.goToStep ("format" );Assert
bracket card “Final Stage” contains new Intl .DateTimeFormat ("en-US" , { month: "short" , day: "numeric" , }).format (…
await expect (newTournament.bracketCard ("Final Stage" )).toContainText (
new Intl .DateTimeFormat ("en-US" , {
month: "short" ,
day: "numeric" ,
}).format (followUpStartTime),
);Act
Select bracket 1
await newTournament.selectBracket (1 );Assert
bracket start time input has value datetimeLocalValue (followUpStartTime)
await expect (newTournament.locators.bracketStartTimeInput).toHaveValue (
datetimeLocalValue (followUpStartTime),
);Act
Go to step “basics”
await newTournament.goToStep ("basics" );Set first date addDays (startTime, 2 )
await newTournament.setFirstDate (addDays (startTime, 2 ));Go to step “prizes”
await newTournament.goToStep ("prizes" );Assert
bracket start before tournament error is visible
await expect (
newTournament.locators.bracketStartBeforeTournamentError,
).toBeVisible ();Act
Fill bracket start time input with datetimeLocalValue (addDays (startTime, 3 ))
await newTournament.locators.bracketStartTimeInput.fill (
datetimeLocalValue (addDays (startTime, 3 )),
);Save
await newTournament.save ();Assert
the page URL is /\/to\/\d+/
await expect (page).toHaveURL (/\/to\/\d+/);Source test ("copies a tournament keeping its follow-up bracket the same time after the start" , async ({
page,
factories,
}) => {
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});
const copiedStartTime = startOfHour (subDays (new Date (), 7 ));
const copied = await factories.TournamentFactory .create ({
authorId: organizer.id,
startTimes: [dateToDatabaseTimestamp (copiedStartTime)],
mapPickingStyle: "AUTO" ,
bracketProgression: [
{
name: "Groups" ,
type : "round_robin" ,
requiresCheckIn: false ,
settings: { teamsPerGroup: 4 },
},
{
name: "Final Stage" ,
type : "single_elimination" ,
requiresCheckIn: true ,
startTime: dateToDatabaseTimestamp (addDays (copiedStartTime, 1 )),
settings: {},
sources: [{ bracketIdx: 0 , placements: [1 , 2 ] }],
},
],
});
await impersonate (page, organizer.id);
const newTournament = new CalendarNewEventPage (page);
await newTournament.gotoNewTournament ();
await newTournament.copyTournament (copied.eventId);
await newTournament.form.fill ("name" , "Copied Tournament" );
const startTime = await newTournament.tournamentStartTime ();
const followUpStartTime = addDays (startTime, 1 );
// starting on another day than the tournament, the card shows the date too
await newTournament.goToStep ("format" );
await expect (newTournament.bracketCard ("Final Stage" )).toContainText (
new Intl .DateTimeFormat ("en-US" , {
month: "short" ,
day: "numeric" ,
}).format (followUpStartTime),
);
await newTournament.selectBracket (1 );
await expect (newTournament.locators.bracketStartTimeInput).toHaveValue (
datetimeLocalValue (followUpStartTime),
);
await newTournament.goToStep ("basics" );
await newTournament.setFirstDate (addDays (startTime, 2 ));
await newTournament.goToStep ("prizes" );
await expect (
newTournament.locators.bracketStartBeforeTournamentError,
).toBeVisible ();
await newTournament.locators.bracketStartTimeInput.fill (
datetimeLocalValue (addDays (startTime, 3 )),
);
await newTournament.save ();
await expect (page).toHaveURL (/\/to\/\d+/);
})copies a tournament keeping registration open until the same time before the new start Renders/calendar/new /to/:id /to/:id/info /to/:id/register
Calendar new event page Tournament register page Arrange
Create user null roles “TOURNAMENT_ORGANIZER” → organizer
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});copiedStartTime = startOfHour (subDays (new Date (), 7 ))
const copiedStartTime = startOfHour (subDays (new Date (), 7 ));Create tournament author id organizer.id start times dateToDatabaseTimestamp (copiedStartTime) map picking style “AUTO” reg closes at dateToDatabaseTimestamp (subHours (copiedStartTime, 1 )) → copied
const copied = await factories.TournamentFactory .create ({
authorId: organizer.id,
startTimes: [dateToDatabaseTimestamp (copiedStartTime)],
mapPickingStyle: "AUTO" ,
regClosesAt: dateToDatabaseTimestamp (subHours (copiedStartTime, 1 )),
});Calendar new event page Act
Go to new tournament
await newTournament.gotoNewTournament ();Copy tournament copied.eventId
await newTournament.copyTournament (copied.eventId);Fill Name with “Copied Tournament”
await newTournament.form.fill ("name" , "Copied Tournament" );Tournament start time → startTime
const startTime = await newTournament.tournamentStartTime ();Set first date addDays (startTime, 7 )
await newTournament.setFirstDate (addDays (startTime, 7 ));Save
await newTournament.save ();Assert
the page URL is /\/to\/\d+/
await expect (page).toHaveURL (/\/to\/\d+/);Act
tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ])
const tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ]);Tournament register page Act
Open the tournament register page tournamentId
await register.goto (tournamentId);Assert
save team button is visible
await expect (register.locators.saveTeamButton).toBeVisible ();registration closed alert is not visible
await isNotVisible (register.locators.registrationClosedAlert);Source test ("copies a tournament keeping registration open until the same time before the new start" , async ({
page,
factories,
}) => {
const organizer = await factories.UserFactory .create (null , {
roles: ["TOURNAMENT_ORGANIZER" ],
});
const copiedStartTime = startOfHour (subDays (new Date (), 7 ));
const copied = await factories.TournamentFactory .create ({
authorId: organizer.id,
startTimes: [dateToDatabaseTimestamp (copiedStartTime)],
mapPickingStyle: "AUTO" ,
regClosesAt: dateToDatabaseTimestamp (subHours (copiedStartTime, 1 )),
});
await impersonate (page, organizer.id);
const newTournament = new CalendarNewEventPage (page);
await newTournament.gotoNewTournament ();
await newTournament.copyTournament (copied.eventId);
await newTournament.form.fill ("name" , "Copied Tournament" );
const startTime = await newTournament.tournamentStartTime ();
await newTournament.setFirstDate (addDays (startTime, 7 ));
await newTournament.save ();
await expect (page).toHaveURL (/\/to\/\d+/);
const tournamentId = Number (page.url ().match (/\/to\/(\d+)/)?.[1 ]);
const register = new TournamentRegisterPage (page);
await register.goto (tournamentId);
await expect (register.locators.saveTeamButton).toBeVisible ();
await isNotVisible (register.locators.registrationClosedAlert);
})reports winners of a past event Renders/calendar/:id /calendar/:id/report-winners
Report winners page Calendar event page Report winners page Arrange
Create calendar event author Sendou start times dateToDatabaseTimestamp (subDays (new Date (), 1 )) → event
const event = await factories.CalendarEventFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (subDays (new Date (), 1 ))],
});Report winners page Act
Open the report winners page event.id
await reportWinners.goto (event.id);Fill participant count input with “50”
await reportWinners.locators.participantCountInput.fill ("50" );Fill team name input with “Team Olive”
await reportWinners.locators.teamNameInput.fill ("Team Olive" );Fill placing input with “1”
await reportWinners.locators.placingInput.fill ("1" );a team without any players can't be reported
Click submit button
await reportWinners.locators.submitButton.click ();Assert
empty team error is visible
await expect (reportWinners.locators.emptyTeamError).toBeVisible ();Act
Select player 1 , “N-ZAP”
await reportWinners.selectPlayer (1 , "N-ZAP" );Fill player as text 2 , “Player Without Account”
await reportWinners.fillPlayerAsText (2 , "Player Without Account" );Submit
await reportWinners.submit ();Assert
the page URL is calendarEventPage (event.id)
await expect (page).toHaveURL (calendarEventPage (event.id));Calendar event page Assert
result row “Team Olive” contains “Player Without Account”
await expect (calendarEvent.resultRow ("Team Olive" )).toContainText (
"Player Without Account" ,
);Report winners page Act
reported results are loaded back into the form for editing
Open the report winners page event.id
await reportWinners.goto (event.id);Assert
participant count input has value “50”
await expect (reportWinners.locators.participantCountInput).toHaveValue (
"50" ,
);team name input has value “Team Olive”
await expect (reportWinners.locators.teamNameInput).toHaveValue (
"Team Olive" ,
);placing input has value “1”
await expect (reportWinners.locators.placingInput).toHaveValue ("1" );player 1 contains “N-ZAP”
await expect (reportWinners.player (1 )).toContainText ("N-ZAP" );player 2 has value “Player Without Account”
await expect (reportWinners.player (2 )).toHaveValue ("Player Without Account" );Source test ("reports winners of a past event" , async ({ page, factories }) => {
const event = await factories.CalendarEventFactory .create ({
authorId: ADMIN_ID ,
startTimes: [dateToDatabaseTimestamp (subDays (new Date (), 1 ))],
});
await impersonate (page);
const reportWinners = new ReportWinnersPage (page);
await reportWinners.goto (event.id);
await reportWinners.locators.participantCountInput.fill ("50" );
await reportWinners.locators.teamNameInput.fill ("Team Olive" );
await reportWinners.locators.placingInput.fill ("1" );
// a team without any players can't be reported
await reportWinners.locators.submitButton.click ();
await expect (reportWinners.locators.emptyTeamError).toBeVisible ();
await reportWinners.selectPlayer (1 , "N-ZAP" );
await reportWinners.fillPlayerAsText (2 , "Player Without Account" );
await reportWinners.submit ();
await expect (page).toHaveURL (calendarEventPage (event.id));
const calendarEvent = new CalendarEventPage (page);
await expect (calendarEvent.resultRow ("Team Olive" )).toContainText (
"Player Without Account" ,
);
// reported results are loaded back into the form for editing
await reportWinners.goto (event.id);
await expect (reportWinners.locators.participantCountInput).toHaveValue (
"50" ,
);
await expect (reportWinners.locators.teamNameInput).toHaveValue (
"Team Olive" ,
);
await expect (reportWinners.locators.placingInput).toHaveValue ("1" );
await expect (reportWinners.player (1 )).toContainText ("N-ZAP" );
await expect (reportWinners.player (2 )).toHaveValue ("Player Without Account" );
})CalendarPage.gotoe2e/pages/calendar/calendar-page.ts:35
async goto (dayMonthYear?: DayMonthYear ) {
await navigate ({ page: this .page, url: calendarPage ({ dayMonthYear }) });
}CalendarPage.toggleEventTypeFiltere2e/pages/calendar/calendar-page.ts:57
async toggleEventTypeFilter (name: "isSendou" | "isRanked" ) {
await this .page.keyboard.press ("Escape" );
await this .openEventTypeFilter ();
await this .page
.getByText (
name === "isSendou"
? "Only events hosted on sendou.ink"
: "Only ranked events" ,
)
.click ();
await this .page.keyboard.press ("Escape" );
}CalendarPage.locators.tournamentCardse2e/pages/calendar/calendar-page.ts:21
No JSDoc
page.getByTestId ("tournament-card" ) CalendarPage.reloade2e/pages/calendar/calendar-page.ts:51
No JSDoc
async reload () {
await this .page.reload ();
await expectIsHydrated (this .page);
}CalendarPage.toggleHiddenEventse2e/pages/calendar/calendar-page.ts:95
async toggleHiddenEvents () {
await this .locators.hiddenEventsButtons.first ().click ();
}CalendarPagee2e/pages/calendar/calendar-page.ts:14
CalendarPage.locators.hiddenEventsButtonse2e/pages/calendar/calendar-page.ts:22
No JSDoc
page.getByTestId ("hidden-events-button" ) isNotVisiblee2e/helpers/playwright.ts:745
No JSDoc
export function isNotVisible (locator: Locator ) {
return expect (locator).toHaveCount (0 );
}CalendarPage.saveFiltersAsDefaulte2e/pages/calendar/calendar-page.ts:88
async saveFiltersAsDefault () {
await waitForPOSTResponse (this .page, () =>
this .locators.saveFiltersAsDefaultButton.click (),
);
}CalendarPage.removeEventTypeFiltere2e/pages/calendar/calendar-page.ts:71
async removeEventTypeFilter () {
await this .page.keyboard.press ("Escape" );
await this .page.getByTestId ("event-type-filter-remove" ).click ();
}CalendarPage.navigatePreviouse2e/pages/calendar/calendar-page.ts:106
No JSDoc
async navigatePrevious () {
await this .locators.navigateButtons.first ().click ();
}CalendarPage.locators.todayHeadere2e/pages/calendar/calendar-page.ts:24
No JSDoc
page.getByTestId ("today-header" ) CalendarPage.navigateNexte2e/pages/calendar/calendar-page.ts:110
No JSDoc
async navigateNext () {
await this .locators.navigateButtons.nth (1 ).click ();
}CalendarNewEventPage.gotoe2e/pages/calendar/calendar-new-event-page.ts:66
No JSDoc
async goto () {
await navigate ({ page: this .page, url: CALENDAR_NEW_PAGE });
}calendarNewBaseSchema.nameapp/features/calendar/calendar-new-schemas.ts:101
name: textField ({
label: "labels.name" ,
minLength: CALENDAR_EVENT .NAME_MIN_LENGTH ,
maxLength: CALENDAR_EVENT .NAME_MAX_LENGTH ,
}) CalendarNewEventPage.setFirstDatee2e/pages/calendar/calendar-new-event-page.ts:100
No JSDoc
async setFirstDate (date: Date ) {
await this .page
.getByLabel (/^Date *\*?$/)
.first ()
.fill (datetimeLocalValue (date));
}calendarNewBaseSchema.bracketUrlapp/features/calendar/calendar-new-schemas.ts:129
bracketUrl: textFieldOptional ({
label: "labels.bracketUrl" ,
maxLength: CALENDAR_EVENT .BRACKET_URL_MAX_LENGTH ,
validate: "url" ,
}) CalendarEventPage.openEdite2e/pages/calendar/calendar-event-page.ts:34
No JSDoc
async openEdit () {
await this .locators.editButton.click ();
return new CalendarNewEventPage (this .page);
}CalendarNewEventPage.savee2e/pages/calendar/calendar-new-event-page.ts:275
async save () {
if (await this .locators.lastStepButton.isVisible ()) {
await this .locators.lastStepButton.click ();
}
return submit (this .page);
}CalendarEventPage.startTimee2e/pages/calendar/calendar-event-page.ts:30
startTime (date: Date ) {
return this .page.locator (`time[datetime="${date.toISOString()}"]` );
}CalendarPage.tournamentCarde2e/pages/calendar/calendar-page.ts:39
No JSDoc
tournamentCard (name: string ) {
return this .locators.tournamentCards.filter ({ hasText: name });
}CalendarEventPage.gotoe2e/pages/calendar/calendar-event-page.ts:21
No JSDoc
async goto (eventId: Tables ["CalendarEvent" ]["id" ]) {
await navigate ({ page: this .page, url: calendarEventPage (eventId) });
}CalendarEventPage.deletee2e/pages/calendar/calendar-event-page.ts:40
async delete () {
await this .locators.deleteButton.click ();
await modalClickConfirmButton (this .page);
}CalendarNewEventPagee2e/pages/calendar/calendar-new-event-page.ts:24
CalendarEventPagee2e/pages/calendar/calendar-event-page.ts:8
CalendarNewEventPage.gotoNewTournamente2e/pages/calendar/calendar-new-event-page.ts:70
No JSDoc
async gotoNewTournament () {
await navigate ({ page: this .page, url: TOURNAMENT_NEW_PAGE });
}calendarNewBaseSchema.descriptionapp/features/calendar/calendar-new-schemas.ts:106
description: textAreaOptional ({
label: "labels.description" ,
maxLength: CALENDAR_EVENT .DESCRIPTION_MAX_LENGTH ,
}) calendarNewBaseSchema.discordInviteCodeapp/features/calendar/calendar-new-schemas.ts:134
discordInviteCode: textFieldOptional ({
label: "labels.discordInvite" ,
maxLength: CALENDAR_EVENT .DISCORD_INVITE_CODE_MAX_LENGTH ,
leftAddon: "https://discord.gg/" ,
inputMode: "url" ,
}) CalendarNewEventPage.goToStepe2e/pages/calendar/calendar-new-event-page.ts:147
async goToStep (step: "basics" | "teams" | "maps" | "format" | "prizes" ) {
await this .stepButton (step).click ();
}calendarNewBaseSchema.requireInGameNamesapp/features/calendar/calendar-new-schemas.ts:194
requireInGameNames: toggle ({
label: "labels.requireInGameNames" ,
bottomText: "bottomTexts.requireInGameNames" ,
}) calendarNewBaseSchema.mapPickingStyleapp/features/calendar/calendar-new-schemas.ts:169
No JSDoc
mapPickingStyle: mapPickingStyleField CalendarNewEventPage.pickMapPoole2e/pages/calendar/calendar-new-event-page.ts:136
No JSDoc
async pickMapPool (maps: Array <{ stage: string ; mode: string }>) {
for (const { stage, mode } of maps) {
await this .page.getByRole ("tab" , { name: mode }).click ();
await this .page
.getByRole ("tabpanel" )
.getByRole ("checkbox" , { name: stage, exact: true })
.click ();
}
}CalendarNewEventPage.addFollowUpBrackete2e/pages/calendar/calendar-new-event-page.ts:284
async addFollowUpBracket ({
name,
format,
placements,
}: {
name: string ;
format: string ;
placements: number [];
}) {
await this .addBracket ({ name, format });
const lastNth = (await this .locators.bracketCards.count ()) - 1 ;
await this .connect (0 , lastNth, placements);
}TournamentInfoPage.gotoe2e/pages/tournament/tournament-info-page.ts:16
No JSDoc
async goto (tournamentId: number ) {
await navigate ({ page: this .page, url: tournamentInfoPage (tournamentId) });
}TournamentInfoPage.startTimee2e/pages/tournament/tournament-info-page.ts:21
startTime (date: Date ) {
return this .page.locator (`time[datetime="${date.toISOString()}"]` );
}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.bracketTabe2e/pages/tournament/tournament-brackets-page.ts:67
bracketTab (name: string ) {
return this .page.getByRole ("tab" , { name });
}TournamentRulesPage.gotoe2e/pages/tournament/tournament-rules-page.ts:16
No JSDoc
async goto (tournamentId: number ) {
await navigate ({ page: this .page, url: tournamentRulesPage (tournamentId) });
}TournamentRulesPage.stageNamee2e/pages/tournament/tournament-rules-page.ts:20
No JSDoc
stageName (stage: string ) {
return this .page.getByText (stage, { exact: true });
}TournamentRulesPage.modeImagee2e/pages/tournament/tournament-rules-page.ts:24
No JSDoc
modeImage (mode: string ) {
return this .page.getByAltText (mode);
}TournamentInfoPagee2e/pages/tournament/tournament-info-page.ts:7
TournamentBracketsPagee2e/pages/tournament/tournament-brackets-page.ts:14
No JSDoc
TournamentRulesPagee2e/pages/tournament/tournament-rules-page.ts:7
CalendarNewEventPage.stepButtone2e/pages/calendar/calendar-new-event-page.ts:152
stepButton (step: "basics" | "teams" | "maps" | "format" | "prizes" ) {
return this .page.getByTestId (`form-step-button-${step}` );
}CalendarNewEventPage.locators.nameInpute2e/pages/calendar/calendar-new-event-page.ts:33
No JSDoc
page.getByLabel (/^Name *\*?$/) CalendarNewEventPage.locators.discardChangesButtone2e/pages/calendar/calendar-new-event-page.ts:54
No JSDoc
page.getByTestId ("discard-changes-button" ) CalendarNewEventPage.setTeamPickModese2e/pages/calendar/calendar-new-event-page.ts:123
async setTeamPickModes (modeNames: string []) {
for (const modeName of ALL_MODE_NAMES ) {
const checkbox = this .teamPickModeCheckbox (modeName);
const shouldBeChecked = modeNames.includes (modeName);
if ((await checkbox.isChecked ()) !== shouldBeChecked) {
await checkbox.click ();
}
}
}CalendarNewEventPage.teamPickCountInpute2e/pages/calendar/calendar-new-event-page.ts:113
No JSDoc
teamPickCountInput (modeName: string ) {
return this .page.getByRole ("spinbutton" , { name: modeName });
}calendarNewBaseSchema.teamPickPoolapp/features/calendar/calendar-new-schemas.ts:172
No JSDoc
teamPickPool: teamPickPoolField CalendarNewEventPage.teamPickPoolStatuse2e/pages/calendar/calendar-new-event-page.ts:118
teamPickPoolStatus (text: string ) {
return this .page.getByText (text);
}TournamentRulesPage.rulee2e/pages/tournament/tournament-rules-page.ts:29
rule (text: string ) {
return this .page.getByText (text);
}CalendarNewEventPage.copyTournamente2e/pages/calendar/calendar-new-event-page.ts:75
async copyTournament (eventId: number ) {
await this .locators.copyTournamentSelect.selectOption (String (eventId));
const formBeforeCopy =
await this .locators.copyTournamentSelect.elementHandle ();
await this .locators.useTemplateButton.click ();
await this .page.waitForURL (/copyEventId=/);
// the URL changes before the form remounts with the copied values, wiping anything filled in between
await formBeforeCopy?.waitForElementState ("hidden" );
}CalendarNewEventPage.tournamentStartTimee2e/pages/calendar/calendar-new-event-page.ts:86
async tournamentStartTime () {
const value = await this .page
.getByLabel (/^Date *\*?$/)
.first ()
.inputValue ();
return parse (value, "yyyy-MM-dd'T'HH:mm" , new Date ());
}CalendarNewEventPage.bracketCarde2e/pages/calendar/calendar-new-event-page.ts:95
No JSDoc
bracketCard (name: string ) {
return this .locators.bracketCards.filter ({ hasText: name });
}CalendarNewEventPage.selectBrackete2e/pages/calendar/calendar-new-event-page.ts:157
async selectBracket (nth: number ) {
await this .goToStep ("format" );
await this .locators.bracketCards
.nth (nth)
.getByRole ("button" )
.first ()
.click ();
}CalendarNewEventPage.locators.bracketStartTimeInpute2e/pages/calendar/calendar-new-event-page.ts:43
No JSDoc
page.getByLabel (/^Start time *\*?$/) CalendarNewEventPage.locators.bracketStartBeforeTournamentErrore2e/pages/calendar/calendar-new-event-page.ts:44
No JSDoc
page.getByText (
"Can't start before the tournament starts" ,
) 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.saveTeamButtone2e/pages/tournament/tournament-register-page.ts:41
No JSDoc
page.getByTestId ("save-team-button" ) TournamentRegisterPage.locators.registrationClosedAlerte2e/pages/tournament/tournament-register-page.ts:31
No JSDoc
page.getByText (
"Registration for this tournament has closed" ,
) TournamentRegisterPagee2e/pages/tournament/tournament-register-page.ts:17
No JSDoc
ReportWinnersPage.gotoe2e/pages/calendar/report-winners-page.ts:24
No JSDoc
async goto (eventId: Tables ["CalendarEvent" ]["id" ]) {
await navigate ({
page: this .page,
url: calendarReportWinnersPage (eventId),
});
}ReportWinnersPage.locators.participantCountInpute2e/pages/calendar/report-winners-page.ts:14
No JSDoc
page.getByLabel ("Participant count" ) ReportWinnersPage.locators.teamNameInpute2e/pages/calendar/report-winners-page.ts:15
No JSDoc
page.getByLabel ("Team name" ) ReportWinnersPage.locators.placingInpute2e/pages/calendar/report-winners-page.ts:16
No JSDoc
page.getByLabel ("Placing" ) ReportWinnersPage.locators.submitButtone2e/pages/calendar/report-winners-page.ts:20
No JSDoc
page.getByTestId ("submit-button" ) ReportWinnersPage.locators.emptyTeamErrore2e/pages/calendar/report-winners-page.ts:17
No JSDoc
page.getByText (
"Each team must have at least one player." ,
) ReportWinnersPage.selectPlayere2e/pages/calendar/report-winners-page.ts:35
No JSDoc
async selectPlayer (number : number , userName: string ) {
await selectUser ({
page: this .page,
userName,
labelName: `Player ${number}` ,
});
}ReportWinnersPage.fillPlayerAsTexte2e/pages/calendar/report-winners-page.ts:44
async fillPlayerAsText (number : number , name: string ) {
await this .page
.getByRole ("button" , { name: "Add as text" })
.nth (number - 1 )
.click ();
await this .player (number ).fill (name);
}ReportWinnersPage.submite2e/pages/calendar/report-winners-page.ts:52
No JSDoc
async submit () {
await submit (this .page);
}CalendarEventPage.resultRowe2e/pages/calendar/calendar-event-page.ts:25
No JSDoc
resultRow (teamName: string ) {
return this .locators.resultRows.filter ({ hasText: teamName });
}ReportWinnersPage.playere2e/pages/calendar/report-winners-page.ts:31
No JSDoc
player (number : number ) {
return this .page.getByLabel (`Player ${number}` );
}ReportWinnersPagee2e/pages/calendar/report-winners-page.ts:7