import { Page, expect, test } from '@playwright/test'; import { AuthPageObject } from '../authentication/auth.po'; import { TeamAccountsPageObject } from '../team-accounts/team-accounts.po'; import { AUTH_STATES } from '../utils/auth-state'; test.describe('Admin Auth flow without MFA', () => { AuthPageObject.setupSession(AUTH_STATES.OWNER_USER); test('will return a 404 for non-admin users', async ({ page }) => { await page.goto('/admin'); expect(page.url()).toContain('/404'); }); }); test.describe('Admin Auth flow with Super Admin but without MFA', () => { AuthPageObject.setupSession(AUTH_STATES.TEST_USER); test('will redirect to 404 for admin users without MFA', async ({ page }) => { await page.goto('/admin'); expect(page.url()).toContain('/404'); }); }); test.describe('Admin', () => { test.describe.configure({ mode: 'serial' }); test.describe('Admin Dashboard', () => { AuthPageObject.setupSession(AUTH_STATES.SUPER_ADMIN); test('displays all stat cards', async ({ page }) => { await page.goto('/admin'); // Check all stat cards are present await expect(page.getByRole('heading', { name: 'Users' })).toBeVisible(); await expect( page.getByRole('heading', { name: 'Team Accounts' }), ).toBeVisible(); await expect( page.getByRole('heading', { name: 'Paying Customers' }), ).toBeVisible(); await expect(page.getByRole('heading', { name: 'Trials' })).toBeVisible(); // Verify stat values are numbers const stats = await page.$$('.text-3xl.font-bold'); for (const stat of stats) { const value = await stat.textContent(); expect(Number.isInteger(Number(value))).toBeTruthy(); } }); }); test.describe('Personal Account Management', () => { AuthPageObject.setupSession(AUTH_STATES.SUPER_ADMIN); let testUserEmail: string; test.beforeEach(async ({ page }) => { // Create a new test user before each test testUserEmail = await createUser(page); await page.goto(`/admin/accounts`); // use the email as the filter text const filterText = testUserEmail; await expect(async () => { await filterAccounts(page, filterText); await selectAccount(page, filterText); }).toPass(); }); test('ban user flow', async ({ page }) => { await page.getByTestId('admin-ban-account-button').click(); await expect( page.getByRole('heading', { name: 'Ban User' }), ).toBeVisible(); // Try with invalid confirmation await page.fill('[placeholder="Type CONFIRM to confirm"]', 'WRONG'); await page.getByRole('button', { name: 'Ban User' }).click(); await expect( page.getByRole('heading', { name: 'Ban User' }), ).toBeVisible(); // Dialog should still be open // Confirm with correct text await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM'); await Promise.all([ page.getByRole('button', { name: 'Ban User' }).click(), page.waitForResponse( (response) => response.url().includes('/admin/accounts') && response.request().method() === 'POST', ), ]); await expect(page.getByText('Banned').first()).toBeVisible(); await page.context().clearCookies(); // Verify user can't log in await page.goto('/auth/sign-in'); const auth = new AuthPageObject(page); await auth.signIn({ email: testUserEmail, password: 'testingpassword', }); // Should show an error message await expect( page.locator('[data-test="auth-error-message"]'), ).toBeVisible(); }); test('reactivate user flow', async ({ page }) => { // First ban the user await page.getByTestId('admin-ban-account-button').click(); await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM'); await page.getByRole('button', { name: 'Ban User' }).click(); await expect(page.getByText('Banned').first()).toBeVisible(); // Now reactivate await page.getByTestId('admin-reactivate-account-button').click(); await expect( page.getByRole('heading', { name: 'Reactivate User' }), ).toBeVisible(); await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM'); await Promise.all([ page.getByRole('button', { name: 'Reactivate User' }).click(), page.waitForResponse( (response) => response.url().includes('/admin/accounts') && response.request().method() === 'POST', ), ]); await page.waitForTimeout(250); // Verify ban badge is removed await expect(page.getByText('Banned')).not.toBeVisible(); // Log out await page.context().clearCookies(); // Verify user can log in again await page.goto('/auth/sign-in'); const auth = new AuthPageObject(page); await auth.loginAsUser({ email: testUserEmail, }); }); test('delete user flow', async ({ page }) => { const auth = new AuthPageObject(page); await page.getByTestId('admin-delete-account-button').click(); await expect( page.getByRole('heading', { name: 'Delete User' }), ).toBeVisible(); // Try with invalid confirmation await page.fill('[placeholder="Type CONFIRM to confirm"]', 'WRONG'); await page.getByRole('button', { name: 'Delete' }).click(); await expect( page.getByRole('heading', { name: 'Delete User' }), ).toBeVisible(); // Dialog should still be open // Confirm with correct text await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM'); await page.getByRole('button', { name: 'Delete' }).click(); // Should redirect to admin dashboard await page.waitForURL('/admin/accounts'); // Log out await auth.signOut(); await page.waitForURL('/'); await auth.goToSignIn(); await auth.signIn({ email: testUserEmail, password: 'testingpassword', }); // Should show an error message await expect( page.locator('[data-test="auth-error-message"]'), ).toBeVisible(); }); }); test.describe('Impersonation', () => { test('can sign in as a user', async ({ page }) => { // TODO: find out why it only fails in the CI if (process.env.CI) { test.skip(); } const auth = new AuthPageObject(page); await auth.loginAsSuperAdmin({}); const filterText = await createUser(page); await page.goto(`/admin/accounts`); await expect(async () => { await filterAccounts(page, filterText); await selectAccount(page, filterText); }).toPass(); await page.getByTestId('admin-impersonate-button').click(); await expect( page.getByRole('heading', { name: 'Impersonate User' }), ).toBeVisible(); await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM'); await page.getByRole('button', { name: 'Impersonate User' }).click(); // Should redirect to home and be logged in as the user await page.waitForURL('/home'); }); }); }); test.describe('Team Account Management', () => { test.describe.configure({ mode: 'serial' }); test.skip( process.env.ENABLE_TEAM_ACCOUNT_TESTS !== 'true', 'Team account tests are disabled', ); let testUserEmail: string; let teamName: string; let slug: string; test.beforeEach(async ({ page }) => { const auth = new AuthPageObject(page); // Create a new test user and team account testUserEmail = await createUser(page); teamName = `test-${Math.random().toString(36).substring(2, 15)}`; await auth.loginAsUser({ email: testUserEmail }); const teamAccountPo = new TeamAccountsPageObject(page); const teamSlug = teamName.toLowerCase().replace(/ /g, '-'); slug = teamSlug; await teamAccountPo.createTeam({ teamName, slug, }); await page.waitForTimeout(250); await auth.signOut(); await page.waitForURL('/'); await auth.loginAsSuperAdmin({}); await page.goto(`/admin/accounts`); await expect(async () => { await filterAccounts(page, teamName); await selectAccount(page, teamName); }).toPass(); }); test('delete team account flow', async ({ page }) => { await expect(page.getByText('Team Account')).toBeVisible(); await page.getByTestId('admin-delete-account-button').click(); await expect( page.getByRole('heading', { name: 'Delete Account' }), ).toBeVisible(); // Try with invalid confirmation await page.fill('[placeholder="Type CONFIRM to confirm"]', 'WRONG'); await page.getByRole('button', { name: 'Delete' }).click(); await expect( page.getByRole('heading', { name: 'Delete Account' }), ).toBeVisible(); // Dialog should still be open // Confirm with correct text await page.fill('[placeholder="Type CONFIRM to confirm"]', 'CONFIRM'); await page.getByRole('button', { name: 'Delete' }).click(); // Should redirect to admin dashboard after deletion await expect(page).toHaveURL('/admin/accounts'); }); }); async function createUser(page: Page) { const auth = new AuthPageObject(page); const password = 'testingpassword'; const email = auth.createRandomEmail(); // create user using bootstrap method await auth.bootstrapUser({ email, password, name: 'Test User', }); // return the email return email; } async function filterAccounts(page: Page, email: string) { await page .locator('[data-test="admin-accounts-table-filter-input"]') .first() .fill(email); await page.keyboard.press('Enter'); await page.waitForTimeout(250); } async function selectAccount(page: Page, email: string) { const link = page .locator('tr', { hasText: email.split('@')[0] }) .locator('a'); await expect(link).toBeVisible(); await link.click(); await page.waitForURL(/\/admin\/accounts\/[^\/]+/); }