import { expect, test } from '@playwright/test'; import { SIGNATURE_COPY } from '../src/app/core/content/signature-copy'; import { expectNoOverflow } from './helpers'; const VIEWPORTS = [320, 390, 768, 1024, 1440] as const; test.describe('terminal dock', () => { test('defaults open, keeps one session scrollport and runs the grammar', async ({ page, }, testInfo) => { const copy = SIGNATURE_COPY.de.terminal; await page.goto('/'); const trigger = page.locator('.terminal-dock-trigger'); const panel = page.locator('.terminal-dock-panel'); const input = page.locator('#terminal-dock-input'); const session = page.locator('.terminal-dock-session'); const maximize = page.locator('.terminal-dock-control[aria-pressed]'); const collapse = page.getByRole('button', { name: copy.collapseLabel }); await expect(panel).toBeVisible(); await expect(trigger).toBeHidden(); await expect(input).not.toBeFocused(); await expect(session).toBeVisible(); await expect(session.locator('form')).toHaveCount(1); await expect(collapse).toHaveAttribute('title', copy.collapseTooltip); await expect(maximize).toHaveAttribute('aria-label', copy.maximizeLabel); await expect(page.locator('.terminal-dock-prompt')).toContainText(copy.prompt); if (testInfo.project.name === 'mobile') { const box = await panel.boundingBox(); expect(box, 'bottom-sheet panel').toBeTruthy(); expect(box!.width).toBeGreaterThan(300); } else { await maximize.click(); await expect(maximize).toHaveAttribute('aria-pressed', 'true'); await expect(maximize).toHaveAttribute('aria-label', copy.restoreLabel); await maximize.click(); await expect(maximize).toHaveAttribute('aria-pressed', 'false'); } await input.fill('navigate pitch'); await input.press('Enter'); await page.waitForURL('**/pitch'); await expect(page).toHaveURL(/\/pitch$/); await expect(session).toContainText(`${copy.prompt} navigate pitch`); await input.press('ArrowUp'); await expect(input).toHaveValue('navigate pitch'); await input.fill('history'); await input.press('Enter'); await expect(session).toContainText(copy.historyIntro); await expect(session).toContainText('navigate pitch'); await input.fill('clear'); await input.press('Enter'); await expect(session.locator('p')).toHaveText(copy.clearedMessage); await input.fill('navigate p'); await input.press('Tab'); await expect(session).toContainText('pitch'); await expect(session).toContainText('projects'); await input.fill('navigate nowhere'); await input.press('Enter'); await expect(session).toContainText(copy.validTargetsLabel); await expect(session).toContainText('pitch'); await input.fill('nav'); await input.press('Tab'); await expect(input).toHaveValue('navigate'); await input.fill('navigate services sof'); await input.press('Tab'); await expect(input).toHaveValue('navigate services software'); }); test('collapse control hides the panel and returns focus to the trigger', async ({ page }) => { const copy = SIGNATURE_COPY.de.terminal; await page.goto('/'); const trigger = page.locator('.terminal-dock-trigger'); const panel = page.locator('.terminal-dock-panel'); await expect(panel).toBeVisible(); await expect(trigger).toBeHidden(); const collapse = page.getByRole('button', { name: copy.collapseLabel }); await collapse.click(); await expect(panel).toHaveCount(0); await expect(trigger).toBeVisible(); await expect(trigger).toBeFocused(); await expect(trigger).toHaveAttribute('aria-expanded', 'false'); }); test('keeps the prompt in the session scrollport after overflow', async ({ page }) => { const copy = SIGNATURE_COPY.de.terminal; await page.goto('/'); const input = page.locator('#terminal-dock-input'); const session = page.locator('.terminal-dock-session'); await expect(session).toBeVisible(); for (let index = 0; index < 8; index += 1) { await input.fill('help'); await input.press('Enter'); } await expect(session.locator('p.echo').last()).toHaveText(`${copy.prompt} help`); await expect .poll(async () => session.evaluate((element) => { return element.scrollTop + element.clientHeight >= element.scrollHeight - 2; }), ) .toBe(true); const form = session.locator('form'); const sessionBox = await session.boundingBox(); const formBox = await form.boundingBox(); expect(sessionBox && formBox).toBeTruthy(); expect(formBox!.y).toBeGreaterThanOrEqual(sessionBox!.y - 1); expect(formBox!.y + formBox!.height).toBeLessThanOrEqual( sessionBox!.y + sessionBox!.height + 1, ); }); test('Escape from a panel control collapses the dock and returns focus to the trigger', async ({ page, }) => { const copy = SIGNATURE_COPY.de.terminal; await page.goto('/'); const trigger = page.locator('.terminal-dock-trigger'); const panel = page.locator('.terminal-dock-panel'); await expect(panel).toBeVisible(); const collapse = page.getByRole('button', { name: copy.collapseLabel }); await collapse.focus(); await expect(collapse).toBeFocused(); await page.keyboard.press('Escape'); await expect(panel).toHaveCount(0); await expect(trigger).toBeFocused(); }); test('stays usable and overflow-free at the required viewports', async ({ page }) => { await page.goto('/'); for (const width of VIEWPORTS) { await page.setViewportSize({ width, height: 900 }); await expect(page.locator('.terminal-dock-panel')).toBeVisible(); await expectNoOverflow(page, '/', width); } }); });