Files
orca/src/shared/workspace-session-schema.test.ts
T
Jinjing 8dc3c1dd97 Display favicons for browser website entries (#18099)
* Display favicons for browser website entries

Capture favicons from pages as they load and persist them with browser
history entries. Display favicons in tabs, tab creation search results,
and palette searches to improve visual recognition of websites and help
users identify pages at a glance.

* Fix favicon retry on back navigation after load failure

Reset the favicon failure cache when the favicon URL changes, enabling
retry of a previously failed favicon when navigating back to the same
URL. Distinguish between explicit null (clear cached favicon) and
omitted (don't update history), so stale favicons don't persist
incorrectly.
2026-09-02 10:21:28 -07:00

702 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 its active projection', () => {
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',
structuredSessionId: 'codex-session-1',
label: 'Codex Chat',
customLabel: null,
color: null,
sortOrder: 0,
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',
structuredSessionId: 'codex-session-1'
})
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()
}
})
})