@@ -178,7 +178,6 @@ async function render(
178178 | 'showModeSelector'
179179 | 'onModeChange'
180180 | 'restoredContexts'
181- | 'allowNoAssistant'
182181 | 'assistantSearchLevel'
183182 | 'onAssistantSearchLevelChange'
184183 > = { isSending : false }
@@ -197,7 +196,6 @@ async function render(
197196 < Composer
198197 requestMode = { requestMode }
199198 assistantSearchLevel = { assistantSearchLevel }
200- allowNoAssistant = { controls . allowNoAssistant }
201199 onAssistantSearchLevelChange = {
202200 controls . onAssistantSearchLevelChange ?? setAssistantSearchLevel
203201 }
@@ -237,7 +235,7 @@ describe('organization voice composer', () => {
237235 expect ( container . querySelector ( '[aria-label="Model and reasoning effort"]' ) ) . toBeNull ( )
238236 expect ( container . querySelector ( '[aria-label="Fast mode"]' ) ) . toBeNull ( )
239237 expect ( mic . previousElementSibling ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Search level' )
240- expect ( mic . nextElementSibling ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Send' )
238+ expect ( mic . parentElement ?. nextElementSibling ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Send' )
241239 await act ( async ( ) => mic . click ( ) )
242240 expect ( mocks . toggleListening ) . toHaveBeenCalledOnce ( )
243241 const speech = mocks . speech . mock . calls . at ( - 1 ) ! [ 0 ]
@@ -372,19 +370,12 @@ describe('organization image composer', () => {
372370 )
373371 } )
374372
375- it ( 'uses the picker and lets an attachment be removed before sending' , async ( ) => {
373+ it ( 'lets a pasted attachment be removed before sending without an attachment button ' , async ( ) => {
376374 await render ( true , '' )
377375 const input = container . querySelector < HTMLInputElement > ( 'input[type="file"]' ) !
378- const click = vi . spyOn ( input , 'click' )
379- await act ( async ( ) =>
380- container . querySelector < HTMLButtonElement > ( 'button[aria-label="Attach images"]' ) ! . click ( )
381- )
382- expect ( click ) . toHaveBeenCalledOnce ( )
376+ expect ( container . querySelector ( '[aria-label="Attach images"]' ) ) . toBeNull ( )
383377 expect ( input . accept ) . toContain ( 'image/png' )
384- Object . defineProperty ( input , 'files' , {
385- value : fileList ( [ new File ( [ 'image' ] , 'screenshot.png' , { type : 'image/png' } ) ] ) ,
386- } )
387- await act ( async ( ) => input . dispatchEvent ( new Event ( 'change' , { bubbles : true } ) ) )
378+ await paste ( [ new File ( [ 'image' ] , 'screenshot.png' , { type : 'image/png' } ) ] )
388379 await act ( async ( ) =>
389380 container
390381 . querySelector < HTMLButtonElement > ( 'button[aria-label="Remove screenshot.png"]' ) !
@@ -505,7 +496,7 @@ it.each([true, false])(
505496 expect ( container . querySelector ( '[aria-label="Model and reasoning effort"]' ) ) . toBeNull ( )
506497 expect ( container . querySelector ( '[aria-label="Fast mode"]' ) ) . toBeNull ( )
507498 expect ( Boolean ( container . querySelector ( '[aria-label="Conversation mode"]' ) ) ) . toBe ( canChoose )
508- expect ( container . querySelector ( '[aria-label="Attach images"]' ) ) . not . toBeNull ( )
499+ expect ( container . querySelector ( '[aria-label="Attach images"]' ) ) . toBeNull ( )
509500 }
510501)
511502
@@ -590,25 +581,26 @@ it('shows global built-ins once with no workspace label and submits no invented
590581 ] )
591582} )
592583
593- it ( 'offers an icon-only controlled mode picker before the staging input controls ' , async ( ) => {
584+ it ( 'shows Build with a chevron in the shared chip and text-only modes in its menu ' , async ( ) => {
594585 const onModeChange = vi . fn ( )
595586 await render ( true , 'Preserved draft' , 'agent' , {
596587 isSending : false ,
597588 showModeSelector : true ,
598589 onModeChange,
599590 } )
600591 const mode = container . querySelector < HTMLButtonElement > ( '[aria-label="Conversation mode"]' ) !
601- expect ( mode . previousElementSibling ) . toBeNull ( )
602- expect ( mode . nextElementSibling ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Add resources' )
603- expect ( mode . textContent ) . toBe ( '' )
604- expect ( mode . querySelector ( 'svg' ) ) . not . toBeNull ( )
592+ expect ( mode . parentElement ?. previousElementSibling ) . toBeNull ( )
593+ expect ( mode . parentElement ?. nextElementSibling ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Add resources' )
594+ expect ( mode . textContent ) . toBe ( 'Build ' )
595+ expect ( mode . querySelectorAll ( 'svg' ) ) . toHaveLength ( 1 )
605596 await act ( async ( ) =>
606597 mode . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Enter' , bubbles : true } ) )
607598 )
608- const search = [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitemradio "]' ) ] . find (
599+ const search = [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitem "]' ) ] . find (
609600 ( item ) => item . textContent === 'Search'
610601 ) !
611- expect ( search . querySelector ( 'svg' ) ) . not . toBeNull ( )
602+ expect ( search . querySelector ( 'svg' ) ) . toBeNull ( )
603+ expect ( document . querySelector ( '[role="tooltip"]' ) ) . toBeNull ( )
612604 await act ( async ( ) => search . click ( ) )
613605 expect ( onModeChange ) . toHaveBeenCalledExactlyOnceWith ( 'assistant' )
614606 expect (
@@ -628,7 +620,7 @@ it('allows changing the next message mode while a response is streaming', async
628620 await act ( async ( ) =>
629621 mode . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Enter' , bubbles : true } ) )
630622 )
631- const search = [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitemradio "]' ) ] . find (
623+ const search = [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitem "]' ) ] . find (
632624 ( item ) => item . textContent === 'Search'
633625 ) !
634626 await act ( async ( ) => search . click ( ) )
@@ -701,7 +693,7 @@ it.each(['skill', 'file'] as const)(
701693 await act ( async ( ) =>
702694 mode . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Enter' , bubbles : true } ) )
703695 )
704- const search = [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitemradio "]' ) ] . find (
696+ const search = [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitem "]' ) ] . find (
705697 ( item ) => item . textContent === 'Search'
706698 ) !
707699 await act ( async ( ) => search . click ( ) )
@@ -769,11 +761,13 @@ describe('Search levels', () => {
769761 onModeChange : vi . fn ( ) ,
770762 } )
771763 const mode = container . querySelector ( '[aria-label="Conversation mode"]' ) !
772- const row = mode . parentElement ! . parentElement !
764+ const row = mode . parentElement ! . parentElement ! . parentElement !
773765 expect (
774766 row . querySelector < HTMLInputElement | HTMLTextAreaElement > ( '[aria-label="Ask Sim"]' )
775767 ) . not . toBeNull ( )
776- expect ( mode . nextElementSibling ?. getAttribute ( 'aria-label' ) ) . toBe ( 'Attach images' )
768+ expect ( mode . textContent ) . toBe ( '' )
769+ expect ( mode . querySelectorAll ( 'svg' ) ) . toHaveLength ( 2 )
770+ expect ( mode . parentElement ?. nextElementSibling ) . toBeNull ( )
777771 expect ( row . querySelector ( '[aria-label="Search level"]' ) ?. textContent ) . toBe ( 'Auto' )
778772 } )
779773} )
@@ -790,32 +784,15 @@ it('keeps Build Fast independent of Search levels', async () => {
790784 await act ( async ( ) => useMothershipEffortStore . getState ( ) . setFastMode ( false ) )
791785} )
792786
793- it . each ( [ true , false ] ) (
794- 'only offers None before a conversation (allowed: %s)' ,
795- async ( allowNoAssistant ) => {
796- await render ( true , '' , 'assistant' , { isSending : false , allowNoAssistant } )
797- const picker = container . querySelector < HTMLButtonElement > ( '[aria-label="Search level"]' ) !
798- await act ( async ( ) =>
799- picker . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Enter' , bubbles : true } ) )
800- )
801- const options = [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitemradio"]' ) ] . map (
787+ it ( 'offers only Fast, Auto, and Max search levels' , async ( ) => {
788+ await render ( true , '' , 'assistant' , { isSending : false } )
789+ const picker = container . querySelector < HTMLButtonElement > ( '[aria-label="Search level"]' ) !
790+ await act ( async ( ) =>
791+ picker . dispatchEvent ( new KeyboardEvent ( 'keydown' , { key : 'Enter' , bubbles : true } ) )
792+ )
793+ expect (
794+ [ ...document . querySelectorAll < HTMLElement > ( '[role="menuitemradio"]' ) ] . map (
802795 ( item ) => item . textContent
803796 )
804- expect ( options ) . toEqual (
805- allowNoAssistant ? [ 'None' , 'Fast' , 'Auto' , 'Max' ] : [ 'Fast' , 'Auto' , 'Max' ]
806- )
807- }
808- )
809-
810- it ( 'keeps attachments out of results-only search' , async ( ) => {
811- await render ( true , 'Orion' , 'assistant' , {
812- isSending : false ,
813- assistantSearchLevel : 'none' ,
814- allowNoAssistant : true ,
815- } )
816- expect ( container . querySelector ( '[aria-label="Attach images"]' ) ) . toBeNull ( )
817- await paste ( [ new File ( [ 'image' ] , 'test.png' , { type : 'image/png' } ) ] )
818- expect ( mocks . upload ) . not . toHaveBeenCalled ( )
819- expect ( container . querySelector ( '[aria-label="Voice input"]' ) ) . not . toBeNull ( )
820- expect ( container . querySelector ( '[aria-label="Search"]' ) ) . not . toBeNull ( )
797+ ) . toEqual ( [ 'Fast' , 'Auto' , 'Max' ] )
821798} )
0 commit comments