Add comprehensive testing covering 420 unit/handler tests across 24 Vitest files, 9 Playwright E2E specs, accessibility scans, and visual regression. Includes GitHub Actions CI, Husky pre-push hook, and TESTING.md docs.
123 lines
4.1 KiB
JavaScript
123 lines
4.1 KiB
JavaScript
import { test, expect } from './helpers/fixtures.js'
|
|
|
|
test.describe('My Updates page', () => {
|
|
test('authenticated user sees the my-updates page', async ({ adminPage }) => {
|
|
await adminPage.goto('/member/my-updates')
|
|
|
|
await expect(adminPage.locator('h1', { hasText: 'My Updates' })).toBeVisible({
|
|
timeout: 10000,
|
|
})
|
|
})
|
|
|
|
test('authenticated user sees the new update link', async ({ adminPage }) => {
|
|
await adminPage.goto('/member/my-updates')
|
|
|
|
// Wait for ClientOnly content to hydrate
|
|
await expect(adminPage.locator('h1', { hasText: 'My Updates' })).toBeVisible({
|
|
timeout: 10000,
|
|
})
|
|
|
|
// The page shows either the "+ New Update" button (stats row) or
|
|
// the "+ Post Your First Update" link (empty state) — both go to /updates/new
|
|
const newUpdateLink = adminPage.locator('a[href="/updates/new"]')
|
|
await expect(newUpdateLink.first()).toBeVisible({ timeout: 10000 })
|
|
})
|
|
|
|
test('unauthenticated user sees sign-in prompt', async ({ browser }) => {
|
|
const context = await browser.newContext()
|
|
const page = await context.newPage()
|
|
|
|
await page.goto('/member/my-updates')
|
|
|
|
await expect(
|
|
page
|
|
.getByText('Sign in required')
|
|
.or(page.getByText('Sign in to view your updates'))
|
|
).toBeVisible({ timeout: 10000 })
|
|
|
|
await context.close()
|
|
})
|
|
})
|
|
|
|
test.describe('New Update page', () => {
|
|
test('loads the new update form', async ({ adminPage }) => {
|
|
await adminPage.goto('/updates/new')
|
|
|
|
await expect(adminPage.locator('h1', { hasText: 'New Update' })).toBeVisible({
|
|
timeout: 10000,
|
|
})
|
|
|
|
// Form elements are present
|
|
await expect(adminPage.locator('textarea')).toBeVisible()
|
|
await expect(adminPage.locator('select')).toBeVisible()
|
|
|
|
// Submit button exists and starts disabled (empty textarea)
|
|
const submitBtn = adminPage.locator('button[type="submit"]')
|
|
await expect(submitBtn).toBeVisible()
|
|
await expect(submitBtn).toBeDisabled()
|
|
})
|
|
|
|
test('submit button enables when content is entered', async ({ adminPage }) => {
|
|
await adminPage.goto('/updates/new')
|
|
|
|
await expect(adminPage.locator('h1', { hasText: 'New Update' })).toBeVisible({
|
|
timeout: 10000,
|
|
})
|
|
|
|
const textarea = adminPage.locator('textarea')
|
|
const submitBtn = adminPage.locator('button[type="submit"]')
|
|
|
|
await expect(submitBtn).toBeDisabled()
|
|
await textarea.fill('Test update content')
|
|
await expect(submitBtn).toBeEnabled()
|
|
})
|
|
|
|
test('privacy selector defaults to members and has all options', async ({ adminPage }) => {
|
|
await adminPage.goto('/updates/new')
|
|
|
|
await expect(adminPage.locator('h1', { hasText: 'New Update' })).toBeVisible({
|
|
timeout: 10000,
|
|
})
|
|
|
|
const select = adminPage.locator('select')
|
|
await expect(select).toHaveValue('members')
|
|
|
|
// Verify all three privacy options exist
|
|
await expect(select.locator('option[value="members"]')).toBeAttached()
|
|
await expect(select.locator('option[value="public"]')).toBeAttached()
|
|
await expect(select.locator('option[value="private"]')).toBeAttached()
|
|
})
|
|
|
|
test('cancel link navigates back to my-updates', async ({ adminPage }) => {
|
|
await adminPage.goto('/updates/new')
|
|
|
|
await expect(adminPage.locator('h1', { hasText: 'New Update' })).toBeVisible({
|
|
timeout: 10000,
|
|
})
|
|
|
|
const cancelLink = adminPage.locator('a', { hasText: 'Cancel' })
|
|
await expect(cancelLink).toHaveAttribute('href', '/member/my-updates')
|
|
})
|
|
|
|
test('back link points to my-updates', async ({ adminPage }) => {
|
|
await adminPage.goto('/updates/new')
|
|
|
|
const backLink = adminPage.locator('.back-link a')
|
|
await expect(backLink).toBeVisible({ timeout: 10000 })
|
|
await expect(backLink).toHaveAttribute('href', '/member/my-updates')
|
|
})
|
|
})
|
|
|
|
test.describe('Updates API (public access)', () => {
|
|
test('public updates endpoint returns data', async ({ page }) => {
|
|
const response = await page.request.get('/api/updates')
|
|
|
|
expect(response.ok()).toBe(true)
|
|
|
|
const data = await response.json()
|
|
expect(data).toHaveProperty('updates')
|
|
expect(data).toHaveProperty('total')
|
|
expect(data).toHaveProperty('hasMore')
|
|
expect(Array.isArray(data.updates)).toBe(true)
|
|
})
|
|
})
|