mirror of
https://github.com/stablyai/orca.git
synced 2026-10-02 16:02:15 +00:00
* refactor(tabs): delete the terminal tab's dead adopted-session field and every branch that read it * refactor(tabs): drop the stale adopted-agent comment and pin legacy load on a chat terminal The comment above the terminal chat-eligibility agent fallback described the removed adopted-session agent fallback. The legacy load test now puts the retired key on a chat-mode terminal tab, the only shape that ever carried it.
720 lines
20 KiB
TypeScript
720 lines
20 KiB
TypeScript
import { describe, it, expect } from 'vitest'
|
|
import { parseWorkspaceSession } from './workspace-session-schema'
|
|
import { MAX_BROWSER_HISTORY_ENTRIES } from './workspace-session-browser-history'
|
|
|
|
describe('parseWorkspaceSession', () => {
|
|
it('accepts a minimal valid session', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {}
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
})
|
|
|
|
it('preserves external SSH file ownership across session parsing', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
openFilesByWorktree: {
|
|
wt: [
|
|
{
|
|
filePath: '/tmp/external.png',
|
|
relativePath: '/tmp/external.png',
|
|
worktreeId: 'wt',
|
|
language: 'png',
|
|
externalSshTargetId: 'ssh-1'
|
|
}
|
|
]
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.openFilesByWorktree?.wt?.[0]?.externalSshTargetId).toBe('ssh-1')
|
|
}
|
|
})
|
|
|
|
it('drops an open file with blank external SSH ownership, keeping the session', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
openFilesByWorktree: {
|
|
wt: [
|
|
{
|
|
filePath: '/tmp/external.png',
|
|
relativePath: '/tmp/external.png',
|
|
worktreeId: 'wt',
|
|
language: 'png',
|
|
externalSshTargetId: ' '
|
|
}
|
|
]
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.openFilesByWorktree?.wt).toEqual([])
|
|
}
|
|
})
|
|
|
|
it('accepts a fully populated session with optional fields', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: 'repo1',
|
|
activeWorktreeId: 'repo1::/path/wt1',
|
|
activeTabId: 'tab1',
|
|
tabsByWorktree: {
|
|
'repo1::/path/wt1': [
|
|
{
|
|
id: 'tab1',
|
|
ptyId: 'daemon-session-abc',
|
|
worktreeId: 'repo1::/path/wt1',
|
|
title: 'bash',
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 1_700_000_000_000
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {
|
|
tab1: {
|
|
root: {
|
|
type: 'split',
|
|
direction: 'vertical',
|
|
first: { type: 'leaf', leafId: 'pane:1' },
|
|
second: { type: 'leaf', leafId: 'pane:2' }
|
|
},
|
|
activeLeafId: 'pane:1',
|
|
expandedLeafId: null,
|
|
ptyIdsByLeafId: { 'pane:1': 'daemon-session-A' }
|
|
}
|
|
},
|
|
activeWorktreeIdsOnShutdown: ['repo1::/path/wt1']
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
})
|
|
|
|
it('preserves an isolated browser tab session partition across hydration', () => {
|
|
// Regression for #6923: the resolved partition must survive persist→load,
|
|
// otherwise a restored isolated tab falls back to the shared default
|
|
// partition when the renderer profile mirror is stale at startup.
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
browserTabsByWorktree: {
|
|
wt: [
|
|
{
|
|
id: 'browser-1',
|
|
worktreeId: 'wt',
|
|
sessionProfileId: 'iso-profile',
|
|
sessionPartition: 'persist:orca-browser-session-iso-profile',
|
|
url: 'https://example.com',
|
|
title: 'Example',
|
|
loading: false,
|
|
faviconUrl: null,
|
|
canGoBack: false,
|
|
canGoForward: false,
|
|
loadError: null,
|
|
createdAt: 1
|
|
}
|
|
]
|
|
}
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
if (!result.ok) {
|
|
return
|
|
}
|
|
expect(result.value.browserTabsByWorktree?.wt?.[0]?.sessionPartition).toBe(
|
|
'persist:orca-browser-session-iso-profile'
|
|
)
|
|
})
|
|
|
|
it('preserves a valid launchAgent on a terminal tab', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
ptyId: null,
|
|
worktreeId: 'wt',
|
|
title: 'codex',
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 1,
|
|
launchAgent: 'codex'
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {}
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.tabsByWorktree.wt[0].launchAgent).toBe('codex')
|
|
}
|
|
})
|
|
|
|
it('drops an unknown launchAgent without failing the whole session', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
ptyId: null,
|
|
worktreeId: 'wt',
|
|
title: 'bash',
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 1,
|
|
launchAgent: 'some-retired-agent'
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {}
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.tabsByWorktree.wt[0].launchAgent).toBeUndefined()
|
|
}
|
|
})
|
|
|
|
it('drops a tab where ptyId is a number (schema drift) without failing the session', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
ptyId: 42,
|
|
worktreeId: 'wt',
|
|
title: 'bash',
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {}
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.tabsByWorktree.wt).toEqual([])
|
|
}
|
|
})
|
|
|
|
it('preserves generated terminal title fields for persistence hydration', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: 'tab1',
|
|
tabsByWorktree: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
ptyId: null,
|
|
worktreeId: 'wt',
|
|
title: 'Claude working',
|
|
defaultTitle: 'Terminal 1',
|
|
generatedTitle: 'Refactor auth',
|
|
aiVaultTitle: {
|
|
agent: 'codex',
|
|
sessionId: 'session-1',
|
|
title: 'Provider thread name'
|
|
},
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {},
|
|
unifiedTabs: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
entityId: 'tab1',
|
|
groupId: 'group1',
|
|
worktreeId: 'wt',
|
|
executionHostId: 'runtime:host-b',
|
|
contentType: 'terminal',
|
|
label: 'Claude working',
|
|
generatedLabel: 'Refactor auth',
|
|
aiVaultTitle: {
|
|
agent: 'codex',
|
|
sessionId: 'session-1',
|
|
title: 'Provider thread name'
|
|
},
|
|
customLabel: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
}
|
|
]
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.tabsByWorktree.wt[0].generatedTitle).toBe('Refactor auth')
|
|
expect(result.value.tabsByWorktree.wt[0].aiVaultTitle?.title).toBe('Provider thread name')
|
|
expect(result.value.unifiedTabs?.wt[0].generatedLabel).toBe('Refactor auth')
|
|
expect(result.value.unifiedTabs?.wt[0].aiVaultTitle?.title).toBe('Provider thread name')
|
|
expect(result.value.unifiedTabs?.wt[0].executionHostId).toBe('runtime:host-b')
|
|
}
|
|
})
|
|
|
|
it('drops malformed AI Vault titles without rejecting the workspace session', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: 'tab1',
|
|
tabsByWorktree: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
ptyId: null,
|
|
worktreeId: 'wt',
|
|
title: 'Codex',
|
|
aiVaultTitle: { agent: 'future-agent', sessionId: 'session-1', title: 'Name' },
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {},
|
|
unifiedTabs: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
entityId: 'tab1',
|
|
groupId: 'group1',
|
|
worktreeId: 'wt',
|
|
contentType: 'terminal',
|
|
label: 'Codex',
|
|
aiVaultTitle: 'malformed',
|
|
customLabel: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
}
|
|
]
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.tabsByWorktree.wt[0].aiVaultTitle).toBeUndefined()
|
|
expect(result.value.unifiedTabs?.wt[0].aiVaultTitle).toBeUndefined()
|
|
}
|
|
})
|
|
|
|
it('preserves quick command label fields while accepting older omitted fields', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: 'tab1',
|
|
tabsByWorktree: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
ptyId: null,
|
|
worktreeId: 'wt',
|
|
title: 'pnpm test',
|
|
defaultTitle: 'Terminal 1',
|
|
quickCommandLabel: 'Run tests',
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
},
|
|
{
|
|
id: 'tab2',
|
|
ptyId: null,
|
|
worktreeId: 'wt',
|
|
title: 'Terminal 2',
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 1,
|
|
createdAt: 1
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {},
|
|
unifiedTabs: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
entityId: 'tab1',
|
|
groupId: 'group1',
|
|
worktreeId: 'wt',
|
|
contentType: 'terminal',
|
|
label: 'pnpm test',
|
|
quickCommandLabel: 'Run tests',
|
|
customLabel: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
}
|
|
]
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.tabsByWorktree.wt[0].quickCommandLabel).toBe('Run tests')
|
|
expect(result.value.tabsByWorktree.wt[1].quickCommandLabel).toBeUndefined()
|
|
expect(result.value.unifiedTabs?.wt[0].quickCommandLabel).toBe('Run tests')
|
|
}
|
|
})
|
|
|
|
it('rejects a session with missing required top-level fields', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null
|
|
// missing activeWorktreeId, tabsByWorktree, etc.
|
|
})
|
|
expect(result.ok).toBe(false)
|
|
})
|
|
|
|
it('rejects a truncated JSON object', () => {
|
|
const result = parseWorkspaceSession({})
|
|
expect(result.ok).toBe(false)
|
|
})
|
|
|
|
it('rejects non-object input (e.g. corrupted file contents)', () => {
|
|
expect(parseWorkspaceSession(null).ok).toBe(false)
|
|
expect(parseWorkspaceSession('garbage').ok).toBe(false)
|
|
expect(parseWorkspaceSession(42).ok).toBe(false)
|
|
})
|
|
|
|
it('drops one truncated tab without discarding other persisted worktrees', () => {
|
|
const validTab = {
|
|
id: 'tab-good',
|
|
ptyId: null,
|
|
worktreeId: 'worktree-good',
|
|
title: 'Terminal',
|
|
customTitle: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 1_700_000_000_000
|
|
}
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'worktree-good',
|
|
activeTabId: 'tab-good',
|
|
tabsByWorktree: {
|
|
'worktree-good': [validTab],
|
|
'worktree-corrupt': [
|
|
{
|
|
id: 'tab-truncated',
|
|
ptyId: null,
|
|
worktreeId: 'worktree-corrupt',
|
|
title: 'Terminal',
|
|
sortOrder: 0,
|
|
generation: 3,
|
|
startupCwd: '/workspace'
|
|
}
|
|
]
|
|
},
|
|
terminalLayoutsByTabId: {}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.tabsByWorktree).toEqual({
|
|
'worktree-good': [validTab],
|
|
'worktree-corrupt': []
|
|
})
|
|
}
|
|
})
|
|
|
|
it('drops bad lastVisitedAtByWorktreeId entries rather than failing the session', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
lastVisitedAtByWorktreeId: {
|
|
good: 1_700_000_000_000,
|
|
nan: Number.NaN,
|
|
infinite: Number.POSITIVE_INFINITY,
|
|
negative: -5,
|
|
string: 'nope'
|
|
}
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.lastVisitedAtByWorktreeId).toEqual({ good: 1_700_000_000_000 })
|
|
}
|
|
})
|
|
|
|
it('accepts default-tab idempotency markers', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
defaultTerminalTabsAppliedByWorktreeId: {
|
|
'repo1::/path/wt1': true
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.defaultTerminalTabsAppliedByWorktreeId).toEqual({
|
|
'repo1::/path/wt1': true
|
|
})
|
|
}
|
|
})
|
|
|
|
it('caps oversized browser history while parsing legacy workspace sessions', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: null,
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
browserUrlHistory: Array.from({ length: 500 }, (_, index) => ({
|
|
url: `https://example.com/${index}`,
|
|
normalizedUrl: `https://example.com/${index}`,
|
|
title: `Example ${index}`,
|
|
faviconUrl: index === 0 ? 'https://example.com/favicon.ico' : null,
|
|
lastVisitedAt: 1_700_000_000_000 - index,
|
|
visitCount: 1
|
|
}))
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.browserUrlHistory).toHaveLength(MAX_BROWSER_HISTORY_ENTRIES)
|
|
expect(result.value.browserUrlHistory?.[0]?.faviconUrl).toBe(
|
|
'https://example.com/favicon.ico'
|
|
)
|
|
expect(result.value.browserUrlHistory?.at(-1)?.url).toBe('https://example.com/199')
|
|
}
|
|
})
|
|
|
|
it('preserves a known viewMode on a unified tab', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
unifiedTabs: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
entityId: 'tab1',
|
|
groupId: 'group1',
|
|
worktreeId: 'wt',
|
|
contentType: 'terminal',
|
|
label: 'Claude',
|
|
customLabel: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0,
|
|
viewMode: 'chat'
|
|
}
|
|
]
|
|
}
|
|
})
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.unifiedTabs?.wt[0].viewMode).toBe('chat')
|
|
}
|
|
})
|
|
|
|
it('preserves a structured agent session tab and drops the retired adoption key', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: 'session-1',
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
unifiedTabs: {
|
|
wt: [
|
|
{
|
|
id: 'session-1',
|
|
entityId: 'session-1',
|
|
groupId: 'group1',
|
|
worktreeId: 'wt',
|
|
contentType: 'agent-session',
|
|
agentSessionAgent: 'codex',
|
|
label: 'Codex Chat',
|
|
customLabel: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0
|
|
},
|
|
{
|
|
id: 'terminal-1',
|
|
entityId: 'terminal-1',
|
|
groupId: 'group1',
|
|
worktreeId: 'wt',
|
|
contentType: 'terminal',
|
|
viewMode: 'chat',
|
|
// Why: older builds could save this key on a chat-mode terminal; it must keep loading.
|
|
structuredSessionId: 'codex-session-1',
|
|
label: 'Terminal 1',
|
|
customLabel: null,
|
|
color: null,
|
|
sortOrder: 1,
|
|
createdAt: 0
|
|
}
|
|
]
|
|
},
|
|
activeTabTypeByWorktree: { wt: 'agent-session' }
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.unifiedTabs?.wt[0]).toMatchObject({
|
|
contentType: 'agent-session',
|
|
agentSessionAgent: 'codex'
|
|
})
|
|
expect(result.value.unifiedTabs?.wt[1]).toMatchObject({
|
|
contentType: 'terminal',
|
|
viewMode: 'chat'
|
|
})
|
|
expect(result.value.unifiedTabs?.wt[1]).not.toHaveProperty('structuredSessionId')
|
|
expect(result.value.activeTabTypeByWorktree?.wt).toBe('agent-session')
|
|
}
|
|
})
|
|
|
|
it('degrades an unknown viewMode to the safe default instead of failing parse', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
unifiedTabs: {
|
|
wt: [
|
|
{
|
|
id: 'tab1',
|
|
entityId: 'tab1',
|
|
groupId: 'group1',
|
|
worktreeId: 'wt',
|
|
contentType: 'terminal',
|
|
label: 'Claude',
|
|
customLabel: null,
|
|
color: null,
|
|
sortOrder: 0,
|
|
createdAt: 0,
|
|
// A newer build could persist a mode this version doesn't know.
|
|
viewMode: 'split-future-mode'
|
|
}
|
|
]
|
|
}
|
|
})
|
|
// The whole-session parse must still succeed; the unknown mode degrades.
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.unifiedTabs?.wt[0].viewMode).toBe('terminal')
|
|
}
|
|
})
|
|
|
|
// Why: z.object strips unlisted keys, so a page row that reaches disk with the remote page
|
|
// identity comes back without it — and hydration can only reconstruct the handle it needs to
|
|
// reclaim a client-hosted page if both halves of that identity survive the round trip.
|
|
it('preserves the remote page identity of a client-hosted browser page', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
browserPagesByWorkspace: {
|
|
'workspace-1': [
|
|
{
|
|
id: 'page-1',
|
|
workspaceId: 'workspace-1',
|
|
worktreeId: 'wt',
|
|
url: 'https://example.com/',
|
|
title: 'Example',
|
|
loading: false,
|
|
faviconUrl: null,
|
|
canGoBack: false,
|
|
canGoForward: false,
|
|
loadError: null,
|
|
createdAt: 1,
|
|
browserRuntimeEnvironmentId: 'env-1',
|
|
remoteBrowserPageId: 'remote-page-1',
|
|
remoteBrowserPageClientHosted: true
|
|
}
|
|
]
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
expect(result.value.browserPagesByWorkspace?.['workspace-1']?.[0]).toMatchObject({
|
|
remoteBrowserPageId: 'remote-page-1',
|
|
remoteBrowserPageClientHosted: true
|
|
})
|
|
}
|
|
})
|
|
|
|
it('accepts a browser page persisted before the remote page identity existed', () => {
|
|
const result = parseWorkspaceSession({
|
|
activeRepoId: null,
|
|
activeWorktreeId: 'wt',
|
|
activeTabId: null,
|
|
tabsByWorktree: {},
|
|
terminalLayoutsByTabId: {},
|
|
browserPagesByWorkspace: {
|
|
'workspace-1': [
|
|
{
|
|
id: 'page-1',
|
|
workspaceId: 'workspace-1',
|
|
worktreeId: 'wt',
|
|
url: 'https://example.com/',
|
|
title: 'Example',
|
|
loading: false,
|
|
faviconUrl: null,
|
|
canGoBack: false,
|
|
canGoForward: false,
|
|
loadError: null,
|
|
createdAt: 1
|
|
}
|
|
]
|
|
}
|
|
})
|
|
|
|
expect(result.ok).toBe(true)
|
|
if (result.ok) {
|
|
const page = result.value.browserPagesByWorkspace?.['workspace-1']?.[0]
|
|
expect(page?.id).toBe('page-1')
|
|
expect(page?.remoteBrowserPageId).toBeUndefined()
|
|
expect(page?.remoteBrowserPageClientHosted).toBeUndefined()
|
|
}
|
|
})
|
|
})
|