import type { Page } from '@playwright/test'; import { expect } from '@playwright/test'; /** * Navigate to a chat conversation ready for message input. * * Minimal signature by design — a helper that starts growing options * becomes a second API surface. If a test needs a specific conversation * id, a specific room type, or pre-seeded messages, that test should * fetch via the API + set store state directly rather than extending * this helper. Keep it boring. * * Flow: * 1. /chat must already be loaded and the sidebar rendered. * 2. If at least one conversation row exists: click the first one. * 3. Otherwise: click "New Channel", create a disposable room. * 4. Wait for the message input to become enabled — that's the * observable signal that `currentConversationId` is set in the * chat store (ChatInput.tsx disables on `!currentConversationId`). * * Returns the conversation id chosen (via data-conversation-id attr) * when an existing one is selected, or null when a new one was * created (the id is assigned by the backend after dialog submit). * * Target selectors use testids added in v1.0.7-rc1-day2: * - chat-conversation-item (on ConversationItem button) * - chat-new-channel-cta (on the "New Channel" footer button) * The pre-rc1 tests used `button[type="button"]` which ambiguously * matched the "New Channel" CTA itself, producing the 22-failure * cascade. */ export async function navigateToConversation( page: Page, ): Promise { const existing = page.getByTestId('chat-conversation-item').first(); const existingCount = await page.getByTestId('chat-conversation-item').count(); if (existingCount > 0) { const id = await existing.getAttribute('data-conversation-id'); await existing.click(); await expectInputReady(page); return id; } // No conversation exists — create a disposable one. Room name is // timestamped to avoid collisions across concurrent test workers. const roomName = `e2e-helper-${Date.now()}`; await page.getByTestId('chat-new-channel-cta').click(); const nameInput = page.locator('#room-name'); await expect(nameInput).toBeVisible({ timeout: 5_000 }); await nameInput.fill(roomName); await page.getByRole('button', { name: /^create room$/i }).click(); // Dialog closes, new conversation becomes current. await expect(nameInput).toBeHidden({ timeout: 5_000 }); await expectInputReady(page); return null; } async function expectInputReady(page: Page): Promise { const input = page.getByLabel('Type a message'); await expect(input).toBeVisible({ timeout: 8_000 }); await expect(input).toBeEnabled({ timeout: 8_000 }); }