import { test, expect } from '@playwright/test'; import { E2E_BACKEND_URL } from '../playwright.config'; test.describe('Claude Working Directory Auto-Update', () => { test('working directory updates automatically after cd command', async ({ page, request }) => { // Increase timeout for this test since it involves real Claude interaction test.setTimeout(180000); // Enable console logging to debug 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', (req) => { if (req.url().includes('/api')) { console.log(`[Request] ${req.method()} ${req.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 Claude Code from the dropdown const claudeOption = page.locator('button:has-text("Claude Code")'); await expect(claudeOption).toBeVisible(); await claudeOption.click(); // 4. Wait for navigation to session page await page.waitForURL(/\/session\/.+/); const sessionUrl = page.url(); const sessionId = sessionUrl.split('/session/')[1]; console.log('[Test] Navigated to session page:', sessionUrl); console.log('[Test] Session ID:', sessionId); // 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. Send a message to Claude asking it to cd into repos (natural language) // Claude should run the cd command and ideally output the current directory const textarea = page.locator('textarea'); await expect(textarea).toBeVisible(); await textarea.fill('change directory to ~/repos and tell me where you are now'); // 8. Click the send button const sendButton = page.locator('button[type="submit"]'); await expect(sendButton).toBeEnabled(); await sendButton.click(); // 9. Wait for streaming to complete 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'); await expect(bouncingDots).toHaveCount(0, { timeout: 60000 }); await expect(pulsingCursor).toHaveCount(0, { timeout: 60000 }); console.log('[Test] Message complete'); // 10. The working directory bar should now show the repos path (automatically updated) // The working dir bar is in a specific container with bg-zinc-900/50 const workingDirBar = page.locator('div.bg-zinc-900\\/50'); await expect(workingDirBar).toBeVisible({ timeout: 10000 }); // The working dir text is in a span.truncate.font-mono inside the bar const workingDirText = workingDirBar.locator('span.truncate.font-mono'); await expect(workingDirText).toBeVisible(); // 11. Wait for the working directory to contain 'repos' (automatic update from tool result) await expect(workingDirText).toContainText('repos', { timeout: 10000 }); const displayedWorkingDir = await workingDirText.textContent(); console.log('[Test] Working directory in UI:', displayedWorkingDir); expect(displayedWorkingDir).toContain('repos'); // 12. Verify the working directory in the database via API const sessionResponse = await request.get(`${E2E_BACKEND_URL}/api/sessions/${sessionId}`); expect(sessionResponse.ok()).toBeTruthy(); const sessionData = await sessionResponse.json(); console.log('[Test] Session data from API:', JSON.stringify(sessionData, null, 2)); // The API returns session data directly (not nested under 'session') const dbWorkingDir = sessionData['working-dir'] || sessionData.workingDir || ''; console.log('[Test] Working directory from DB:', dbWorkingDir); // DB should have the repos path expect(dbWorkingDir).toContain('repos'); // UI and DB should match expect(displayedWorkingDir).toBe(dbWorkingDir); console.log('[Test] Auto-sync test passed - working directory automatically updated to repos path'); }); });