import { Page, expect } from '@playwright/test'; import { AuthPageObject } from '../authentication/auth.po'; import { OtpPo } from '../utils/otp.po'; export class TeamAccountsPageObject { private readonly page: Page; public auth: AuthPageObject; public otp: OtpPo; constructor(page: Page) { this.page = page; this.auth = new AuthPageObject(page); this.otp = new OtpPo(page); } async setup(params = this.createTeamName()) { const auth = new AuthPageObject(this.page); const email = auth.createRandomEmail(); await auth.bootstrapUser({ email, name: 'Test User', }); await auth.loginAsUser({ email }); await this.createTeam(params); return { email: email, teamName: params.teamName, slug: params.slug, }; } getTeamFromSelector(teamName: string) { return this.page.locator('[data-test="workspace-team-item"]', { hasText: teamName, }); } getTeams() { return this.page.locator('[data-test="workspace-team-item"]'); } goToSettings() { return expect(async () => { await this.page .locator('a', { hasText: 'Settings', }) .click(); await this.page.waitForURL('**/home/*/settings'); }).toPass(); } goToMembers() { return expect(async () => { await this.page .locator('a', { hasText: 'Members', }) .click(); await this.page.waitForURL('**/home/*/members'); }).toPass(); } goToBilling() { return expect(async () => { await this.page .locator('a', { hasText: 'Billing', }) .click(); return await this.page.waitForURL('**/home/*/billing'); }).toPass(); } openAccountsSelector() { return expect(async () => { await this.page.click('[data-test="workspace-dropdown-trigger"]'); await this.page.click('[data-test="workspace-switch-submenu"]'); return expect( this.page.locator('[data-test="workspace-switch-content"]'), ).toBeVisible(); }).toPass(); } async tryCreateTeam(teamName: string, slug?: string) { const nameInput = this.page.locator( '[data-test="create-team-form"] [data-test="team-name-input"]', ); await nameInput.fill(''); await nameInput.fill(teamName); // If slug is provided (for non-Latin names), fill the slug field if (slug) { const slugInput = this.page.locator( '[data-test="create-team-form"] [data-test="team-slug-input"]', ); await expect(slugInput).toBeVisible(); await slugInput.fill(slug); } return this.page.click('[data-test="create-team-form"] button:last-child'); } async createTeam({ teamName, slug } = this.createTeamName()) { await this.openAccountsSelector(); await this.page.click('[data-test="create-team-trigger"]'); await this.page.fill( '[data-test="create-team-form"] [data-test="team-name-input"]', teamName, ); // Slug field is only shown for non-Latin names, so we don't fill it for Latin names // The database trigger will auto-generate the slug from the name const click = this.page.click( '[data-test="create-team-form"] button:last-child', ); const response = this.page.waitForURL(`/home/${slug}`); await Promise.all([click, response]); // Verify user landed on the team page await expect(this.page).toHaveURL(`/home/${slug}`); // Verify the team was created and appears in the selector await this.openAccountsSelector(); await expect(this.getTeamFromSelector(teamName)).toBeVisible(); await this.closeAccountsSelector(); } async createTeamWithNonLatinName(teamName: string, slug: string) { await this.openAccountsSelector(); await this.page.click('[data-test="create-team-trigger"]'); await this.page.fill( '[data-test="create-team-form"] [data-test="team-name-input"]', teamName, ); // Wait for slug field to appear (triggered by non-Latin name) await expect(this.getSlugField()).toBeVisible(); await this.page.fill( '[data-test="create-team-form"] [data-test="team-slug-input"]', slug, ); const click = this.page.click( '[data-test="create-team-form"] button:last-child', ); const response = this.page.waitForURL(`/home/${slug}`); await Promise.all([click, response]); // Verify user landed on the team page await expect(this.page).toHaveURL(`/home/${slug}`); // Verify the team was created and appears in the selector await this.openAccountsSelector(); await expect(this.getTeamFromSelector(teamName)).toBeVisible(); await this.closeAccountsSelector(); } async closeAccountsSelector() { await this.page.locator('body').click({ position: { x: 0, y: 0 } }); await expect( this.page.locator('[data-test="workspace-switch-content"]'), ).toBeHidden(); } getSlugField() { return this.page.locator( '[data-test="create-team-form"] [data-test="team-slug-input"]', ); } async updateTeamName(name: string) { await expect(async () => { await this.page.fill( '[data-test="update-team-account-name-form"] input', name, ); await Promise.all([ this.page.click('[data-test="update-team-account-name-form"] button'), this.page.waitForResponse((response) => { return ( response.url().includes('settings') && response.request().method() === 'POST' ); }), ]); }).toPass(); } async deleteAccount(email: string) { await this.page.click('[data-test="delete-team-trigger"]'); await this.otp.completeOtpVerification(email); await expect(async () => { const click = this.page.click( '[data-test="delete-team-form-confirm-button"]', ); const response = this.page.waitForURL('**/home'); return Promise.all([click, response]); }).toPass(); } async updateMemberRole(memberEmail: string, newRole: string) { await expect(async () => { // Find the member row and click the actions button const memberRow = this.page.getByRole('row', { name: memberEmail }); await memberRow.getByRole('button').click(); // Click the update role option in the dropdown menu await this.page.getByText('Update Role').click(); // Select the new role await this.page.click('[data-test="role-selector-trigger"]'); await this.page.click(`[data-test="role-option-${newRole}"]`); // Wait for the update to complete and page to reload const response = this.page.waitForResponse((response) => { return ( response.url().includes('members') && response.request().method() === 'POST' ); }); return Promise.all([ this.page.click('[data-test="confirm-update-member-role"]'), response, ]); }).toPass(); } async transferOwnership(memberEmail: string, ownerEmail: string) { await expect(async () => { // Find the member row and click the actions button const memberRow = this.page.getByRole('row', { name: memberEmail }); await memberRow.getByRole('button').click(); // Click the transfer ownership option in the dropdown menu await this.page.getByText('Transfer Ownership').click(); // Complete OTP verification await this.otp.completeOtpVerification(ownerEmail); // Wait for the transfer to complete and page to reload const response = this.page.waitForResponse('**/members'); return Promise.all([ this.page.click('[data-test="confirm-transfer-ownership-button"]'), response, ]); }).toPass(); } createTeamName() { const random = (Math.random() * 100000000).toFixed(0); const teamName = `Team-Name-${random}`; const slug = `team-name-${random}`; return { teamName, slug }; } }