diff --git a/e2e/helpers/auth.ts b/e2e/helpers/auth.ts index 8b3d0762b..ea267acce 100644 --- a/e2e/helpers/auth.ts +++ b/e2e/helpers/auth.ts @@ -15,11 +15,16 @@ export async function loginAsAdmin(page: Page): Promise { await loginAs(page, 'admin'); } -/** Logs in as a general (non-admin) user and waits for redirect to home. */ +/** Logs in as a general (non-admin) user verified with a fictional AtCoder handle. */ export async function loginAsUser(page: Page): Promise { await loginAs(page, 'guest'); } +/** Logs in as a general user without AtCoder verification and waits for redirect to home. */ +export async function loginAsUnverifiedUser(page: Page): Promise { + await loginAs(page, 'Alice'); +} + /** * Fills and submits the login form without navigating first. * Use when already on /login?redirectTo=... to preserve the redirect destination. diff --git a/e2e/user_edit.spec.ts b/e2e/user_edit.spec.ts index 39bca448b..f2f7510e3 100644 --- a/e2e/user_edit.spec.ts +++ b/e2e/user_edit.spec.ts @@ -1,5 +1,5 @@ -import { test, expect } from '@playwright/test'; -import { loginAsUser } from './helpers/auth'; +import { test, expect, type Page } from '@playwright/test'; +import { loginAsUnverifiedUser, loginAsUser } from './helpers/auth'; const TIMEOUT = 60 * 1000; const SHORT_TIMEOUT = 5000; @@ -7,31 +7,59 @@ const SHORT_TIMEOUT = 5000; // Mirrors /users/edit route const EDIT_PAGE_URL = '/users/edit'; +const BASIC_INFO_TAB_NAME = '基本情報'; +const ATCODER_TAB_NAME = 'AtCoder IDを設定'; + +function getTab(page: Page, name: string) { + return page.getByRole('tab', { name }); +} + +async function openEditPage(page: Page): Promise { + await page.goto(EDIT_PAGE_URL); + // Wait for page to stabilize before running assertions + await expect(getTab(page, BASIC_INFO_TAB_NAME)).toBeVisible({ timeout: TIMEOUT }); +} + test.describe('user edit page (/users/edit)', () => { test('unauthenticated user is redirected to /login', async ({ page }) => { await page.goto(EDIT_PAGE_URL); await expect(page).toHaveURL(/\/login/, { timeout: TIMEOUT }); }); - test.describe('logged-in user (guest)', () => { + test.describe('logged-in user without AtCoder verification (Alice)', () => { test.beforeEach(async ({ page }) => { - await loginAsUser(page); - await page.goto(EDIT_PAGE_URL); - // Wait for page to stabilize before running assertions - await expect(page.getByRole('tab', { name: '基本情報' })).toBeVisible({ timeout: TIMEOUT }); + await loginAsUnverifiedUser(page); + await openEditPage(page); }); test('基本情報 tab is active by default', async ({ page }) => { - // Tab visibility already asserted in beforeEach; check active state only - await expect(page.getByRole('tab', { name: '基本情報' })).toHaveAttribute( - 'aria-selected', - 'true', - { timeout: SHORT_TIMEOUT }, - ); + await expect(getTab(page, BASIC_INFO_TAB_NAME)).toHaveAttribute('aria-selected', 'true', { + timeout: SHORT_TIMEOUT, + }); + await expect(getTab(page, ATCODER_TAB_NAME)).toHaveAttribute('aria-selected', 'false'); + }); + }); + + test.describe('logged-in user with AtCoder verification (guest)', () => { + test.beforeEach(async ({ page }) => { + await loginAsUser(page); + await openEditPage(page); }); - test('username is displayed in the 基本情報 tab', async ({ page }) => { - // Assert the username label input shows the logged-in user's name + test('AtCoder IDを設定 tab is active by default', async ({ page }) => { + await expect(getTab(page, ATCODER_TAB_NAME)).toHaveAttribute('aria-selected', 'true', { + timeout: SHORT_TIMEOUT, + }); + await expect(getTab(page, BASIC_INFO_TAB_NAME)).toHaveAttribute('aria-selected', 'false'); + }); + + test('username is displayed after opening the 基本情報 tab', async ({ page }) => { + // The AtCoder tab also renders a ユーザ名 field, so confirm the 基本情報 tab is selected first + await getTab(page, BASIC_INFO_TAB_NAME).click(); + await expect(getTab(page, BASIC_INFO_TAB_NAME)).toHaveAttribute('aria-selected', 'true', { + timeout: SHORT_TIMEOUT, + }); + await expect(page.getByRole('textbox', { name: 'ユーザ名' })).toHaveValue('guest', { timeout: SHORT_TIMEOUT, }); @@ -39,7 +67,7 @@ test.describe('user edit page (/users/edit)', () => { test('アカウント削除 tab is not shown for the guest user', async ({ page }) => { // guest is excluded from account deletion (isGeneralUser returns false for username 'guest') - await expect(page.getByRole('tab', { name: 'アカウント削除' })).not.toBeVisible({ + await expect(getTab(page, 'アカウント削除')).not.toBeVisible({ timeout: SHORT_TIMEOUT, }); });