New team creation Renders/ /t/:customUrl /t/:customUrl /t/new
Anything adder New team page Act
Go to “/”
await navigate ({ page, url: "/" });Anything adder Act
Add “team”
await new AnythingAdder (page).add ("team" );Assert
the page URL is /t\/new /
await expect (page).toHaveURL (/t\/new /);New team page Act
Fill Name with “Chimera”
await newTeam.form.fill ("name" , "Chimera" );Submit
await newTeam.form.submit ();Assert
the page URL is /chimera/
await expect (page).toHaveURL (/chimera/);Source test ("creates new team" , async ({ page }) => {
await impersonate (page, NZAP_TEST_ID );
await navigate ({ page, url: "/" });
await new AnythingAdder (page).add ("team" );
await expect (page).toHaveURL (/t\/new /);
const newTeam = new NewTeamPage (page);
await newTeam.form.fill ("name" , "Chimera" );
await newTeam.form.submit ();
await expect (page).toHaveURL (/chimera/);
})Team page Renders/t/:customUrl /t/:customUrl /t/:customUrl/edit
Team page Team edit page Team page Arrange
Create team name “Alliance Rogue” member user ids Sendou → { customUrl }
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});Team page Act
Open the team page customUrl
await team.goto (customUrl);Open edit → teamEdit
const teamEdit = await team.openEdit ();Team edit page Act
Fill Name with “Better Alliance Rogue”
await teamEdit.form.fill ("name" , "Better Alliance Rogue" );Fill Team Bluesky with “BetterAllianceRogue”
await teamEdit.form.fill ("bsky" , "BetterAllianceRogue" );Fill Bio with “shorter bio”
await teamEdit.form.fill ("bio" , "shorter bio" );Submit
await teamEdit.form.submit ();Assert
the page URL is /better-alliance-rogue/
await expect (page).toHaveURL (/better-alliance-rogue/);Team page Assert
bio reads “shorter bio”
await expect (team.locators.bio).toHaveText ("shorter bio" );bsky link has “href” = “https://bsky.app/profile/BetterAllianceRogue”
await expect (team.locators.bskyLink).toHaveAttribute (
"href" ,
"https://bsky.app/profile/BetterAllianceRogue" ,
);Source test ("edit team info" , async ({ page, factories }) => {
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
const teamEdit = await team.openEdit ();
await teamEdit.form.fill ("name" , "Better Alliance Rogue" );
await teamEdit.form.fill ("bsky" , "BetterAllianceRogue" );
await teamEdit.form.fill ("bio" , "shorter bio" );
await teamEdit.form.submit ();
await expect (page).toHaveURL (/better-alliance-rogue/);
await expect (team.locators.bio).toHaveText ("shorter bio" );
await expect (team.locators.bskyLink).toHaveAttribute (
"href" ,
"https://bsky.app/profile/BetterAllianceRogue" ,
);
})edits and removes team map preferences Renders/t/:customUrl /t/:customUrl/edit
Team edit page Arrange
Create team name “Alliance Rogue” member user ids Sendou → { customUrl }
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});Team edit page Act
Open the team edit page customUrl
await teamEdit.goto (customUrl);Assert
a team without preferences has nothing to remove
remove map preferences button is not visible
await isNotVisible (teamEdit.locators.removeMapPreferencesButton);Act
Set mode preference “SZ” , “Prefer”
await teamEdit.maps.setModePreference ("SZ" , "Prefer" );Set mode preference “TW” , “Avoid”
await teamEdit.maps.setModePreference ("TW" , "Avoid" );Select mode tab “SZ”
await teamEdit.maps.selectModeTab ("SZ" );Click map button “SZ” , 1
await teamEdit.maps.mapButton ("SZ" , 1 ).click ();Save map preferences
await teamEdit.saveMapPreferences ();reload so the form shows what was actually persisted
Open the team edit page customUrl
await teamEdit.goto (customUrl);Assert
preference radio “SZ” , “Prefer” is checked
await expect (teamEdit.maps.preferenceRadio ("SZ" , "Prefer" )).toBeChecked ();preference radio “TW” , “Avoid” is checked
await expect (teamEdit.maps.preferenceRadio ("TW" , "Avoid" )).toBeChecked ();Act
Select mode tab “SZ”
await teamEdit.maps.selectModeTab ("SZ" );Assert
map button “SZ” , 1 has class SELECTED_MAP_CLASS
await expect (teamEdit.maps.mapButton ("SZ" , 1 )).toHaveClass (
SELECTED_MAP_CLASS ,
);Act
Remove map preferences
await teamEdit.removeMapPreferences ();Assert
remove map preferences button is not visible
await isNotVisible (teamEdit.locators.removeMapPreferencesButton);the form re-syncs in place, so a following save can't restore what was removed
preference radio “SZ” , “Neutral” is checked
await expect (teamEdit.maps.preferenceRadio ("SZ" , "Neutral" )).toBeChecked ();preference radio “TW” , “Neutral” is checked
await expect (teamEdit.maps.preferenceRadio ("TW" , "Neutral" )).toBeChecked ();Act
Select mode tab “SZ”
await teamEdit.maps.selectModeTab ("SZ" );Assert
map button “SZ” , 1 does not have class SELECTED_MAP_CLASS
await expect (teamEdit.maps.mapButton ("SZ" , 1 )).not.toHaveClass (
SELECTED_MAP_CLASS ,
);Act
Open the team edit page customUrl
await teamEdit.goto (customUrl);Assert
preference radio “SZ” , “Neutral” is checked
await expect (teamEdit.maps.preferenceRadio ("SZ" , "Neutral" )).toBeChecked ();remove map preferences button is not visible
await isNotVisible (teamEdit.locators.removeMapPreferencesButton);Source test ("edits and removes team map preferences" , async ({
page,
factories,
}) => {
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});
await impersonate (page, ADMIN_ID );
const teamEdit = new TeamEditPage (page);
await teamEdit.goto (customUrl);
// a team without preferences has nothing to remove
await isNotVisible (teamEdit.locators.removeMapPreferencesButton);
await teamEdit.maps.setModePreference ("SZ" , "Prefer" );
await teamEdit.maps.setModePreference ("TW" , "Avoid" );
await teamEdit.maps.selectModeTab ("SZ" );
await teamEdit.maps.mapButton ("SZ" , 1 ).click ();
await teamEdit.saveMapPreferences ();
// reload so the form shows what was actually persisted
await teamEdit.goto (customUrl);
await expect (teamEdit.maps.preferenceRadio ("SZ" , "Prefer" )).toBeChecked ();
await expect (teamEdit.maps.preferenceRadio ("TW" , "Avoid" )).toBeChecked ();
await teamEdit.maps.selectModeTab ("SZ" );
await expect (teamEdit.maps.mapButton ("SZ" , 1 )).toHaveClass (
SELECTED_MAP_CLASS ,
);
await teamEdit.removeMapPreferences ();
await isNotVisible (teamEdit.locators.removeMapPreferencesButton);
// the form re-syncs in place, so a following save can't restore what was removed
await expect (teamEdit.maps.preferenceRadio ("SZ" , "Neutral" )).toBeChecked ();
await expect (teamEdit.maps.preferenceRadio ("TW" , "Neutral" )).toBeChecked ();
await teamEdit.maps.selectModeTab ("SZ" );
await expect (teamEdit.maps.mapButton ("SZ" , 1 )).not.toHaveClass (
SELECTED_MAP_CLASS ,
);
await teamEdit.goto (customUrl);
await expect (teamEdit.maps.preferenceRadio ("SZ" , "Neutral" )).toBeChecked ();
await isNotVisible (teamEdit.locators.removeMapPreferencesButton);
})kicks a member & changes a role Renders/t/:customUrl /t/:customUrl /t/:customUrl/roster
Team page Team roster page Member row Team roster page Team page Arrange
Create full team → { customUrl }
const { customUrl } = await createFullTeam (factories);Team page Act
Open the team page customUrl
await team.goto (customUrl);Assert
owner badge Sendou is visible
await expect (team.ownerBadge (ADMIN_ID )).toBeVisible ();Act
Open manage roster → roster
const roster = await team.openManageRoster ();Team roster page Act
Select role “SUPPORT”
await roster.memberRow (0 ).selectRole ("SUPPORT" );Member row ROSTER_SIZE - 1 → lastMember
const lastMember = roster.memberRow (ROSTER_SIZE - 1 );Member row Assert
row is visible
await expect (lastMember.locators.row).toBeVisible ();Act
Remove
await lastMember.remove ();Assert
row is not visible
await isNotVisible (lastMember.locators.row);Team roster page Act
Save
await roster.save ();Team page Act
Open the team page customUrl
await team.goto (customUrl);Assert
member role 0 reads “Support”
await expect (team.memberRole (0 )).toHaveText ("Support" );Source test ("kicks a member & changes a role" , async ({ page, factories }) => {
const { customUrl } = await createFullTeam (factories);
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
await expect (team.ownerBadge (ADMIN_ID )).toBeVisible ();
const roster = await team.openManageRoster ();
await roster.memberRow (0 ).selectRole ("SUPPORT" );
const lastMember = roster.memberRow (ROSTER_SIZE - 1 );
await expect (lastMember.locators.row).toBeVisible ();
await lastMember.remove ();
await isNotVisible (lastMember.locators.row);
await roster.save ();
await team.goto (customUrl);
await expect (team.memberRole (0 )).toHaveText ("Support" );
})sets a custom role for a member Renders/t/:customUrl /t/:customUrl /t/:customUrl/roster
Team page Team roster page Team page Arrange
Create full team → { customUrl }
const { customUrl } = await createFullTeam (factories);Team page Act
Open the team page customUrl
await team.goto (customUrl);Open manage roster → roster
const roster = await team.openManageRoster ();Team roster page Act
Set custom role “Strategist” , “OTHER”
await roster.memberRow (1 ).setCustomRole ("Strategist" , "OTHER" );Save
await roster.save ();Team page Act
Open the team page customUrl
await team.goto (customUrl);custom role is classified as "OTHER" so it lives under the "Other" tab
Click other roles tab
await team.locators.otherRolesTab.click ();Assert
custom role “Strategist” is visible
await expect (team.customRole ("Strategist" )).toBeVisible ();Source test ("sets a custom role for a member" , async ({ page, factories }) => {
const { customUrl } = await createFullTeam (factories);
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
const roster = await team.openManageRoster ();
await roster.memberRow (1 ).setCustomRole ("Strategist" , "OTHER" );
await roster.save ();
await team.goto (customUrl);
// custom role is classified as "OTHER" so it lives under the "Other" tab
await team.locators.otherRolesTab.click ();
await expect (team.customRole ("Strategist" )).toBeVisible ();
})reorders members via move buttons Renders/t/:customUrl /t/:customUrl /t/:customUrl/roster
Team page Member row Team roster page Team page Member row Arrange
Create full team → { customUrl }
const { customUrl } = await createFullTeam (factories);Team page Act
Open the team page customUrl
await team.goto (customUrl);Open manage roster → roster
const roster = await team.openManageRoster ();Team roster page
Act
Member row 0 → firstRow
const firstRow = roster.memberRow (0 );Member row 1 → secondRow
const secondRow = roster.memberRow (1 );Member row ROSTER_SIZE - 1 → lastRow
const lastRow = roster.memberRow (ROSTER_SIZE - 1 );Member row Act
Inner text → firstName
const firstName = await firstRow.locators.username.innerText ();Inner text → secondName
const secondName = await secondRow.locators.username.innerText ();Assert
firstName is not secondName
expect (firstName).not.toBe (secondName);the first member can't move up and the last can't move down
move up button is disabled
await expect (firstRow.locators.moveUpButton).toBeDisabled ();move down button is disabled
await expect (lastRow.locators.moveDownButton).toBeDisabled ();Act
Move down
await firstRow.moveDown ();Assert
username reads secondName
await expect (firstRow.locators.username).toHaveText (secondName);username reads firstName
await expect (secondRow.locators.username).toHaveText (firstName);Team roster page Act
Save
await roster.save ();Team page Act
Open the team page customUrl
await team.goto (customUrl);Open manage roster
await team.openManageRoster ();Member row Assert
username reads secondName
await expect (firstRow.locators.username).toHaveText (secondName);Source test ("reorders members via move buttons" , async ({ page, factories }) => {
const { customUrl } = await createFullTeam (factories);
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
const roster = await team.openManageRoster ();
const firstRow = roster.memberRow (0 );
const secondRow = roster.memberRow (1 );
const lastRow = roster.memberRow (ROSTER_SIZE - 1 );
const firstName = await firstRow.locators.username.innerText ();
const secondName = await secondRow.locators.username.innerText ();
expect (firstName).not.toBe (secondName);
// the first member can't move up and the last can't move down
await expect (firstRow.locators.moveUpButton).toBeDisabled ();
await expect (lastRow.locators.moveDownButton).toBeDisabled ();
await firstRow.moveDown ();
await expect (firstRow.locators.username).toHaveText (secondName);
await expect (secondRow.locators.username).toHaveText (firstName);
await roster.save ();
await team.goto (customUrl);
await team.openManageRoster ();
await expect (firstRow.locators.username).toHaveText (secondName);
})shows a finalized tournament placement on the results page Renders/t/:customUrl /t/:customUrl /t/:customUrl/results
Team page Team results page Arrange
Create ROSTER_SIZE - 1 × user → teammates
const teammates = await factories.UserFactory .createMany (ROSTER_SIZE - 1 );memberUserIds = [ ADMIN_ID , ...teammates.map ((teammate) => teammate.id), ]
const memberUserIds = [
ADMIN_ID ,
...teammates.map ((teammate) => teammate.id),
];Create team name “Alliance Rogue” memberUserIds → { id: teamId, customUrl }
const { id: teamId, customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds,
});Create tournament author Sendou name “In The Zone 30” → tournament
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
name: TOURNAMENT_NAME ,
});Create tournament team tournament id tournament.id memberUserIds team name “Alliance Rogue” prefers not to host 0 teamId is checked in true → linkedTournamentTeam
const linkedTournamentTeam = await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds,
team: { name: TEAM_NAME , prefersNotToHost: 0 , teamId },
},
{ isCheckedIn: true },
);Create 4 × user → opponents
const opponents = await factories.UserFactory .createMany (ROSTER_SIZE );Create tournament team tournament id tournament.id member user ids opponents.map ((opponent) => opponent.id) is checked in true
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: opponents.map ((opponent) => opponent.id),
},
{ isCheckedIn: true },
);Play out tournament tournament.id “all” → matches
const matches = await factories.TournamentFactory .playOut (
tournament.id,
"all" ,
);Team page Act
Open the team page customUrl
await team.goto (customUrl);Open results → results
const results = await team.openResults ();Assert
the page URL is /\/results/
await expect (page).toHaveURL (/\/results/);wonTheFinal = matches.some ( (match) => match.winnerTeamId === linkedTournamentTeam.id, )
const wonTheFinal = matches.some (
(match) => match.winnerTeamId === linkedTournamentTeam.id,
);Team results page Assert
result row “In The Zone 30” contains “/ 2”
await expect (results.resultRow (TOURNAMENT_NAME )).toContainText ("/ 2" );placement “In The Zone 30” , wonTheFinal ? "1st" : "2nd" is visible
await expect (
results.placement (TOURNAMENT_NAME , wonTheFinal ? "1st" : "2nd" ),
).toBeVisible ();Source test ("shows a finalized tournament placement on the results page" , async ({
page,
factories,
}) => {
const teammates = await factories.UserFactory .createMany (ROSTER_SIZE - 1 );
const memberUserIds = [
ADMIN_ID ,
...teammates.map ((teammate) => teammate.id),
];
const { id: teamId, customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds,
});
const tournament = await factories.TournamentFactory .create ({
authorId: ADMIN_ID ,
name: TOURNAMENT_NAME ,
});
const linkedTournamentTeam = await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds,
team: { name: TEAM_NAME , prefersNotToHost: 0 , teamId },
},
{ isCheckedIn: true },
);
const opponents = await factories.UserFactory .createMany (ROSTER_SIZE );
await factories.TournamentTeamFactory .create (
{
tournamentId: tournament.id,
memberUserIds: opponents.map ((opponent) => opponent.id),
},
{ isCheckedIn: true },
);
const matches = await factories.TournamentFactory .playOut (
tournament.id,
"all" ,
);
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
const results = await team.openResults ();
await expect (page).toHaveURL (/\/results/);
const wonTheFinal = matches.some (
(match) => match.winnerTeamId === linkedTournamentTeam.id,
);
await expect (results.resultRow (TOURNAMENT_NAME )).toContainText ("/ 2" );
await expect (
results.placement (TOURNAMENT_NAME , wonTheFinal ? "1st" : "2nd" ),
).toBeVisible ();
})Renders/ /t/:customUrl /t/:customUrl
Team page Arrange
Create team name “Alliance Rogue” member user ids Sendou → { customUrl }
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});Team page Act
Open the team page customUrl
await team.goto (customUrl);Open actions menu
await team.openActionsMenu ();Delete
await team.delete ();Assert
the page URL is not new RegExp (customUrl)
await expect (page).not.toHaveURL (new RegExp (customUrl));Source test ("deletes team" , async ({ page, factories }) => {
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
await team.openActionsMenu ();
await team.delete ();
await expect (page).not.toHaveURL (new RegExp (customUrl));
})resets invite code, joins team, leaves, rejoins Renders/t/:customUrl /t/:customUrl /t/:customUrl/join /t/:customUrl/roster
Team page Team roster page Join team page Team page Join team page Team page Arrange
Create team name “Alliance Rogue” member user ids Sendou → { customUrl }
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});Team page Act
Open the team page customUrl
await team.goto (customUrl);Open manage roster → roster
const roster = await team.openManageRoster ();Team roster page Act
Invite link → oldInviteLink
const oldInviteLink = await roster.inviteLink ();Reset invite link
await roster.resetInviteLink ();Assert
invite link does not read oldInviteLink
await expect (roster.locators.inviteLink).not.toHaveText (oldInviteLink);Act
Invite link → newInviteLink
const newInviteLink = await roster.inviteLink ();Join team page Act
Open the join team page newInviteLink
await join.goto (newInviteLink);Join
await join.join ();Team page Act
Open actions menu
await team.openActionsMenu ();Leave
await team.leave ();Join team page Act
Open the join team page newInviteLink
await join.goto (newInviteLink);Join
await join.join ();Team page Act
Open actions menu
await team.openActionsMenu ();Assert
leave team button is visible
await expect (team.locators.leaveTeamButton).toBeVisible ();Source test ("resets invite code, joins team, leaves, rejoins" , async ({
page,
factories,
}) => {
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
const roster = await team.openManageRoster ();
const oldInviteLink = await roster.inviteLink ();
await roster.resetInviteLink ();
await expect (roster.locators.inviteLink).not.toHaveText (oldInviteLink);
const newInviteLink = await roster.inviteLink ();
await impersonate (page, NZAP_TEST_ID );
const join = new JoinTeamPage (page);
await join.goto (newInviteLink);
await join.join ();
await team.openActionsMenu ();
await team.leave ();
await join.goto (newInviteLink);
await join.join ();
await team.openActionsMenu ();
await expect (team.locators.leaveTeamButton).toBeVisible ();
})joins a secondary team, makes main team & leaves making the seconary team the main one Renders/t/:customUrl /t/:customUrl /t/:customUrl/join /t/:customUrl/roster /u/:identifier /u/:identifier
Team page Team roster page Join team page Team page User page Team page User page Arrange
Create team name “Alliance Rogue” member user ids Sendou
await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});Create user → secondaryTeamOwner
const secondaryTeamOwner = await factories.UserFactory .create ();Create team name “Team Olive” member user ids secondaryTeamOwner.id → { customUrl: secondaryCustomUrl }
const { customUrl: secondaryCustomUrl } =
await factories.TeamFactory .create ({
name: SECONDARY_TEAM_NAME ,
memberUserIds: [secondaryTeamOwner.id],
});Team page Act
Open the team page secondaryCustomUrl
await secondaryTeam.goto (secondaryCustomUrl);Open manage roster → roster
const roster = await secondaryTeam.openManageRoster ();Team roster page Act
Invite link → inviteLink
const inviteLink = await roster.inviteLink ();Join team page Act
Open the join team page inviteLink
await join.goto (inviteLink);Join
await join.join ();Team page Act
Open actions menu
await secondaryTeam.openActionsMenu ();Make main team
await secondaryTeam.makeMainTeam ();User page Act
Open the user page Sendou
await user.goto (ADMIN_ID );Assert
team links count is 2
await expect (user.teamLinks ()).toHaveCount (2 );first team links does not contain “Alliance Rogue”
await expect (user.teamLinks ().first ()).not.toContainText (TEAM_NAME );Act
Open main team → mainTeam
const mainTeam = await user.openMainTeam ();Team page Act
Open actions menu
await mainTeam.openActionsMenu ();Assert
main team indicator is visible
await expect (mainTeam.locators.mainTeamIndicator).toBeVisible ();Act
Leave
await mainTeam.leave ();User page Act
Open the user page Sendou
await user.goto (ADMIN_ID );Assert
team links count is 1
await expect (user.teamLinks ()).toHaveCount (1 );first team links contains “Alliance Rogue”
await expect (user.teamLinks ().first ()).toContainText (TEAM_NAME );Source test ("joins a secondary team, makes main team & leaves making the seconary team the main one" , async ({
page,
factories,
}) => {
await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});
const secondaryTeamOwner = await factories.UserFactory .create ();
const { customUrl: secondaryCustomUrl } =
await factories.TeamFactory .create ({
name: SECONDARY_TEAM_NAME ,
memberUserIds: [secondaryTeamOwner.id],
});
await impersonate (page, secondaryTeamOwner.id);
const secondaryTeam = new TeamPage (page);
await secondaryTeam.goto (secondaryCustomUrl);
const roster = await secondaryTeam.openManageRoster ();
const inviteLink = await roster.inviteLink ();
await impersonate (page, ADMIN_ID );
const join = new JoinTeamPage (page);
await join.goto (inviteLink);
await join.join ();
await secondaryTeam.openActionsMenu ();
await secondaryTeam.makeMainTeam ();
const user = new UserPage (page);
await user.goto (ADMIN_ID );
await expect (user.teamLinks ()).toHaveCount (2 );
await expect (user.teamLinks ().first ()).not.toContainText (TEAM_NAME );
const mainTeam = await user.openMainTeam ();
await mainTeam.openActionsMenu ();
await expect (mainTeam.locators.mainTeamIndicator).toBeVisible ();
await mainTeam.leave ();
await user.goto (ADMIN_ID );
await expect (user.teamLinks ()).toHaveCount (1 );
await expect (user.teamLinks ().first ()).toContainText (TEAM_NAME );
})makes another user editor, who can edit the page & becomes owner after the original leaves Renders/t/:customUrl /t/:customUrl /t/:customUrl/edit /t/:customUrl/roster
Team page Team roster page Team edit page Team page Team page Arrange
Create team name “Alliance Rogue” member user ids Sendou , N-ZAP → { customUrl }
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID , NZAP_TEST_ID ],
});Team page Act
Open the team page customUrl
await team.goto (customUrl);Open manage roster → roster
const roster = await team.openManageRoster ();Team roster page Act
the owner has no editor toggle, so the first one belongs to N-ZAP
Make editor 0
await roster.makeEditor (0 );Save
await roster.save ();Team edit page Act
Open the team edit page customUrl
await teamEdit.goto (customUrl);Fill Bio with “from editor”
await teamEdit.form.fill ("bio" , "from editor" );Submit
await teamEdit.form.submit ();Assert
the page URL is new RegExp (customUrl)
await expect (page).toHaveURL (new RegExp (customUrl));Team page Assert
bio reads “from editor”
await expect (team.locators.bio).toHaveText ("from editor" );Team page Act
Open the team page customUrl
await team.goto (customUrl);Open actions menu
await team.openActionsMenu ();Start leaving
await team.startLeaving ();Assert
confirm dialog contains “New owner will be N-ZAP”
await expect (team.locators.confirmDialog).toContainText (
"New owner will be N-ZAP" ,
);Act
Confirm leaving
await team.confirmLeaving ();Assert
owner badge N-ZAP is visible
await expect (team.ownerBadge (NZAP_TEST_ID )).toBeVisible ();actions menu button is not visible
await isNotVisible (team.locators.actionsMenuButton);Source test ("makes another user editor, who can edit the page & becomes owner after the original leaves" , async ({
page,
factories,
}) => {
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID , NZAP_TEST_ID ],
});
await impersonate (page, ADMIN_ID );
const team = new TeamPage (page);
await team.goto (customUrl);
const roster = await team.openManageRoster ();
// the owner has no editor toggle, so the first one belongs to N-ZAP
await roster.makeEditor (0 );
await roster.save ();
await impersonate (page, NZAP_TEST_ID );
const teamEdit = new TeamEditPage (page);
await teamEdit.goto (customUrl);
await teamEdit.form.fill ("bio" , "from editor" );
await teamEdit.form.submit ();
await expect (page).toHaveURL (new RegExp (customUrl));
await expect (team.locators.bio).toHaveText ("from editor" );
await impersonate (page, ADMIN_ID );
await team.goto (customUrl);
await team.openActionsMenu ();
await team.startLeaving ();
await expect (team.locators.confirmDialog).toContainText (
"New owner will be N-ZAP" ,
);
await team.confirmLeaving ();
await expect (team.ownerBadge (NZAP_TEST_ID )).toBeVisible ();
await isNotVisible (team.locators.actionsMenuButton);
})Team schedule member sees the heatmap, the grid states and playable windows Renders/t/:customUrl /t/:customUrl /t/:customUrl/schedule
Team page Team schedule page Arrange
Create user discord name “Schedules-Later” → noScheduleMember
const noScheduleMember = await factories.UserFactory .create ({
discordName: "Schedules-Later" ,
});Create team name “Alliance Rogue” member user ids Sendou , N-ZAP , noScheduleMember.id → { id: teamId, customUrl }
const { id: teamId, customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID , NZAP_TEST_ID , noScheduleMember.id],
});{ startsAt } = weekRange ()
const { startsAt } = weekRange ();Create availability week user Sendou week starts at startsAt timezone MACHINE_TIMEZONE slots daySlot (WEDNESDAY , "18:00" , "22:00" ), daySlot (THURSDAY , "00:30" , "02:00" ) day notes date weekDates ()[WEDNESDAY ] text “Leaving early”
await factories.AvailabilityWeekFactory .create ({
userId: ADMIN_ID ,
weekStartsAt: startsAt,
timezone: MACHINE_TIMEZONE ,
// the small-hours slot guards day bucketing: on machines off UTC it
// falls on another UTC day, so it moves columns if the server ignores
// the viewer's timezone
slots: [
daySlot (WEDNESDAY , "18:00" , "22:00" ),
daySlot (THURSDAY , "00:30" , "02:00" ),
],
dayNotes: [{ date: weekDates ()[WEDNESDAY ], text: "Leaving early" }],
});Create availability week user N-ZAP week starts at startsAt timezone MACHINE_TIMEZONE slots daySlot (WEDNESDAY , "19:00" , "23:00" )
await factories.AvailabilityWeekFactory .create ({
userId: NZAP_TEST_ID ,
weekStartsAt: startsAt,
timezone: MACHINE_TIMEZONE ,
slots: [daySlot (WEDNESDAY , "19:00" , "23:00" )],
});a commitment late in the shared Wednesday evening: renders as a busy block beside the reported time in the grid and trims the heatmap's effective availability without removing the window
Create team event teamId author Sendou name “VoD review” ...daySlot (WEDNESDAY , "22:00" , "23:30" )
await factories.TeamEventFactory .create ({
teamId,
authorId: ADMIN_ID ,
name: "VoD review" ,
...daySlot (WEDNESDAY , "22:00" , "23:30" ),
});Team page Act
Open the team page customUrl
await team.goto (customUrl);Open schedule → schedule
const schedule = await team.openSchedule ();Team schedule page Assert
heatmap is the default view: two share Wed 19-22, one is also free Wed 18-19 and Thu 1-2
heatmap is visible
await expect (schedule.locators.heatmap).toBeVisible ();heatmap cells 2 count is 3
await expect (schedule.heatmapCells (2 )).toHaveCount (3 );heatmap cells 1 count is 2
await expect (schedule.heatmapCells (1 )).toHaveCount (2 );the count shade must actually paint: an equal-specificity base background once blanked the whole grid
heatmap cell background 2 is not schedule.heatmapCellBackground (0 )
expect (await schedule.heatmapCellBackground (2 )).not.toBe (
await schedule.heatmapCellBackground (0 ),
);heatmap unreported contains “Schedules-Later”
await expect (schedule.locators.heatmapUnreported).toContainText (
"Schedules-Later" ,
);day dot 2 is visible
await expect (schedule.dayDot (WEDNESDAY )).toBeVisible ();Act
hovering a block names who is free then and who has no schedule
Hover first heatmap cells 2
await schedule.heatmapCells (2 ).first ().hover ();Assert
heatmap tooltip contains “2/3”
await expect (schedule.locators.heatmapTooltip).toContainText ("2/3" );heatmap tooltip contains “N-ZAP”
await expect (schedule.locators.heatmapTooltip).toContainText ("N-ZAP" );heatmap tooltip contains “Schedules-Later”
await expect (schedule.locators.heatmapTooltip).toContainText (
"Schedules-Later" ,
);Act
dropping the member without a schedule from the count clears the nudge about them
Click member chip noScheduleMember.id
await schedule.memberChip (noScheduleMember.id).click ();Assert
heatmap unreported is not visible
await isNotVisible (schedule.locators.heatmapUnreported);Act
Click member chip noScheduleMember.id
await schedule.memberChip (noScheduleMember.id).click ();Click grid view tab
await schedule.locators.gridViewTab.click ();Assert
grid is visible
await expect (schedule.locators.grid).toBeVisible ();cell range Sendou , 2 is visible
await expect (schedule.cellRange (ADMIN_ID , WEDNESDAY )).toBeVisible ();cell range Sendou , 3 is visible
await expect (schedule.cellRange (ADMIN_ID , THURSDAY )).toBeVisible ();cell Sendou , 0 reads “—”
await expect (schedule.cell (ADMIN_ID , 0 )).toHaveText ("—" );cell noScheduleMember.id, 0 reads “?”
await expect (schedule.cell (noScheduleMember.id, 0 )).toHaveText ("?" );the grid shows the reported 19-23 in full, the commitment named beside it with its own time rather than cut out of it
cell range N-ZAP , 2 contains “11:00”
await expect (schedule.cellRange (NZAP_TEST_ID , WEDNESDAY )).toContainText (
"11:00" ,
);cell busy N-ZAP , 2 contains “VoD review”
await expect (schedule.cellBusy (NZAP_TEST_ID , WEDNESDAY )).toContainText (
"VoD review" ,
);cell busy N-ZAP , 2 contains “10:00”
await expect (schedule.cellBusy (NZAP_TEST_ID , WEDNESDAY )).toContainText (
"10:00" ,
);notes contains “Leaving early”
await expect (schedule.locators.notes).toContainText ("Leaving early" );two members share Wed 19-22 while the third has no schedule, so the only playable window is the one-short tier
windows reads /Wed /
await expect (schedule.locators.windows).toHaveText (/Wed /);day dot 2 is visible
await expect (schedule.dayDot (WEDNESDAY )).toBeVisible ();day dot 0 is not visible
await isNotVisible (schedule.dayDot (0 ));team events contains “VoD review”
await expect (schedule.locators.teamEvents).toContainText ("VoD review" );Source test ("member sees the heatmap, the grid states and playable windows" , async ({
page,
factories,
}) => {
const noScheduleMember = await factories.UserFactory .create ({
discordName: "Schedules-Later" ,
});
const { id: teamId, customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID , NZAP_TEST_ID , noScheduleMember.id],
});
const { startsAt } = weekRange ();
await factories.AvailabilityWeekFactory .create ({
userId: ADMIN_ID ,
weekStartsAt: startsAt,
timezone: MACHINE_TIMEZONE ,
// the small-hours slot guards day bucketing: on machines off UTC it
// falls on another UTC day, so it moves columns if the server ignores
// the viewer's timezone
slots: [
daySlot (WEDNESDAY , "18:00" , "22:00" ),
daySlot (THURSDAY , "00:30" , "02:00" ),
],
dayNotes: [{ date: weekDates ()[WEDNESDAY ], text: "Leaving early" }],
});
await factories.AvailabilityWeekFactory .create ({
userId: NZAP_TEST_ID ,
weekStartsAt: startsAt,
timezone: MACHINE_TIMEZONE ,
slots: [daySlot (WEDNESDAY , "19:00" , "23:00" )],
});
// a commitment late in the shared Wednesday evening: renders as a busy
// block beside the reported time in the grid and trims the heatmap's
// effective availability without removing the window
await factories.TeamEventFactory .create ({
teamId,
authorId: ADMIN_ID ,
name: "VoD review" ,
...daySlot (WEDNESDAY , "22:00" , "23:30" ),
});
await impersonate (page, ADMIN_ID );
await setTimezoneCookie (page);
const team = new TeamPage (page);
await team.goto (customUrl);
const schedule = await team.openSchedule ();
// heatmap is the default view: two share Wed 19-22, one is also free Wed 18-19 and Thu 1-2
await expect (schedule.locators.heatmap).toBeVisible ();
await expect (schedule.heatmapCells (2 )).toHaveCount (3 );
await expect (schedule.heatmapCells (1 )).toHaveCount (2 );
// the count shade must actually paint: an equal-specificity base background once blanked the whole grid
expect (await schedule.heatmapCellBackground (2 )).not.toBe (
await schedule.heatmapCellBackground (0 ),
);
await expect (schedule.locators.heatmapUnreported).toContainText (
"Schedules-Later" ,
);
await expect (schedule.dayDot (WEDNESDAY )).toBeVisible ();
// hovering a block names who is free then and who has no schedule
await schedule.heatmapCells (2 ).first ().hover ();
await expect (schedule.locators.heatmapTooltip).toContainText ("2/3" );
await expect (schedule.locators.heatmapTooltip).toContainText ("N-ZAP" );
await expect (schedule.locators.heatmapTooltip).toContainText (
"Schedules-Later" ,
);
// dropping the member without a schedule from the count clears the nudge about them
await schedule.memberChip (noScheduleMember.id).click ();
await isNotVisible (schedule.locators.heatmapUnreported);
await schedule.memberChip (noScheduleMember.id).click ();
await schedule.locators.gridViewTab.click ();
await expect (schedule.locators.grid).toBeVisible ();
await expect (schedule.cellRange (ADMIN_ID , WEDNESDAY )).toBeVisible ();
await expect (schedule.cellRange (ADMIN_ID , THURSDAY )).toBeVisible ();
await expect (schedule.cell (ADMIN_ID , 0 )).toHaveText ("—" );
await expect (schedule.cell (noScheduleMember.id, 0 )).toHaveText ("?" );
// the grid shows the reported 19-23 in full, the commitment named beside
// it with its own time rather than cut out of it
await expect (schedule.cellRange (NZAP_TEST_ID , WEDNESDAY )).toContainText (
"11:00" ,
);
await expect (schedule.cellBusy (NZAP_TEST_ID , WEDNESDAY )).toContainText (
"VoD review" ,
);
await expect (schedule.cellBusy (NZAP_TEST_ID , WEDNESDAY )).toContainText (
"10:00" ,
);
await expect (schedule.locators.notes).toContainText ("Leaving early" );
// two members share Wed 19-22 while the third has no schedule, so the
// only playable window is the one-short tier
await expect (schedule.locators.windows).toHaveText (/Wed /);
await expect (schedule.dayDot (WEDNESDAY )).toBeVisible ();
await isNotVisible (schedule.dayDot (0 ));
await expect (schedule.locators.teamEvents).toContainText ("VoD review" );
})hides the schedule from non-members, a friend of a member included Renders/t/:customUrl /t/:customUrl/schedule
Team schedule page Arrange
Create user → friend
const friend = await factories.UserFactory .create ();Create team name “Alliance Rogue” member user ids Sendou → { customUrl }
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});Create friendship user one Sendou user two id friend.id
await factories.FriendshipFactory .create ({
userOneId: ADMIN_ID ,
userTwoId: friend.id,
});Create availability week user Sendou week starts at weekRange ().startsAt timezone MACHINE_TIMEZONE slots daySlot (WEDNESDAY , "18:00" , "22:00" )
await factories.AvailabilityWeekFactory .create ({
userId: ADMIN_ID ,
weekStartsAt: weekRange ().startsAt,
timezone: MACHINE_TIMEZONE ,
slots: [daySlot (WEDNESDAY , "18:00" , "22:00" )],
});Team schedule page Act
Open the team schedule page customUrl
await schedule.goto (customUrl);Assert
hidden message is visible
await expect (schedule.locators.hiddenMessage).toBeVisible ();grid is not visible
await isNotVisible (schedule.locators.grid);Source test ("hides the schedule from non-members, a friend of a member included" , async ({
page,
factories,
}) => {
const friend = await factories.UserFactory .create ();
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID ],
});
await factories.FriendshipFactory .create ({
userOneId: ADMIN_ID ,
userTwoId: friend.id,
});
await factories.AvailabilityWeekFactory .create ({
userId: ADMIN_ID ,
weekStartsAt: weekRange ().startsAt,
timezone: MACHINE_TIMEZONE ,
slots: [daySlot (WEDNESDAY , "18:00" , "22:00" )],
});
await impersonate (page, friend.id);
const schedule = new TeamSchedulePage (page);
await schedule.goto (customUrl);
await expect (schedule.locators.hiddenMessage).toBeVisible ();
await isNotVisible (schedule.locators.grid);
})owner adds, edits and deletes a team event, a regular member only sees it Renders/t/:customUrl /t/:customUrl/schedule
Team schedule page Team schedule page Team schedule page Arrange
Create team name “Alliance Rogue” member user ids Sendou , N-ZAP → { customUrl }
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID , NZAP_TEST_ID ],
});Team schedule page Act
Open the team schedule page customUrl
await schedule.goto (customUrl);Click add event button
await schedule.locators.addEventButton.click ();Fill name with “VoD review vs. FTWin”
await form.fill ("name" , "VoD review vs. FTWin" );Set starts at to nextWeekTime (WEDNESDAY , "20:00" )
await form.setDateTime ("startsAt" , nextWeekTime (WEDNESDAY , "20:00" ));Select “90” for duration
await form.select ("duration" , "90" );Submit
await form.submit ();Click next week toggle
await schedule.locators.nextWeekToggle.click ();Assert
team events contains “VoD review vs. FTWin”
await expect (schedule.locators.teamEvents).toContainText (
"VoD review vs. FTWin" ,
);Act
Click element /edit-team-event/
await page.getByTestId (/edit-team-event/).click ();Fill name with “Strategy meeting”
await editForm.fill ("name" , "Strategy meeting" );Check items participants “SELECTED”
await editForm.checkItems ("participants" , ["SELECTED" ]);Click field “N-ZAP” , exact true
await page.getByLabel ("N-ZAP" , { exact: true }).click ();Submit
await editForm.submit ();Assert
team events contains “Strategy meeting”
await expect (schedule.locators.teamEvents).toContainText (
"Strategy meeting" ,
);team events contains “N-ZAP”
await expect (schedule.locators.teamEvents).toContainText ("N-ZAP" );Team schedule page Act
Open the team schedule page customUrl
await schedule.goto (customUrl);Click next week toggle
await schedule.locators.nextWeekToggle.click ();Assert
team events is visible
await expect (schedule.locators.teamEvents).toBeVisible ();add event button is not visible
await isNotVisible (schedule.locators.addEventButton);element /delete-team-event/ is not visible
await isNotVisible (page.getByTestId (/delete-team-event/));Team schedule page Act
Open the team schedule page customUrl
await schedule.goto (customUrl);Click next week toggle
await schedule.locators.nextWeekToggle.click ();Click element /delete-team-event/
await page.getByTestId (/delete-team-event/).click ();Click element “confirm-button”
await page.getByTestId ("confirm-button" ).click ();Assert
team events is not visible
await isNotVisible (schedule.locators.teamEvents);Source test ("owner adds, edits and deletes a team event, a regular member only sees it" , async ({
page,
factories,
}) => {
const { customUrl } = await factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID , NZAP_TEST_ID ],
});
await impersonate (page, ADMIN_ID );
await setTimezoneCookie (page);
const schedule = new TeamSchedulePage (page);
await schedule.goto (customUrl);
await schedule.locators.addEventButton.click ();
const form = createFormHelpers (page, addTeamEventSchema);
await form.fill ("name" , "VoD review vs. FTWin" );
await form.setDateTime ("startsAt" , nextWeekTime (WEDNESDAY , "20:00" ));
await form.select ("duration" , "90" );
await form.submit ();
await schedule.locators.nextWeekToggle.click ();
await expect (schedule.locators.teamEvents).toContainText (
"VoD review vs. FTWin" ,
);
await page.getByTestId (/edit-team-event/).click ();
const editForm = createFormHelpers (page, editTeamEventSchema);
await editForm.fill ("name" , "Strategy meeting" );
await editForm.checkItems ("participants" , ["SELECTED" ]);
await page.getByLabel ("N-ZAP" , { exact: true }).click ();
await editForm.submit ();
await expect (schedule.locators.teamEvents).toContainText (
"Strategy meeting" ,
);
await expect (schedule.locators.teamEvents).toContainText ("N-ZAP" );
await impersonate (page, NZAP_TEST_ID );
await schedule.goto (customUrl);
await schedule.locators.nextWeekToggle.click ();
await expect (schedule.locators.teamEvents).toBeVisible ();
await isNotVisible (schedule.locators.addEventButton);
await isNotVisible (page.getByTestId (/delete-team-event/));
await impersonate (page, ADMIN_ID );
await schedule.goto (customUrl);
await schedule.locators.nextWeekToggle.click ();
await page.getByTestId (/delete-team-event/).click ();
await page.getByTestId ("confirm-button" ).click ();
await isNotVisible (schedule.locators.teamEvents);
})AnythingAdder.adde2e/pages/layout/anything-adder.ts:28
No JSDoc
async add (item: AddableItem ) {
await this .locators.menuButton.click ();
await this .page.getByTestId (`menu-item-${item}` ).click ();
}createTeamSchema.nameapp/features/team/team-schemas.ts:56
name: textField ({
label: "labels.name" ,
minLength: TEAM .NAME_MIN_LENGTH ,
maxLength: TEAM .NAME_MAX_LENGTH ,
validate: teamNameValidate,
}) AnythingAddere2e/pages/layout/anything-adder.ts:17
NewTeamPagee2e/pages/team/new-team-page.ts:7
No JSDoc
TeamPage.gotoe2e/pages/team/team-page.ts:36
No JSDoc
async goto (customUrl: string ) {
await navigate ({ page: this .page, url: teamPage (customUrl) });
}TeamPage.openEdite2e/pages/team/team-page.ts:57
No JSDoc
async openEdit () {
await this .locators.editTeamButton.click ();
return new TeamEditPage (this .page);
}editTeamFormSchema.nameapp/features/team/team-schemas.ts:66
name: textField ({
label: "labels.name" ,
bottomText: "bottomTexts.name" ,
minLength: TEAM .NAME_MIN_LENGTH ,
maxLength: TEAM .NAME_MAX_LENGTH ,
validate: teamNameValidate,
}) editTeamFormSchema.bskyapp/features/team/team-schemas.ts:78
bsky: textFieldOptional ({
label: "labels.teamBsky" ,
leftAddon: "https://bsky.app/profile/" ,
inputMode: "url" ,
maxLength: TEAM .BSKY_MAX_LENGTH ,
}) editTeamFormSchema.bioapp/features/team/team-schemas.ts:84
bio: textAreaOptional ({
label: "labels.bio" ,
maxLength: TEAM .BIO_MAX_LENGTH ,
}) TeamPage.locators.bioe2e/pages/team/team-page.ts:20
No JSDoc
page.getByTestId ("team-bio" ) TeamPage.locators.bskyLinke2e/pages/team/team-page.ts:21
No JSDoc
page.getByTestId ("bsky-link" ).first () TeamPagee2e/pages/team/team-page.ts:13
No JSDoc
TeamEditPagee2e/pages/team/team-edit-page.ts:12
No JSDoc
TeamEditPage.gotoe2e/pages/team/team-edit-page.ts:31
No JSDoc
async goto (customUrl: string ) {
await navigate ({ page: this .page, url: editTeamPage (customUrl) });
}TeamEditPage.locators.removeMapPreferencesButtone2e/pages/team/team-edit-page.ts:25
No JSDoc
page.getByRole ("button" , {
name: "Remove team map preferences" ,
}) isNotVisiblee2e/helpers/playwright.ts:745
No JSDoc
export function isNotVisible (locator: Locator ) {
return expect (locator).toHaveCount (0 );
}TeamEditPage.saveMapPreferencese2e/pages/team/team-edit-page.ts:35
No JSDoc
async saveMapPreferences () {
await submit (this .page, "team-map-preferences-submit-button" );
}TeamEditPage.removeMapPreferencese2e/pages/team/team-edit-page.ts:39
No JSDoc
async removeMapPreferences () {
await this .locators.removeMapPreferencesButton.click ();
await modalClickConfirmButton (this .page);
}createFullTeame2e/team.spec.ts:412
async function createFullTeam (factories: Factories ) {
const members = await factories.UserFactory .createMany (ROSTER_SIZE - 1 );
return factories.TeamFactory .create ({
name: TEAM_NAME ,
memberUserIds: [ADMIN_ID , ...members.map ((member) => member.id)],
});
}TeamPage.ownerBadgee2e/pages/team/team-page.ts:44
No JSDoc
ownerBadge (userId: number ) {
return this .page.getByTestId (`member-owner-${userId}` );
}TeamPage.openManageRostere2e/pages/team/team-page.ts:52
No JSDoc
async openManageRoster () {
await this .locators.manageRosterButton.click ();
return new TeamRosterPage (this .page);
}TeamRosterPage.memberRowe2e/pages/team/team-roster-page.ts:26
No JSDoc
memberRow (nth: number ) {
return new MemberRow (this .page, nth);
}MemberRow.locators.rowe2e/pages/team/team-roster-page.ts:60
No JSDoc
this .rowMemberRow.removee2e/pages/team/team-roster-page.ts:81
No JSDoc
async remove () {
await this .locators.removeButton.click ();
}TeamRosterPage.savee2e/pages/team/team-roster-page.ts:43
No JSDoc
async save () {
await submit (this .page);
}TeamPage.memberRolee2e/pages/team/team-page.ts:40
No JSDoc
memberRole (nth: number ) {
return this .page.getByTestId (`member-row-role-${nth}` );
}TeamRosterPagee2e/pages/team/team-roster-page.ts:10
No JSDoc
MemberRowe2e/pages/team/team-roster-page.ts:48
No JSDoc
TeamPage.locators.otherRolesTabe2e/pages/team/team-page.ts:30
No JSDoc
page.getByRole ("tab" , { name: /Other / }) TeamPage.customRolee2e/pages/team/team-page.ts:48
No JSDoc
customRole (name: string ) {
return this .page.getByText (name).first ();
}MemberRow.locators.moveUpButtone2e/pages/team/team-roster-page.ts:66
No JSDoc
this .fieldset.getByRole ("button" , { name: "Move up" })MemberRow.locators.moveDownButtone2e/pages/team/team-roster-page.ts:67
No JSDoc
this .fieldset.getByRole ("button" , { name: "Move down" })MemberRow.moveDowne2e/pages/team/team-roster-page.ts:85
No JSDoc
async moveDown () {
await this .locators.moveDownButton.click ();
}MemberRow.locators.usernamee2e/pages/team/team-roster-page.ts:61
No JSDoc
page.getByTestId (`member-row-username-${index}` ) TeamPage.openResultse2e/pages/team/team-page.ts:62
No JSDoc
async openResults () {
await this .locators.resultsBannerLink.click ();
return new TeamResultsPage (this .page);
}TeamResultsPage.resultRowe2e/pages/team/team-results-page.ts:14
No JSDoc
resultRow (tournamentName: string ) {
return this .locators.table
.getByRole ("row" )
.filter ({ has: this .page.getByRole ("link" , { name: tournamentName }) });
}TeamResultsPage.placemente2e/pages/team/team-results-page.ts:21
placement (tournamentName: string , placementText: string ) {
return this .resultRow (tournamentName).getByRole ("img" , {
name: placementText,
});
}TeamResultsPagee2e/pages/team/team-results-page.ts:3
No JSDoc
TeamPage.openActionsMenue2e/pages/team/team-page.ts:72
No JSDoc
async openActionsMenu () {
await this .locators.actionsMenuButton.click ();
}TeamPage.deletee2e/pages/team/team-page.ts:99
async delete () {
await this .locators.deleteTeamButton.click ();
await modalClickConfirmButton (this .page);
}TeamRosterPage.inviteLinke2e/pages/team/team-roster-page.ts:30
No JSDoc
inviteLink () {
return this .locators.inviteLink.innerText ();
}TeamRosterPage.resetInviteLinke2e/pages/team/team-roster-page.ts:34
No JSDoc
async resetInviteLink () {
await submit (this .page, "reset-invite-link-button" );
}TeamRosterPage.locators.inviteLinke2e/pages/team/team-roster-page.ts:17
No JSDoc
page.getByTestId ("invite-link" ) JoinTeamPage.gotoe2e/pages/team/join-team-page.ts:13
async goto (inviteLink: string ) {
await navigate ({ page: this .page, url: inviteLink });
}JoinTeamPage.joine2e/pages/team/join-team-page.ts:17
No JSDoc
async join () {
await submit (this .page);
return new TeamPage (this .page);
}TeamPage.leavee2e/pages/team/team-page.ts:93
async leave () {
await this .startLeaving ();
await this .confirmLeaving ();
}TeamPage.locators.leaveTeamButtone2e/pages/team/team-page.ts:27
No JSDoc
page.getByTestId ("leave-team-button" ) JoinTeamPagee2e/pages/team/join-team-page.ts:5
No JSDoc
TeamPage.makeMainTeame2e/pages/team/team-page.ts:77
async makeMainTeam () {
await waitForPOSTResponse (this .page, () =>
this .locators.makeMainTeamButton.click (),
);
}UserPage.gotoe2e/pages/user/user-page.ts:35
No JSDoc
async goto (userId: number ) {
await navigate ({ page: this .page, url: userPage ({ id: userId }) });
}UserPage.teamLinkse2e/pages/user/user-page.ts:90
teamLinks () {
return this .widget ("teams" ).getByRole ("link" );
}UserPage.openMainTeame2e/pages/user/user-page.ts:111
No JSDoc
async openMainTeam () {
await this .teamLinks ().first ().click ();
return new TeamPage (this .page);
}TeamPage.locators.mainTeamIndicatore2e/pages/team/team-page.ts:25
No JSDoc
page.getByTestId ("main-team-indicator" ) UserPagee2e/pages/user/user-page.ts:12
No JSDoc
TeamRosterPage.makeEditore2e/pages/team/team-roster-page.ts:39
async makeEditor (nth: number ) {
await this .locators.editorToggles.nth (nth).click ({ force: true });
}TeamPage.startLeavinge2e/pages/team/team-page.ts:84
async startLeaving () {
await this .locators.leaveTeamButton.click ();
}TeamPage.locators.confirmDialoge2e/pages/team/team-page.ts:31
No JSDoc
page.getByRole ("dialog" ) TeamPage.confirmLeavinge2e/pages/team/team-page.ts:88
No JSDoc
async confirmLeaving () {
await modalClickConfirmButton (this .page);
}TeamPage.locators.actionsMenuButtone2e/pages/team/team-page.ts:24
No JSDoc
page.getByTestId ("team-actions-menu-button" ) TeamPage.openSchedulee2e/pages/team/team-page.ts:67
No JSDoc
async openSchedule () {
await this .locators.scheduleButton.click ();
return new TeamSchedulePage (this .page);
}TeamSchedulePage.locators.heatmape2e/pages/team/team-schedule-page.ts:13
No JSDoc
page.getByTestId ("schedule-heatmap" ) TeamSchedulePage.heatmapCellse2e/pages/team/team-schedule-page.ts:58
No JSDoc
heatmapCells (count: number ) {
return this .page.locator (
`[data-testid="schedule-heatmap-cell"][data-count="${count}"]` ,
);
}TeamSchedulePage.heatmapCellBackgrounde2e/pages/team/team-schedule-page.ts:64
No JSDoc
heatmapCellBackground (count: number ) {
return this .heatmapCells (count)
.first ()
.evaluate ((cell) => getComputedStyle (cell).backgroundColor);
}TeamSchedulePage.locators.heatmapUnreportede2e/pages/team/team-schedule-page.ts:15
No JSDoc
page.getByTestId ("schedule-heatmap-unreported" ) TeamSchedulePage.dayDote2e/pages/team/team-schedule-page.ts:50
No JSDoc
dayDot (dayIndex: number ) {
return this .page.getByTestId (`schedule-day-dot-${dayIndex}` );
}TeamSchedulePage.locators.heatmapTooltipe2e/pages/team/team-schedule-page.ts:14
No JSDoc
page.getByTestId ("schedule-heatmap-tooltip" ) TeamSchedulePage.memberChipe2e/pages/team/team-schedule-page.ts:54
No JSDoc
memberChip (userId: number ) {
return this .page.getByTestId (`heatmap-member-${userId}` );
}TeamSchedulePage.locators.gridViewTabe2e/pages/team/team-schedule-page.ts:27
No JSDoc
page.getByRole ("tab" , { name: "Grid" }) TeamSchedulePage.locators.gride2e/pages/team/team-schedule-page.ts:12
No JSDoc
page.getByTestId ("schedule-grid" ) TeamSchedulePage.cellRangee2e/pages/team/team-schedule-page.ts:42
No JSDoc
cellRange (userId: number , dayIndex: number ) {
return this .cell (userId, dayIndex).getByTestId ("schedule-range" );
}TeamSchedulePage.celle2e/pages/team/team-schedule-page.ts:38
No JSDoc
cell (userId: number , dayIndex: number ) {
return this .page.getByTestId (`schedule-cell-${userId}-${dayIndex}` );
}TeamSchedulePage.cellBusye2e/pages/team/team-schedule-page.ts:46
No JSDoc
cellBusy (userId: number , dayIndex: number ) {
return this .cell (userId, dayIndex).getByTestId ("schedule-busy" );
}TeamSchedulePage.locators.notese2e/pages/team/team-schedule-page.ts:19
No JSDoc
page.getByTestId ("schedule-note" ) TeamSchedulePage.locators.windowse2e/pages/team/team-schedule-page.ts:18
No JSDoc
page.getByTestId ("schedule-window" ) TeamSchedulePage.locators.teamEventse2e/pages/team/team-schedule-page.ts:20
No JSDoc
page.getByTestId ("schedule-team-event" ) TeamSchedulePagee2e/pages/team/team-schedule-page.ts:5
No JSDoc
TeamSchedulePage.gotoe2e/pages/team/team-schedule-page.ts:31
No JSDoc
async goto (customUrl: string ) {
await navigate ({
page: this .page,
url: `${teamPage(customUrl)}/schedule` ,
});
}TeamSchedulePage.locators.hiddenMessagee2e/pages/team/team-schedule-page.ts:17
No JSDoc
page.getByTestId ("schedule-hidden" ) TeamSchedulePage.locators.addEventButtone2e/pages/team/team-schedule-page.ts:21
No JSDoc
page.getByTestId ("add-team-event-button" ) TeamSchedulePage.locators.nextWeekTogglee2e/pages/team/team-schedule-page.ts:24
No JSDoc
page.locator (
'label[for="chip-radio-schedule-week-next"]' ,
)