diff --git a/e2e/tests/tmux-terminal.spec.ts b/e2e/tests/tmux-terminal.spec.ts index 1211b6e..cd54bed 100644 --- a/e2e/tests/tmux-terminal.spec.ts +++ b/e2e/tests/tmux-terminal.spec.ts @@ -1,4 +1,4 @@ -import { test, expect } from '@playwright/test'; +import { test, expect, type BrowserContext } from '@playwright/test'; import { E2E_BACKEND_URL } from '../playwright.config'; // Helper to delete a specific session by ID via API @@ -182,6 +182,92 @@ test.describe('Tmux Terminal Session', () => { console.log('[Test] Tmux session properly cleaned up'); }); + test('copy selection from terminal works', async ({ page, request, context }) => { + // Grant clipboard permissions + await context.grantPermissions(['clipboard-read', 'clipboard-write']); + + // Track session ID for cleanup + let createdSessionId: string | null = null; + + // 1. Navigate to homepage and create a tmux session + await page.goto('/'); + const createButton = page.locator('button[title="New Session"]'); + await createButton.click(); + + const tmuxOption = page.locator('button:has-text("Terminal (tmux)")'); + await tmuxOption.click(); + + // Wait for navigation to session page + await page.waitForURL(/\/session\/.+/); + const sessionUrl = page.url(); + createdSessionId = decodeURIComponent(sessionUrl.split('/session/')[1]); + console.log('[Test] Created session:', createdSessionId); + + // 2. Wait for terminal to load + const terminalOutput = page.locator('pre.text-green-400'); + await expect(terminalOutput).toBeVisible({ timeout: 10000 }); + + // 3. Run a command with unique output + const uniqueMarker = `COPY-TEST-${Date.now()}`; + const commandInput = page.locator('input[aria-label="Terminal input"]'); + await expect(commandInput).toBeEnabled({ timeout: 5000 }); + await commandInput.focus(); + await page.keyboard.type(`echo "${uniqueMarker}"`); + await page.keyboard.press('Enter'); + console.log('[Test] Sent echo command with marker:', uniqueMarker); + + // Wait for output + await expect(async () => { + const content = await terminalOutput.textContent(); + expect(content).toContain(uniqueMarker); + }).toPass({ timeout: 5000 }); + console.log('[Test] Marker appeared in terminal'); + + // 4. Clear the clipboard first + await page.evaluate(() => navigator.clipboard.writeText('')); + + // 5. Select the marker text by triple-clicking on the line containing it + // First, find the position of the marker in the terminal + const terminalBox = await terminalOutput.boundingBox(); + expect(terminalBox).not.toBeNull(); + + // Get the terminal content to find the marker position + const content = await terminalOutput.textContent(); + const lines = content?.split('\n') || []; + const markerLineIndex = lines.findIndex(line => line.includes(uniqueMarker) && !line.includes('echo')); + console.log('[Test] Marker found on line index:', markerLineIndex); + + if (markerLineIndex >= 0 && terminalBox) { + // Calculate approximate Y position of the marker line + // Assuming ~20px per line (adjust based on actual font size) + const lineHeight = 20; + const yOffset = markerLineIndex * lineHeight + lineHeight / 2 + 12; // +12 for padding + + // Triple-click to select the entire line + await page.mouse.click(terminalBox.x + 50, terminalBox.y + yOffset, { clickCount: 3 }); + console.log('[Test] Triple-clicked to select line at y offset:', yOffset); + + // Give a moment for the selection to register and copy to happen + await page.waitForTimeout(200); + + // 6. Verify clipboard contains the marker + const clipboardContent = await page.evaluate(() => navigator.clipboard.readText()); + console.log('[Test] Clipboard content:', clipboardContent); + + // The clipboard should contain the marker (the entire line might be selected) + expect(clipboardContent).toContain(uniqueMarker); + console.log('[Test] Copy selection test passed - clipboard contains marker'); + } else { + throw new Error(`Could not find marker line in terminal output`); + } + + // 7. Cleanup + if (createdSessionId) { + await deleteSession(request, createdSessionId); + } + console.log('[Test] Cleanup complete'); + }); + test('eject tmux session removes from spiceflow but keeps tmux running', async ({ page, request }) => { // Track session ID for cleanup let createdSessionId: string | null = null;