mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 16:03:20 +00:00
test(mobile): pin both stale-failure guards and the in-flight retry precedence
Review found two coverage gaps, each proven by mutation:
- The `requestIsCurrent()` guard on the not-ok/`{error}` page path could be
deleted with all 913 tests still green — only the rejection twin was pinned.
The swapped-out-from-under test now runs over both failure shapes.
- `not.toContain('Tap to retry')` was asserted with no `loadEarlierError` set,
so it could not fail. It now renders both props, pinning that an in-flight
retry outranks the failure it is retrying.
This commit is contained in:
@@ -269,11 +269,19 @@ describe('MobileNativeChatView', () => {
|
||||
expect(header.props.accessibilityLabel).toBe('Couldn’t load earlier messages. Tap to retry')
|
||||
})
|
||||
|
||||
it('keeps the header tappable-but-idle while a page is in flight', async () => {
|
||||
await render({ hasMore: true, loadingEarlier: true, onLoadEarlier: vi.fn() })
|
||||
// An in-flight retry outranks the failure it is retrying, so the header shows
|
||||
// the spinner rather than a stale error the user has already acted on.
|
||||
it('disables the header and hides the failure while a retry is in flight', async () => {
|
||||
await render({
|
||||
hasMore: true,
|
||||
loadingEarlier: true,
|
||||
loadEarlierError: 'Couldn’t load earlier messages',
|
||||
onLoadEarlier: vi.fn()
|
||||
})
|
||||
|
||||
const header = loadEarlierHeader()
|
||||
expect(header.props.disabled).toBe(true)
|
||||
expect(headerText(header)).not.toContain('Couldn’t load earlier messages')
|
||||
expect(headerText(header)).not.toContain('Tap to retry')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,6 +8,9 @@ import {
|
||||
type MobileNativeChatSession
|
||||
} from './use-mobile-native-chat-session'
|
||||
|
||||
/** Hands a pending `sendRequest` promise's settlers to the test body. */
|
||||
type PageSettle = { resolve: (response: unknown) => void; reject: (reason: unknown) => void }
|
||||
|
||||
function message(id: string): NativeChatMessage {
|
||||
return {
|
||||
id,
|
||||
@@ -255,10 +258,19 @@ describe('useMobileNativeChatSession', () => {
|
||||
expect(state?.loadingEarlier).toBe(false)
|
||||
})
|
||||
|
||||
it('drops a failure from a page the session swapped out from under', async () => {
|
||||
let rejectEarlier: (reason: unknown) => void = () => {}
|
||||
// Both stale-failure guards have to hold: a swapped-out page can fail by
|
||||
// rejecting or by resolving not-ok, and each has its own `requestIsCurrent()`.
|
||||
it.each([
|
||||
['a rejected request', (settle: PageSettle) => settle.reject(new Error('disconnected'))],
|
||||
['a not-ok response', (settle: PageSettle) => settle.resolve({ ok: false, error: 'offline' })]
|
||||
])('drops %s from a page the session swapped out from under', async (_label, settleEarlier) => {
|
||||
const settle: PageSettle = { resolve: () => {}, reject: () => {} }
|
||||
const sendRequest = vi.fn(
|
||||
() => new Promise((_resolve, reject) => (rejectEarlier = reject))
|
||||
() =>
|
||||
new Promise((resolve, reject) => {
|
||||
settle.resolve = resolve
|
||||
settle.reject = reject
|
||||
})
|
||||
) as unknown as RpcClient['sendRequest']
|
||||
const subscribe: RpcClient['subscribe'] = vi.fn((_method, _params, onData) => {
|
||||
emit = onData
|
||||
@@ -277,7 +289,7 @@ describe('useMobileNativeChatSession', () => {
|
||||
emit({ type: 'replacement', messages: [message('fresh')], hasMore: true, beforeOffset: 0 })
|
||||
)
|
||||
await act(async () => {
|
||||
rejectEarlier(new Error('disconnected'))
|
||||
settleEarlier(settle)
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user