/** Desktop layout regression: opening the right panel must not make Harness enter narrow mode. */ import { mkdirSync } from 'node:fs' import { homedir } from 'node:os' import { join } from 'node:path' import { expect, test, type Page } from '@playwright/test' import { PAGE_URL } from './host' const WORKSPACE_PATH = process.env.DSH_E2E_DESKTOP_WORKSPACE ?? (process.env.DSH_E2E_WORKSPACE === undefined ? join(homedir(), 'dsh-e2e-desktop-layout-workspace') : `${process.env.DSH_E2E_WORKSPACE}-desktop-layout`) async function dismissOnboarding(page: Page): Promise { await expect .poll(() => page.getByRole('button', { name: /^(Continue|Configure later|继续|稍后配置)$/ }).count(), { timeout: 60_000 }) .toBeGreaterThan(0) for (let round = 0; round < 8; round++) { let dismissed = false for (const name of [/^(Continue|继续)$/, /^(Configure later|稍后配置)$/]) { const button = page.getByRole('button', { name }).first() if ((await button.count()) === 0) continue await button.click() dismissed = true await expect(button).toBeHidden() } if (!dismissed) return } throw new Error('onboarding takeovers did not settle after visible dismissal') } test.beforeAll(() => { mkdirSync(WORKSPACE_PATH, { recursive: true }) }) test('right panel keeps desktop session actions in their header positions', async ({ page }) => { const pageErrors: string[] = [] const consoleErrors: string[] = [] const requestFailures: string[] = [] const badResponses: string[] = [] page.on('pageerror', error => pageErrors.push(error.stack ?? error.message)) page.on('console', message => { if (message.type() === 'error') consoleErrors.push(message.text()) }) page.on('requestfailed', request => requestFailures.push(`${request.method()} ${request.url()} ${request.failure()?.errorText ?? 'unknown'}`)) page.on('response', response => { if (response.status() >= 400) badResponses.push(`${response.status()} ${response.url()}`) }) await page.goto(PAGE_URL, { waitUntil: 'domcontentloaded' }) await dismissOnboarding(page) await page.getByRole('button', { name: /^(Choose workspace|选择工作区)$/ }).click() const picker = page.getByRole('dialog', { name: /^(Select Workspace Directory|选择工作区目录)$/ }) await expect(picker).toBeVisible() await picker.getByRole('button', { name: /^(Edit path|编辑路径)$/ }).click() const pathInput = picker.getByRole('textbox', { name: /^(Edit path|编辑路径)$/ }) await pathInput.fill(WORKSPACE_PATH) await pathInput.press('Enter') await expect(pathInput).toBeHidden() await picker.getByRole('button', { name: /^(Open|打开)$/ }).click() // Target the ACTIVE composer by its accessible name. DSH 0.1.2-alpha hosts // keep an INERT `data-composer-input` "Choose workspace" textbox (with // contenteditable="false") later in DOM order than the real composer — a // bare `getByRole('textbox').last()` resolved to that one and fill() threw // ("not an ,