import { test, expect } from './helpers/fixtures.js' import { loginAsMember } from './helpers/auth.js' // The default `memberPage` fixture authenticates as test-admin@ghostguild.dev, // the same account auth.spec.js's logout test revokes mid-suite. Bypass the // fixture and use a seeded, non-shared member instead so cross-file logout // can't strand this file mid-flow. const SEEDED_MEMBER_EMAIL = 'riley.johnson@cooperativedev.org' const newMemberPage = async (browser) => { const context = await browser.newContext() const page = await context.newPage() await loginAsMember(page, SEEDED_MEMBER_EMAIL) return { context, page } } test.describe('Board page', () => { test('page loads for authenticated member', async ({ browser }) => { const { context, page: memberPage } = await newMemberPage(browser) try { await memberPage.goto('/board') await expect(memberPage.getByRole('heading', { name: 'Bulletin Board' })).toBeVisible({ timeout: 15000 }) await expect(memberPage.getByRole('button', { name: '+ New Post' }).first()).toBeVisible() } finally { await context.close() } }) test('clicking New Post reveals the form', async ({ browser }) => { const { context, page: memberPage } = await newMemberPage(browser) try { await memberPage.goto('/board') await memberPage.waitForLoadState('networkidle') await expect(memberPage.getByRole('button', { name: '+ New Post' }).first()).toBeVisible({ timeout: 15000, }) await memberPage.getByRole('button', { name: '+ New Post' }).first().click() await expect(memberPage.getByRole('heading', { name: 'New post' })).toBeVisible() await expect(memberPage.locator('#post-title')).toBeVisible() await expect(memberPage.locator('#post-seeking')).toBeVisible() } finally { await context.close() } }) test('tags drawer toggles open and closed', async ({ browser }) => { const { context, page: memberPage } = await newMemberPage(browser) try { await memberPage.goto('/board') await expect(memberPage.getByRole('heading', { name: 'Bulletin Board' })).toBeVisible({ timeout: 15000 }) const drawerToggle = memberPage.getByRole('button', { name: /^Tags\.\.\./ }) // Drawer toggle only appears if cooperative tags exist — skip quietly if not if (!(await drawerToggle.isVisible().catch(() => false))) { test.skip(true, 'No cooperative tags seeded in this environment') return } await drawerToggle.click() await expect(memberPage.getByText('Filter:')).toBeVisible() await drawerToggle.click() await expect(memberPage.getByText('Filter:')).not.toBeVisible() } finally { await context.close() } }) test('create, edit, and delete own post', async ({ browser }) => { const { context, page: memberPage } = await newMemberPage(browser) try { await memberPage.goto('/board') await memberPage.waitForLoadState('networkidle') await expect(memberPage.getByRole('button', { name: '+ New Post' }).first()).toBeVisible({ timeout: 15000, }) const uniqueSuffix = Date.now().toString().slice(-6) const originalTitle = `E2E test post ${uniqueSuffix}` const editedTitle = `E2E test post edited ${uniqueSuffix}` // --- Create --- await memberPage.getByRole('button', { name: '+ New Post' }).first().click() await expect(memberPage.getByRole('heading', { name: 'New post' })).toBeVisible() await memberPage.locator('#post-title').fill(originalTitle) await memberPage.locator('#post-seeking').fill('Playwright test seeking text') await memberPage.getByRole('button', { name: 'Post', exact: true }).click() await expect(memberPage.getByRole('heading', { name: originalTitle })).toBeVisible({ timeout: 10000, }) // --- Edit --- // Find the post card containing our title, then click its Edit button const postCard = memberPage.locator('article.board-post', { hasText: originalTitle }) await postCard.getByRole('button', { name: 'Edit' }).click() await expect(memberPage.getByRole('heading', { name: 'Edit post' })).toBeVisible() const titleInput = memberPage.locator('#post-title') await titleInput.fill(editedTitle) await memberPage.getByRole('button', { name: 'Save changes' }).click() await expect(memberPage.getByRole('heading', { name: editedTitle })).toBeVisible({ timeout: 10000, }) // --- Delete (in-card two-step confirm; not a native dialog) --- const editedCard = memberPage.locator('article.board-post', { hasText: editedTitle }) await editedCard.getByRole('button', { name: 'Delete' }).click() await editedCard.getByRole('button', { name: 'Confirm' }).click() await expect(memberPage.getByRole('heading', { name: editedTitle })).not.toBeVisible({ timeout: 10000, }) } finally { await context.close() } }) })