mirror of
https://github.com/hanzoai/mobile.git
synced 2026-10-30 14:53:17 +00:00
68 lines
2.7 KiB
TypeScript
68 lines
2.7 KiB
TypeScript
// lib/widget captures its native binding at import, so each case installs a
|
|
// fake into the expo-modules-core shim FIRST and then requires a fresh graph.
|
|
type Fake = { set: jest.Mock }
|
|
|
|
function fresh(fake?: Fake): {
|
|
widget: typeof import('@/lib/widget')
|
|
runs: typeof import('@/lib/runs')
|
|
} {
|
|
jest.resetModules()
|
|
const shim = require('./stubs/modules') as typeof import('./stubs/modules')
|
|
if (fake) shim.install('Widget', fake)
|
|
return {
|
|
widget: require('@/lib/widget') as typeof import('@/lib/widget'),
|
|
runs: require('@/lib/runs') as typeof import('@/lib/runs'),
|
|
}
|
|
}
|
|
|
|
function state(fake: Fake, call: number): { snippet: string; updatedAt: number } {
|
|
return JSON.parse(fake.set.mock.calls[call]![0] as string)
|
|
}
|
|
|
|
test('setState hands the platform one JSON string {snippet, updatedAt}', async () => {
|
|
const fake: Fake = { set: jest.fn() }
|
|
const { widget } = fresh(fake)
|
|
await widget.setState({ snippet: 'hello', updatedAt: 1234 })
|
|
expect(fake.set).toHaveBeenCalledTimes(1)
|
|
expect(state(fake, 0)).toEqual({ snippet: 'hello', updatedAt: 1234 })
|
|
})
|
|
|
|
test('setLastMessage trims, collapses whitespace and caps at 200', () => {
|
|
const fake: Fake = { set: jest.fn() }
|
|
const { widget } = fresh(fake)
|
|
widget.setLastMessage(' a markdown\n\nanswer with breaks ')
|
|
expect(state(fake, 0).snippet).toBe('a markdown answer with breaks')
|
|
widget.setLastMessage(`${'x'.repeat(300)}`)
|
|
expect(state(fake, 1).snippet).toHaveLength(200)
|
|
widget.setLastMessage(' \n ')
|
|
expect(fake.set).toHaveBeenCalledTimes(2)
|
|
})
|
|
|
|
test('an absent native module makes every call a silent no-op', async () => {
|
|
const { widget } = fresh()
|
|
await expect(widget.setState({ snippet: 'ghost', updatedAt: 1 })).resolves.toBeUndefined()
|
|
expect(() => widget.setLastMessage('ghost')).not.toThrow()
|
|
})
|
|
|
|
test('follow() pushes only changed run lines and leaves the last message on end', () => {
|
|
const fake: Fake = { set: jest.fn() }
|
|
const { widget, runs } = fresh(fake)
|
|
const off = widget.follow()
|
|
const run = runs.start({ kind: 'chat', title: 'zen5', phase: 'streaming' })
|
|
expect(fake.set).toHaveBeenCalledTimes(1)
|
|
expect(state(fake, 0).snippet).toBe('zen5 — streaming')
|
|
// A detail-only patch keeps the same line; WidgetKit reload budgets are
|
|
// real, so no write happens.
|
|
runs.update(run.id, { detail: '42 tok/s' })
|
|
expect(fake.set).toHaveBeenCalledTimes(1)
|
|
runs.update(run.id, { phase: 'thinking' })
|
|
expect(fake.set).toHaveBeenCalledTimes(2)
|
|
expect(state(fake, 1).snippet).toBe('zen5 — thinking')
|
|
// Ending the run writes nothing: the last message stays on the widget.
|
|
runs.end(run.id)
|
|
expect(fake.set).toHaveBeenCalledTimes(2)
|
|
off()
|
|
runs.start({ kind: 'image', title: 'later', phase: 'generating' })
|
|
expect(fake.set).toHaveBeenCalledTimes(2)
|
|
})
|