11/**
22 * @vitest -environment jsdom
33 */
4- import { act } from 'react'
4+ import { act , useState } from 'react'
55import { createRoot } from 'react-dom/client'
66import { afterEach , describe , expect , it , vi } from 'vitest'
77
@@ -13,31 +13,36 @@ import { QueuedMessages } from '@/app/workspace/[workspaceId]/home/components/qu
1313const mounted : Array < ( ) => void > = [ ]
1414
1515/** jsdom has no ResizeObserver; the queue measures its width with one. */
16- class ResizeObserverMock {
17- observe = vi . fn ( )
18- unobserve = vi . fn ( )
19- disconnect = vi . fn ( )
16+ class ResizeObserverStub {
17+ observe ( ) { }
18+ unobserve ( ) { }
19+ disconnect ( ) { }
20+ }
21+
22+ /** The queue as the chat composer drives it: Edit opens the entry, Cancel edit closes it. */
23+ function EditableQueue ( { admissionUnknown } : { admissionUnknown : boolean } ) {
24+ const [ editingQueuedId , setEditingQueuedId ] = useState < string | null > ( null )
25+ return (
26+ < QueuedMessages
27+ messageQueue = { [ { id : 'held-first' , content : 'inspect the workspace' , admissionUnknown } ] }
28+ editingQueuedId = { editingQueuedId }
29+ dispatchingHeadId = { null }
30+ onRemove = { ( ) => { } }
31+ onSendNow = { async ( ) => { } }
32+ onEdit = { setEditingQueuedId }
33+ onCancelEdit = { ( ) => setEditingQueuedId ( null ) }
34+ />
35+ )
2036}
2137
2238function renderQueue ( admissionUnknown : boolean ) {
2339 vi . stubGlobal ( 'IS_REACT_ACT_ENVIRONMENT' , true )
24- vi . stubGlobal ( 'ResizeObserver' , ResizeObserverMock )
40+ vi . stubGlobal ( 'ResizeObserver' , ResizeObserverStub )
2541 const container = document . createElement ( 'div' )
2642 document . body . appendChild ( container )
2743 const root = createRoot ( container )
28- const onEdit = vi . fn ( )
2944 act ( ( ) => {
30- root . render (
31- < QueuedMessages
32- messageQueue = { [ { id : 'held-first' , content : 'inspect the workspace' , admissionUnknown } ] }
33- editingQueuedId = { null }
34- dispatchingHeadId = { null }
35- onRemove = { ( ) => { } }
36- onSendNow = { async ( ) => { } }
37- onEdit = { onEdit }
38- onCancelEdit = { ( ) => { } }
39- />
40- )
45+ root . render ( < EditableQueue admissionUnknown = { admissionUnknown } /> )
4146 } )
4247 mounted . push ( ( ) => {
4348 act ( ( ) => root . unmount ( ) )
@@ -47,7 +52,9 @@ function renderQueue(admissionUnknown: boolean) {
4752 'button[aria-label="Edit queued message"], button[aria-label^="May already be sent"]'
4853 )
4954 if ( ! editButton ) throw new Error ( 'Edit button not rendered' )
50- return { editButton, onEdit }
55+ /** An entry open for editing shows its Cancel edit control in place of Edit. */
56+ const isEditing = ( ) => container . querySelector ( 'button[aria-label="Cancel edit"]' ) !== null
57+ return { editButton, isEditing }
5158}
5259
5360describe ( 'QueuedMessages edit button' , ( ) => {
@@ -56,7 +63,7 @@ describe('QueuedMessages edit button', () => {
5663 } )
5764
5865 it ( 'keeps a message that may already be sent from being edited, and says why' , ( ) => {
59- const { editButton, onEdit } = renderQueue ( true )
66+ const { editButton, isEditing } = renderQueue ( true )
6067
6168 /** Still focusable and hoverable, so its tooltip can explain why. */
6269 expect ( editButton . disabled ) . toBe ( false )
@@ -65,13 +72,13 @@ describe('QueuedMessages edit button', () => {
6572 'May already be sent; editing could send a second message'
6673 )
6774 act ( ( ) => editButton . click ( ) )
68- expect ( onEdit ) . not . toHaveBeenCalled ( )
75+ expect ( isEditing ( ) ) . toBe ( false )
6976 } )
7077
71- it ( 'edits an ordinary queued message' , ( ) => {
72- const { editButton, onEdit } = renderQueue ( false )
78+ it ( 'opens an ordinary queued message for editing ' , ( ) => {
79+ const { editButton, isEditing } = renderQueue ( false )
7380
7481 act ( ( ) => editButton . click ( ) )
75- expect ( onEdit ) . toHaveBeenCalledWith ( 'held-first' )
82+ expect ( isEditing ( ) ) . toBe ( true )
7683 } )
7784} )
0 commit comments