105 lines
4.6 KiB
TypeScript
105 lines
4.6 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
test.describe('OpenCode Chat Workflow', () => {
|
|
// Skip: OpenCode (Go binary) has stdout buffering issues when run as subprocess from Java
|
|
// Go binaries ignore stdbuf and require a pseudo-terminal for proper streaming
|
|
test.skip('create new chat and send message to OpenCode', async ({ page }) => {
|
|
// Enable console logging to debug WebSocket issues
|
|
page.on('console', (msg) => {
|
|
console.log(`[Browser ${msg.type()}]`, msg.text());
|
|
});
|
|
|
|
// Log WebSocket frames
|
|
page.on('websocket', (ws) => {
|
|
console.log(`[WebSocket] Connected to ${ws.url()}`);
|
|
ws.on('framesent', (frame) => console.log(`[WS Sent]`, frame.payload));
|
|
ws.on('framereceived', (frame) => console.log(`[WS Received]`, frame.payload));
|
|
ws.on('close', () => console.log('[WebSocket] Closed'));
|
|
});
|
|
|
|
// Log network requests to /api
|
|
page.on('request', (request) => {
|
|
if (request.url().includes('/api')) {
|
|
console.log(`[Request] ${request.method()} ${request.url()}`);
|
|
}
|
|
});
|
|
page.on('response', (response) => {
|
|
if (response.url().includes('/api')) {
|
|
console.log(`[Response] ${response.status()} ${response.url()}`);
|
|
}
|
|
});
|
|
|
|
// 1. Navigate to homepage
|
|
await page.goto('/');
|
|
await expect(page).toHaveTitle(/Spiceflow/i);
|
|
|
|
// 2. Click the + button to open new session menu
|
|
const createButton = page.locator('button[title="New Session"]');
|
|
await expect(createButton).toBeVisible();
|
|
await createButton.click();
|
|
|
|
// 3. Select OpenCode from the dropdown
|
|
const opencodeOption = page.locator('button:has-text("OpenCode")');
|
|
await expect(opencodeOption).toBeVisible();
|
|
await opencodeOption.click();
|
|
|
|
// 4. Wait for navigation to session page
|
|
await page.waitForURL(/\/session\/.+/);
|
|
console.log('[Test] Navigated to session page:', page.url());
|
|
|
|
// 5. Wait for the page to load (no loading spinner)
|
|
await expect(page.locator('text=Loading')).not.toBeVisible({ timeout: 5000 });
|
|
|
|
// 6. Verify we see the empty message state
|
|
await expect(page.locator('text=No messages yet')).toBeVisible();
|
|
|
|
// 7. Type a message in the textarea
|
|
const textarea = page.locator('textarea');
|
|
await expect(textarea).toBeVisible();
|
|
await textarea.fill('say hi. respond in a single concise sentence');
|
|
|
|
// 8. Click the send button
|
|
const sendButton = page.locator('button[type="submit"]');
|
|
await expect(sendButton).toBeEnabled();
|
|
await sendButton.click();
|
|
|
|
// 9. Verify user message appears immediately (optimistic update)
|
|
await expect(page.locator('text=say hi. respond in a single concise sentence')).toBeVisible();
|
|
console.log('[Test] User message displayed');
|
|
|
|
// 10. Verify thinking indicator appears immediately after sending
|
|
// The assistant bubble with bouncing dots should show right away (isThinking state)
|
|
const assistantMessage = page.locator('.rounded-lg.border').filter({
|
|
has: page.locator('text=Assistant')
|
|
}).last();
|
|
|
|
// Thinking indicator should appear almost immediately (within 2 seconds)
|
|
await expect(assistantMessage).toBeVisible({ timeout: 2000 });
|
|
console.log('[Test] Thinking indicator appeared immediately');
|
|
|
|
// Verify bouncing dots are present (thinking state)
|
|
const bouncingDotsInAssistant = assistantMessage.locator('.animate-bounce');
|
|
await expect(bouncingDotsInAssistant.first()).toBeVisible({ timeout: 2000 });
|
|
console.log('[Test] Bouncing dots visible in thinking state');
|
|
|
|
// 11. Wait for streaming to complete - progress indicator should disappear
|
|
// The streaming indicator has animate-bounce dots and animate-pulse cursor
|
|
// Note: With fast responses, the indicator may appear and disappear quickly,
|
|
// so we just verify it's gone after the response is visible
|
|
const bouncingDots = page.locator('.animate-bounce');
|
|
// Only look for pulsing cursor inside markdown-content (not the header status indicator)
|
|
const pulsingCursor = page.locator('.markdown-content .animate-pulse');
|
|
|
|
// Wait for streaming indicators to disappear (they should be gone after message-stop)
|
|
await expect(bouncingDots).toHaveCount(0, { timeout: 30000 });
|
|
await expect(pulsingCursor).toHaveCount(0, { timeout: 30000 });
|
|
console.log('[Test] Streaming complete - progress indicator disappeared');
|
|
|
|
// 12. Verify the response contains some text content
|
|
const responseText = await assistantMessage.locator('.font-mono').textContent();
|
|
console.log('[Test] Assistant response text:', responseText);
|
|
expect(responseText).toBeTruthy();
|
|
expect(responseText!.length).toBeGreaterThan(0);
|
|
});
|
|
});
|