diff --git a/e2e/admin-board-channels.spec.js b/e2e/admin-board-channels.spec.js index 7a2d1e7..91111cf 100644 --- a/e2e/admin-board-channels.spec.js +++ b/e2e/admin-board-channels.spec.js @@ -11,6 +11,7 @@ test.describe('Admin board channels page', () => { test('create, edit, and delete a channel', async ({ adminPage }) => { await adminPage.goto('/admin/board-channels') + await adminPage.waitForLoadState('networkidle') await expect(adminPage.getByRole('heading', { name: 'Board Channels' })).toBeVisible({ timeout: 15000, }) @@ -18,14 +19,14 @@ test.describe('Admin board channels page', () => { const suffix = Date.now().toString().slice(-6) const channelName = `e2e-channel-${suffix}` const editedName = `e2e-channel-${suffix}-edited` - const slackId = `C${suffix}XYZ` // --- Create --- + // Create flow only takes a name; the Slack channel ID is auto-assigned on + // creation and only becomes editable in the Edit modal. await adminPage.getByRole('button', { name: '+ New Channel' }).click() await expect(adminPage.getByRole('heading', { name: 'New Channel' })).toBeVisible() - await adminPage.locator('input[placeholder="e.g., #coop-formation"]').fill(channelName) - await adminPage.locator('input[placeholder="C0123456789"]').fill(slackId) + await adminPage.locator('input[placeholder="e.g., coop-formation"]').fill(channelName) // Select the first available cooperative tag if any are present const firstTagCheckbox = adminPage.locator('.tag-select input[type="checkbox"]').first() @@ -44,7 +45,7 @@ test.describe('Admin board channels page', () => { await row.getByRole('button', { name: 'Edit' }).click() await expect(adminPage.getByRole('heading', { name: 'Edit Channel' })).toBeVisible() - const nameInput = adminPage.locator('input[placeholder="e.g., #coop-formation"]') + const nameInput = adminPage.locator('input[placeholder="e.g., coop-formation"]') await nameInput.fill(editedName) await adminPage.getByRole('button', { name: 'Save Changes' }).click() diff --git a/e2e/board.spec.js b/e2e/board.spec.js index 6f2fe7e..945cd4d 100644 --- a/e2e/board.spec.js +++ b/e2e/board.spec.js @@ -9,6 +9,7 @@ test.describe('Board page', () => { test('clicking New Post reveals the form', async ({ memberPage }) => { await memberPage.goto('/board') + await memberPage.waitForLoadState('networkidle') await expect(memberPage.getByRole('button', { name: '+ New Post' }).first()).toBeVisible({ timeout: 15000, }) @@ -40,6 +41,7 @@ test.describe('Board page', () => { test('create, edit, and delete own post', async ({ memberPage }) => { await memberPage.goto('/board') + await memberPage.waitForLoadState('networkidle') await expect(memberPage.getByRole('button', { name: '+ New Post' }).first()).toBeVisible({ timeout: 15000, }) @@ -55,7 +57,7 @@ test.describe('Board page', () => { await memberPage.locator('#post-title').fill(originalTitle) await memberPage.locator('#post-seeking').fill('Playwright test seeking text') - await memberPage.getByRole('button', { name: 'Post' }).click() + await memberPage.getByRole('button', { name: 'Post', exact: true }).click() await expect(memberPage.getByRole('heading', { name: originalTitle })).toBeVisible({ timeout: 10000, @@ -75,10 +77,10 @@ test.describe('Board page', () => { timeout: 10000, }) - // --- Delete (confirm dialog) --- - memberPage.once('dialog', (dialog) => dialog.accept()) + // --- 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, diff --git a/e2e/join-flow.spec.js b/e2e/join-flow.spec.js index 919e9ec..0062b95 100644 --- a/e2e/join-flow.spec.js +++ b/e2e/join-flow.spec.js @@ -68,8 +68,12 @@ test.describe('Join page — member signup flow', () => { await page.locator('#join-name').fill('Test User') await expect(page.locator('.form-submit')).toBeDisabled() - // Fill email too — now all fields are populated and button should be enabled + // Fill email — agreement still unchecked, so still disabled await page.locator('#join-email').fill('incomplete-test@example.com') + await expect(page.locator('.form-submit')).toBeDisabled() + + // Check the Community Guidelines agreement — now all required fields satisfied + await page.getByRole('checkbox', { name: /Community Guidelines/ }).check() await expect(page.locator('.form-submit')).toBeEnabled() }) @@ -83,8 +87,9 @@ test.describe('Join page — member signup flow', () => { await page.locator('#join-name').fill('E2E Test User') await page.locator('#join-email').fill(uniqueEmail) await page.locator('#circle-community').check({ force: true }) - await page.locator('#join-contribution').click() - await page.getByRole('option', { name: '$0/mo' }).click() + // Contribution is now a numeric input with preset chips, not a select + await page.locator('#join-contribution').fill('0') + await page.getByRole('checkbox', { name: /Community Guidelines/ }).check() await expect(page.locator('.form-submit')).toBeEnabled() @@ -93,8 +98,10 @@ test.describe('Join page — member signup flow', () => { await page.locator('.form-submit').click() - // Free tier creates subscription then shows confirmation (step 3) - await expect(page.locator('.success-box')).toBeVisible({ timeout: 15000 }) + // Free tier flips the SignupFlowOverlay into its success state + await expect( + page.getByRole('heading', { name: 'Welcome to Ghost Guild!' }) + ).toBeVisible({ timeout: 15000 }) }) test('duplicate email shows error', async ({ page }) => { @@ -109,12 +116,13 @@ test.describe('Join page — member signup flow', () => { await page.locator('#join-name').fill('Dup Test User') await page.locator('#join-email').fill(duplicateEmail) await page.locator('#circle-community').check({ force: true }) - await page.locator('#join-contribution').click() - await page.getByRole('option', { name: '$0/mo' }).click() + await page.locator('#join-contribution').fill('0') + await page.getByRole('checkbox', { name: /Community Guidelines/ }).check() await page.locator('.form-submit').click() - // Should show an error about the email already existing - await expect(page.locator('.error-box')).toBeVisible({ timeout: 10000 }) - await expect(page.locator('.error-box')).toContainText(/already/i) + // Helcim 409 puts SignupFlowOverlay into its error state + const overlayError = page.locator('.signup-flow-overlay .error-box') + await expect(overlayError).toBeVisible({ timeout: 10000 }) + await expect(overlayError).toContainText(/already/i) }) }) diff --git a/e2e/member-profile.spec.js b/e2e/member-profile.spec.js index 0fa51c8..22af212 100644 --- a/e2e/member-profile.spec.js +++ b/e2e/member-profile.spec.js @@ -3,9 +3,11 @@ import { test, expect } from './helpers/fixtures.js' test.describe('Member profile page', () => { test('profile page loads', async ({ adminPage }) => { await adminPage.goto('/member/profile') + await adminPage.waitForLoadState('networkidle') // Auth is checked client-side in onMounted — wait for profile form to render await expect(adminPage.getByText('Edit Profile')).toBeVisible({ timeout: 15000 }) - await expect(adminPage.getByText('How you appear to other members')).toBeVisible() + // Verify a stable structural section label, not transient marketing copy + await expect(adminPage.getByText('Show in Member Directory')).toBeVisible() }) test('form fields are present', async ({ adminPage }) => { @@ -24,6 +26,7 @@ test.describe('Member profile page', () => { test('bio field accepts input', async ({ adminPage }) => { await adminPage.goto('/member/profile') + await adminPage.waitForLoadState('networkidle') await expect(adminPage.getByText('Edit Profile')).toBeVisible({ timeout: 15000 }) const bio = adminPage.locator('textarea[placeholder*="Share your background"]')