Add scenario tests from design doc verification matrix

Tests matching the verification matrix in mobile-terminal-phone-mode-checklist.md:
- Desktop window resize while mobile viewing: override persists
- Mobile disconnect restores all terminals: overrides fully cleared
- PTY exits while phone-fitted: no stale state remains
- Mobile reconnects after disconnect: new override works
- Multiple tabs with same pane IDs: cross-tab isolation
- Change listener lifecycle: fires for both override and restore

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo-H
2026-04-30 18:03:57 -04:00
co-authored by Orca
parent d107e7ed1c
commit 83db85eb25
@@ -301,3 +301,109 @@ describe('getAllOverrides', () => {
expect(getFitOverrideForPty('pty-1')).not.toBeNull()
})
})
// ---------------------------------------------------------------------------
// Scenario tests (from design doc verification matrix)
// ---------------------------------------------------------------------------
describe('scenario: desktop window resize while mobile is viewing', () => {
it('override persists across setFitOverride calls — desktop safeFit will see it', () => {
setFitOverride('pty-1', 'mobile-fit', 49, 20)
bindPanePtyId(1, 'pty-1', 'tab-0')
// Simulate desktop resize triggering a re-check — override should still be there
expect(getFitOverrideForPty('pty-1')).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 })
expect(getFitOverrideForPane(1, 'tab-0')).toEqual({ mode: 'mobile-fit', cols: 49, rows: 20 })
})
})
describe('scenario: mobile disconnect restores all terminals', () => {
it('clearing all overrides for a disconnected client removes all traces', () => {
setFitOverride('pty-1', 'mobile-fit', 49, 20)
setFitOverride('pty-2', 'mobile-fit', 49, 20)
bindPanePtyId(1, 'pty-1', 'tab-0')
bindPanePtyId(2, 'pty-2', 'tab-0')
// Simulate runtime clearing overrides on disconnect
setFitOverride('pty-1', 'desktop-fit', 120, 40)
setFitOverride('pty-2', 'desktop-fit', 120, 40)
expect(getFitOverrideForPty('pty-1')).toBeNull()
expect(getFitOverrideForPty('pty-2')).toBeNull()
expect(getFitOverrideForPane(1, 'tab-0')).toBeNull()
expect(getFitOverrideForPane(2, 'tab-0')).toBeNull()
})
})
describe('scenario: PTY exits while phone-fitted', () => {
it('clearing override for exited PTY leaves no stale state', () => {
setFitOverride('pty-1', 'mobile-fit', 49, 20)
bindPanePtyId(1, 'pty-1', 'tab-0')
// Runtime clears override on PTY exit
setFitOverride('pty-1', 'desktop-fit', 0, 0)
unbindPane(1, 'tab-0')
expect(getFitOverrideForPty('pty-1')).toBeNull()
expect(getFitOverrideForPane(1, 'tab-0')).toBeNull()
expect(getPaneIdsForPty('pty-1')).toEqual([])
expect(getAllOverrides().size).toBe(0)
})
})
describe('scenario: mobile reconnects after disconnect', () => {
it('new override after clear works correctly', () => {
// First session
setFitOverride('pty-1', 'mobile-fit', 49, 20)
bindPanePtyId(1, 'pty-1', 'tab-0')
// Disconnect
setFitOverride('pty-1', 'desktop-fit', 120, 40)
// Reconnect — new session sets override again
setFitOverride('pty-1', 'mobile-fit', 55, 22)
expect(getFitOverrideForPty('pty-1')).toEqual({ mode: 'mobile-fit', cols: 55, rows: 22 })
expect(getFitOverrideForPane(1, 'tab-0')).toEqual({ mode: 'mobile-fit', cols: 55, rows: 22 })
})
})
describe('scenario: multiple tabs with same pane IDs', () => {
it('override on one tab does not affect the other', () => {
bindPanePtyId(1, 'pty-A', 'tab-0')
bindPanePtyId(1, 'pty-B', 'tab-1')
setFitOverride('pty-A', 'mobile-fit', 49, 20)
expect(getFitOverrideForPane(1, 'tab-0')?.cols).toBe(49)
expect(getFitOverrideForPane(1, 'tab-1')).toBeNull()
})
it('clearing override for one tab does not affect the other', () => {
bindPanePtyId(1, 'pty-A', 'tab-0')
bindPanePtyId(1, 'pty-B', 'tab-1')
setFitOverride('pty-A', 'mobile-fit', 49, 20)
setFitOverride('pty-B', 'mobile-fit', 60, 25)
// Clear only tab-0's PTY
setFitOverride('pty-A', 'desktop-fit', 120, 40)
expect(getFitOverrideForPane(1, 'tab-0')).toBeNull()
expect(getFitOverrideForPane(1, 'tab-1')?.cols).toBe(60)
})
})
describe('scenario: change listener fires for both override and restore', () => {
it('tracks full lifecycle: mobile-fit → desktop-fit', () => {
const events: { mode: string }[] = []
const unsub = onOverrideChange((e) => events.push({ mode: e.mode }))
setFitOverride('pty-1', 'mobile-fit', 49, 20)
setFitOverride('pty-1', 'desktop-fit', 120, 40)
expect(events).toEqual([{ mode: 'mobile-fit' }, { mode: 'desktop-fit' }])
unsub()
})
})