a11y (main.css):
- Nuxt UI's default placeholder color (text-dimmed = #a6a09b) failed WCAG
AA contrast on cream (2.43:1) and white (2.58:1) backgrounds, blocking
axe checks on /member/profile (timezone) and /admin/events/create
(tags). Override [data-slot="placeholder"] globally to var(--text-dim)
(#5a5040), comfortably above 4.5:1 on both surfaces.
auth.spec.js (logout):
- Same hydration race as the board/admin-board-channels click tests:
/admin's sidebar Sign-out @click handler isn't bound when Playwright
fires the click immediately after admin-tag visibility, so the click
no-ops and waitForResponse for /api/auth/logout times out.
- Add waitForLoadState('networkidle') after goto so hydration completes
before the click.
65 lines
2.7 KiB
JavaScript
65 lines
2.7 KiB
JavaScript
import { test, expect } from '@playwright/test'
|
||
import { loginAsAdmin, loginAsMember } from './helpers/auth.js'
|
||
|
||
test.describe('Authentication flows', () => {
|
||
test('protected page shows sign-in prompt when logged out', async ({ page }) => {
|
||
// Navigate to a protected member page without being logged in
|
||
await page.goto('/member/dashboard')
|
||
|
||
// Modal auto-opens on load; close it via the × button and wait for it to dismiss
|
||
await page.locator('.modal-close').click()
|
||
await expect(page.getByRole('dialog')).toBeHidden({ timeout: 5000 })
|
||
|
||
// Page shows the unauth state with sign-in button
|
||
await expect(page.getByRole('heading', { name: 'Sign in required' })).toBeVisible({ timeout: 10000 })
|
||
await expect(page.getByRole('button', { name: 'Sign In' })).toBeVisible()
|
||
|
||
// Clicking Sign In re-opens the login modal with email input
|
||
await page.getByRole('button', { name: 'Sign In' }).click()
|
||
await expect(page.locator('.modal-title')).toBeVisible({ timeout: 5000 })
|
||
await expect(page.locator('input[type="email"]')).toBeVisible()
|
||
})
|
||
|
||
test('admin login sets auth cookie', async ({ page }) => {
|
||
await loginAsAdmin(page)
|
||
|
||
// Verify cookie was set
|
||
const cookies = await page.context().cookies()
|
||
const authCookie = cookies.find((c) => c.name === 'auth-token')
|
||
expect(authCookie).toBeTruthy()
|
||
|
||
// Navigate to admin page — should show admin layout
|
||
await page.goto('/admin')
|
||
await expect(page.locator('.admin-tag')).toBeVisible({ timeout: 15000 })
|
||
})
|
||
|
||
test('member login sets auth cookie', async ({ page }) => {
|
||
await loginAsMember(page, 'test-admin@ghostguild.dev')
|
||
|
||
const cookies = await page.context().cookies()
|
||
const authCookie = cookies.find((c) => c.name === 'auth-token')
|
||
expect(authCookie).toBeTruthy()
|
||
})
|
||
|
||
test('logout clears auth', async ({ page }) => {
|
||
await loginAsAdmin(page)
|
||
await page.goto('/admin')
|
||
await page.waitForLoadState('networkidle')
|
||
await expect(page.locator('.admin-tag')).toBeVisible({ timeout: 15000 })
|
||
|
||
// Set up response listener BEFORE clicking to avoid race
|
||
const logoutResponse = page.waitForResponse((resp) => resp.url().includes('/api/auth/logout'))
|
||
|
||
// Click the "Sign out" link in the sidebar meta area
|
||
await page.locator('.sidebar-meta a').filter({ hasText: 'Sign out' }).click()
|
||
|
||
// Wait for the logout API call to complete
|
||
await logoutResponse
|
||
|
||
// Navigating to a protected page should show the sign-in prompt
|
||
await page.goto('/member/dashboard')
|
||
await page.locator('.modal-close').click()
|
||
await expect(page.getByRole('dialog')).toBeHidden({ timeout: 5000 })
|
||
await expect(page.getByRole('heading', { name: 'Sign in required' })).toBeVisible({ timeout: 10000 })
|
||
})
|
||
})
|