33 */
44import { act } from 'react'
55import { createRoot , type Root } from 'react-dom/client'
6- import { afterEach , beforeEach , describe , expect , it } from 'vitest'
6+ import { afterEach , beforeEach , describe , expect , it , vi } from 'vitest'
77import { ActivityStatus } from '@/components/ui/activity-status'
88import { ToolActivityGroup } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group'
99import type { ToolCallItemProps } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item'
@@ -46,6 +46,8 @@ describe('ToolActivityGroup search disclosure', () => {
4646 let root : Root
4747
4848 beforeEach ( ( ) => {
49+ vi . stubGlobal ( 'IS_REACT_ACT_ENVIRONMENT' , true )
50+ vi . useFakeTimers ( )
4951 container = document . createElement ( 'div' )
5052 document . body . append ( container )
5153 root = createRoot ( container )
@@ -54,12 +56,13 @@ describe('ToolActivityGroup search disclosure', () => {
5456 afterEach ( ( ) => {
5557 act ( ( ) => root . unmount ( ) )
5658 container . remove ( )
59+ vi . useRealTimers ( )
5760 } )
5861
59- function render ( tool : ToolCallData , isLive : boolean ) {
62+ function render ( tools : ToolCallData [ ] , isLive : boolean ) {
6063 act ( ( ) =>
6164 root . render (
62- < ToolActivityGroup tools = { [ tool ] } isLive = { isLive } ToolCallComponent = { TestToolCall } />
65+ < ToolActivityGroup tools = { tools } isLive = { isLive } ToolCallComponent = { TestToolCall } />
6366 )
6467 )
6568 }
@@ -71,26 +74,55 @@ describe('ToolActivityGroup search disclosure', () => {
7174 }
7275
7376 it ( 'opens when live results arrive, closes for the answer, and respects manual choices' , ( ) => {
74- render ( executingSearch , true )
77+ render ( [ executingSearch ] , true )
7578 expect ( container . querySelector ( '[role="button"][aria-expanded]' ) ) . toBeNull ( )
7679
77- render ( completedSearch , true )
80+ render ( [ completedSearch ] , true )
7881 expect ( disclosure ( ) . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
7982 expect ( container . querySelector ( 'a[href="https://example.test/guide"]' ) ) . not . toBeNull ( )
8083
81- render ( completedSearch , false )
84+ render ( [ completedSearch ] , false )
8285 expect ( disclosure ( ) . getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
8386
8487 act ( ( ) => disclosure ( ) . click ( ) )
8588 expect ( disclosure ( ) . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
86- render ( completedSearch , false )
89+ render ( [ completedSearch ] , false )
8790 expect ( disclosure ( ) . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
88- render ( completedSearch , true )
91+ render ( [ completedSearch ] , true )
8992 expect ( disclosure ( ) . getAttribute ( 'aria-expanded' ) ) . toBe ( 'true' )
9093
9194 act ( ( ) => disclosure ( ) . click ( ) )
92- render ( completedSearch , false )
93- render ( completedSearch , true )
95+ render ( [ completedSearch ] , false )
96+ render ( [ completedSearch ] , true )
9497 expect ( disclosure ( ) . getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
9598 } )
99+
100+ it ( 'keeps empty searches out of the disclosure while subsequent tools run' , ( ) => {
101+ const emptySearch : ToolCallData = {
102+ ...completedSearch ,
103+ id : 'empty-search' ,
104+ result : { success : true , output : { success : true , data : { results : [ ] } } } ,
105+ }
106+ const runningRead : ToolCallData = {
107+ id : 'read-1' ,
108+ toolName : 'read_document' ,
109+ displayTitle : 'Reading document' ,
110+ status : 'executing' ,
111+ }
112+
113+ render ( [ emptySearch ] , true )
114+ expect . soft ( container . querySelector ( '[role="button"][aria-expanded]' ) ) . toBeNull ( )
115+
116+ render ( [ emptySearch , runningRead ] , true )
117+ expect . soft ( disclosure ( ) . getAttribute ( 'aria-expanded' ) ) . toBe ( 'false' )
118+
119+ if ( disclosure ( ) . getAttribute ( 'aria-expanded' ) === 'false' ) {
120+ act ( ( ) => disclosure ( ) . click ( ) )
121+ }
122+ expect . soft ( container . textContent ) . not . toContain ( 'No results' )
123+
124+ render ( [ emptySearch , completedSearch ] , true )
125+ expect ( container . querySelector ( 'a[href="https://example.test/guide"]' ) ) . not . toBeNull ( )
126+ expect ( container . textContent ) . not . toContain ( 'No results' )
127+ } )
96128} )
0 commit comments