diff --git a/mobile/src/session/MobileNativeChatView.test.ts b/mobile/src/session/MobileNativeChatView.test.ts index 2c235337bb8a..fe01a4014e1e 100644 --- a/mobile/src/session/MobileNativeChatView.test.ts +++ b/mobile/src/session/MobileNativeChatView.test.ts @@ -157,68 +157,7 @@ describe('MobileNativeChatView', () => { }) } - it("holds Stop and says Stopping while this phone's own Stop request is in flight", async () => { - await render({ - structuredActivityUi: true, - agentWorking: true, - canStop: true, - turnIndicator: { - thinking: false, - activityText: null, - stopping: true, - stopRequestInFlight: true - } - }) - const stop = renderer!.root.find( - (node) => node.type === 'Pressable' && node.props.accessibilityLabel === 'Stopping…' - ) - expect(stop.props.disabled).toBe(true) - }) - - it.each([ - ['queue', 'Queue a message to run after the stop'], - ['send', 'Send a message to run after the stop'] - ] as const)( - 'tells the composer a message sent now runs after the stop (%s)', - async (afterStop, placeholder) => { - await render({ - structuredActivityUi: true, - agentWorking: true, - canStop: true, - turnIndicator: { thinking: false, activityText: null, stopping: true, afterStop } - }) - const composer = renderer!.root.find((node) => node.type === 'Composer') - expect(composer.props.placeholder).toBe(placeholder) - } - ) - - // A Stop the provider took and never answered ends only at a repeat Stop. - it('keeps Stop for the repeat that escalates while the host alone says Stopping', async () => { - await render({ - structuredActivityUi: true, - agentWorking: true, - canStop: true, - turnIndicator: { thinking: false, activityText: null, stopping: true } - }) - const stop = renderer!.root.find( - (node) => node.type === 'Pressable' && node.props.accessibilityLabel === 'Stop the agent' - ) - expect(stop.props.disabled).toBe(false) - }) - /** Ids of the rows the list is currently rendering. */ - it('keeps Stop hidden during a structured dispatch until a provider turn can be cancelled', async () => { - const props = { structuredActivityUi: true, agentWorking: true, canStop: false } - await render(props) - const stops = () => - renderer!.root.findAll((node) => node.props.accessibilityLabel === 'Stop the agent') - expect(stops()).toHaveLength(0) - await update({ ...props, canStop: true }) - expect(stops()).toHaveLength(1) - await update({ agentWorking: true }) - expect(stops()).toHaveLength(1) - }) - function listIds(): string[] { return (list().props.data as { id: string }[]).map((row) => row.id) } diff --git a/mobile/src/session/MobileNativeChatView.turn-status.test.ts b/mobile/src/session/MobileNativeChatView.turn-status.test.ts index c49e3d89d5f3..cec5253b1e88 100644 --- a/mobile/src/session/MobileNativeChatView.turn-status.test.ts +++ b/mobile/src/session/MobileNativeChatView.turn-status.test.ts @@ -166,6 +166,67 @@ describe('MobileNativeChatView', () => { return listNode.props.renderItem({ item: data[index], index }) } + it("holds Stop and says Stopping while this phone's own Stop request is in flight", async () => { + await render({ + structuredActivityUi: true, + agentWorking: true, + canStop: true, + turnIndicator: { + thinking: false, + activityText: null, + stopping: true, + stopRequestInFlight: true + } + }) + const stop = renderer!.root.find( + (node) => node.type === 'Pressable' && node.props.accessibilityLabel === 'Stopping…' + ) + expect(stop.props.disabled).toBe(true) + }) + + it.each([ + ['queue', 'Queue a message to run after the stop'], + ['send', 'Send a message to run after the stop'] + ] as const)( + 'tells the composer a message sent now runs after the stop (%s)', + async (afterStop, placeholder) => { + await render({ + structuredActivityUi: true, + agentWorking: true, + canStop: true, + turnIndicator: { thinking: false, activityText: null, stopping: true, afterStop } + }) + const composer = renderer!.root.find((node) => node.type === 'Composer') + expect(composer.props.placeholder).toBe(placeholder) + } + ) + + // A Stop the provider took and never answered ends only at a repeat Stop. + it('keeps Stop for the repeat that escalates while the host alone says Stopping', async () => { + await render({ + structuredActivityUi: true, + agentWorking: true, + canStop: true, + turnIndicator: { thinking: false, activityText: null, stopping: true } + }) + const stop = renderer!.root.find( + (node) => node.type === 'Pressable' && node.props.accessibilityLabel === 'Stop the agent' + ) + expect(stop.props.disabled).toBe(false) + }) + + it('keeps Stop hidden during a structured dispatch until a provider turn can be cancelled', async () => { + const props = { structuredActivityUi: true, agentWorking: true, canStop: false } + await render(props) + const stops = () => + renderer!.root.findAll((node) => node.props.accessibilityLabel === 'Stop the agent') + expect(stops()).toHaveLength(0) + await update({ ...props, canStop: true }) + expect(stops()).toHaveLength(1) + await update({ agentWorking: true }) + expect(stops()).toHaveLength(1) + }) + describe('structured turn status wiring', () => { const userTurn = (id: string, text: string): NativeChatMessage => ({ id,