diff --git a/tests/e2e/layout-compat-baselines.json b/tests/e2e/layout-compat-baselines.json index 89680221eca..846e01e2447 100644 --- a/tests/e2e/layout-compat-baselines.json +++ b/tests/e2e/layout-compat-baselines.json @@ -1326,6 +1326,638 @@ ] } }, + "old-phone-on-new-server.layout-owner": { + "linux": { + "steps": [ + { + "step": "phone creates two terminals", + "phone": { + "activeTab": "tab2", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab2" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "Terminal", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "server": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "Terminal", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + } + }, + { + "step": "phone activates the first", + "reply": { + "ok": true, + "fields": [ + "activeGroupId", + "activeTabId", + "activeTabType", + "publicationEpoch", + "snapshotVersion", + "tabGroups", + "tabs", + "worktree" + ] + }, + "phone": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "Terminal", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "server": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "Terminal", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + } + }, + { + "step": "phone renames the first", + "reply": { + "ok": true, + "fields": ["rename"] + }, + "phone": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "server": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + } + }, + { + "step": "phone focuses the second", + "reply": { + "ok": true, + "fields": ["focus"] + }, + "phone": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "server": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "tree": "leaf2", + "paneTitles": [] + } + ] + } + }, + { + "step": "host pins the second", + "phone": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "server": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + } + ] + } + }, + { + "step": "phone closes the pinned tab", + "reply": { + "ok": false, + "error": "terminal_tab_pinned" + }, + "phone": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "server": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + } + ] + } + }, + { + "step": "phone opens a file", + "reply": { + "ok": true, + "fields": ["kind", "opened", "relativePath", "worktree"] + }, + "phone": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "server": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + } + ] + } + }, + { + "step": "phone reconnects after a server restart", + "snapshotBefore": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + } + ] + }, + "snapshotAfter": { + "activeTab": "tab1", + "groups": [ + { + "tabs": ["tab1", "tab2", "tab3"], + "active": "tab1" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab1", + "leaf": "leaf1", + "pty": "ptyServe1", + "title": "phone-renamed", + "tree": "leaf1", + "paneTitles": [] + }, + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal 2", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + }, + { + "type": "markdown", + "tab": "tab3", + "title": "README.md" + } + ] + }, + "terminalsAfter": [ + { + "tab": "tab1", + "pty": "ptyServe1" + }, + { + "tab": "tab2", + "pty": "ptyServe2" + } + ] + }, + { + "step": "phone closes the first terminal", + "reply": { + "ok": true, + "fields": ["close"] + }, + "phone": { + "activeTab": "tab2", + "groups": [ + { + "tabs": ["tab2", "tab3"], + "active": "tab2" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal 2", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + }, + { + "type": "markdown", + "tab": "tab3", + "title": "README.md" + } + ] + }, + "server": { + "activeTab": "tab2", + "groups": [ + { + "tabs": ["tab2", "tab3"], + "active": "tab2" + } + ], + "rows": [ + { + "type": "terminal", + "tab": "tab2", + "leaf": "leaf2", + "pty": "ptyServe2", + "title": "Terminal 2", + "pinned": true, + "tree": "leaf2", + "paneTitles": [] + }, + { + "type": "markdown", + "tab": "tab3", + "title": "README.md" + } + ] + } + } + ], + "snapshotFields": [ + "activeGroupId", + "activeTabId", + "activeTabType", + "publicationEpoch", + "snapshotVersion", + "tabGroups", + "tabs", + "type", + "worktree" + ], + "terminalRowFields": [ + "customTitle", + "id", + "incarnationId", + "isActive", + "isPinned", + "leafId", + "parentLayout", + "parentTabId", + "ptyId", + "startupCwd", + "status", + "terminal", + "title", + "type" + ] + } + }, "serve-restart-keeps-@@-terminals": { "linux": { "created": { diff --git a/tests/e2e/layout-compat-old-phone-on-new-server.spec.ts b/tests/e2e/layout-compat-old-phone-on-new-server.spec.ts index cf75752abad..f358cd81c03 100644 --- a/tests/e2e/layout-compat-old-phone-on-new-server.spec.ts +++ b/tests/e2e/layout-compat-old-phone-on-new-server.spec.ts @@ -6,6 +6,8 @@ * after the server restarts. Each step records the phone's and * the server's tab lists; the shapes are compared with main's. */ +import { existsSync } from 'node:fs' +import path from 'node:path' import { expect, test } from './helpers/orca-app' import { launchHeadlessPairedRuntimeHost } from './helpers/headless-paired-runtime-host' import { pairMobileClient, type PairedMobileClient } from './helpers/paired-mobile-client' @@ -43,6 +45,11 @@ const OLD_PHONE_CAPABILITIES: readonly RuntimeCapability[] = [ 'agent.launch.v2' ] +// A layout-owner build's main opens the file itself, so the open succeeds and the tab is listed. +const LAYOUT_OWNER_BUILD = existsSync( + path.join(process.cwd(), 'out', 'main', 'ORCA_LAYOUT_OWNER_MAIN') +) + // Set until the restarted host hears from its paired hosts, or a deadline passes: timing, not shape. const TRANSIENT_FIELDS = new Set(['clientHostedPagesUnreconciled']) @@ -182,7 +189,8 @@ test('old-phone-on-new-server: create, activate, rename, focus, close, open a fi }) await record('phone closes the pinned tab', closedPinned) - // Today a headless server refuses: runtime_error "renderer_unavailable" (no window to open in). + // A headless server refuses with runtime_error "renderer_unavailable" (no window to open in), + // except in a layout-owner build, whose main opens it: the README tab is listed from the restart. const opened = await reply(phone, 'files.open', { worktree, relativePath: 'README.md' }) await record('phone opens a file', opened) @@ -217,7 +225,10 @@ test('old-phone-on-new-server: create, activate, rename, focus, close, open a fi : { ok: false, error: 'no terminal.list row for the tab' } await record('phone closes the first terminal', closedLast) - await expectLayoutCompatBaseline(testInfo, 'old-phone-on-new-server', { + const scenario = LAYOUT_OWNER_BUILD + ? 'old-phone-on-new-server.layout-owner' + : 'old-phone-on-new-server' + await expectLayoutCompatBaseline(testInfo, scenario, { steps, // The payload the old phone parses: its snapshot and terminal-row fields. snapshotFields: fieldsOf(snapshot),