@@ -23,6 +23,7 @@ const mocks = vi.hoisted(() => ({
2323 upload : vi . fn ( ) ,
2424 addResource : vi . fn ( ) ,
2525} ) )
26+ vi . mock ( '@/blocks/integration-matcher' , ( ) => ( { mentionifyIntegrations : ( text : string ) => text } ) )
2627vi . mock ( 'next/navigation' , ( ) => ( { useRouter : ( ) => ( { push : mocks . push } ) } ) )
2728vi . mock ( '@tanstack/react-query' , ( ) => ( { useQueryClient : ( ) => ( { fetchQuery : vi . fn ( ) } ) } ) )
2829vi . mock ( '@/app/workspace/[workspaceId]/home/hooks/use-resource-panel' , ( ) => ( {
@@ -54,6 +55,13 @@ vi.mock('@/hooks/queries/mothership-chats', () => ({
5455 useMarkMothershipChatRead : ( ) => ( { mutate : mocks . markRead } ) ,
5556} ) )
5657vi . mock ( '@/app/o/[organizationId]/home/components/composer' , ( ) => ( { Composer : mocks . composer } ) )
58+ vi . mock ( '@/app/workspace/[workspaceId]/home/components/suggested-actions' , ( ) => ( {
59+ SuggestedActions : ( { onSelectPrompt } : { onSelectPrompt : ( prompt : string ) => void } ) => (
60+ < button onClick = { ( ) => onSelectPrompt ( 'Create a CRM with sample data.' ) } >
61+ Suggested actions
62+ </ button >
63+ ) ,
64+ } ) )
5765vi . mock ( '@/hooks/queries/kb/connectors' , ( ) => ( { useSearchSourceOverview : mocks . sources } ) )
5866vi . mock ( '@/hooks/queries/api-keys' , ( ) => ( { useApiKeys : mocks . apiKeys } ) )
5967vi . mock ( '@/hooks/queries/oauth-provider' , ( ) => ( { useAuthorizedApps : mocks . authorizedApps } ) )
@@ -80,7 +88,7 @@ beforeEach(() => {
8088 )
8189 mocks . upload . mockResolvedValue ( { key : 'image-key' , path : '/image-path' } )
8290 mocks . context . mockReturnValue ( {
83- organization : { id : 'organization-a' } ,
91+ organization : { id : 'organization-a' , name : 'Acme' } ,
8492 searchAccess : { memberScoped : true } ,
8593 mothershipAvailable : true ,
8694 canBuild : true ,
@@ -159,7 +167,8 @@ describe('organization home', () => {
159167 await act ( async ( ) => renderHome ( < OrganizationHome userName = 'Ada Lovelace' /> ) )
160168 expect ( container . textContent ) . toContain ( 'What should we get done, Ada?' )
161169 expect ( container . textContent ) . toContain ( 'Question composer' )
162- expect ( container . textContent ) . toContain ( 'Get started' )
170+ expect ( container . textContent ) . toContain ( 'Suggested actions' )
171+ expect ( container . textContent ) . not . toContain ( 'Get started' )
163172 expect ( mocks . renderer ) . not . toHaveBeenCalled ( )
164173 expect ( mocks . chat ) . toHaveBeenCalledWith (
165174 { organizationId : 'organization-a' } ,
@@ -240,13 +249,13 @@ describe('organization home', () => {
240249 'routes onboarding for admin=$isAdmin without a workspace creation requirement' ,
241250 async ( { isAdmin, integrationHref } ) => {
242251 mocks . context . mockReturnValue ( {
243- organization : { id : 'organization-a' } ,
252+ organization : { id : 'organization-a' , name : 'Acme' } ,
244253 searchAccess : { memberScoped : true } ,
245254 mothershipAvailable : true ,
246255 canBuild : true ,
247256 viewer : { isAdmin, canUseSearchMcp : true } ,
248257 } )
249- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
258+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
250259 expect (
251260 Array . from ( container . querySelectorAll ( 'a' ) ) . map ( ( link ) => ( {
252261 label : link . textContent ,
@@ -265,21 +274,21 @@ describe('organization home', () => {
265274 )
266275 it ( 'does not complete MCP onboarding for an unrelated personal API key' , async ( ) => {
267276 mocks . apiKeys . mockReturnValue ( { data : { personalKeys : [ { id : 'workflow-api-key' } ] } } )
268- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
277+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
269278 expect ( hasCompletedMcpStep ( ) ) . toBe ( false )
270279 expect ( mocks . apiKeys ) . not . toHaveBeenCalled ( )
271280 } )
272281
273282 it ( 'hides MCP onboarding and stops authorization paging when organization policy blocks access' , async ( ) => {
274283 mocks . context . mockReturnValue ( {
275- organization : { id : 'organization-a' } ,
284+ organization : { id : 'organization-a' , name : 'Acme' } ,
276285 searchAccess : { memberScoped : true } ,
277286 mothershipAvailable : true ,
278287 canBuild : true ,
279288 viewer : { isAdmin : false , canUseSearchMcp : false } ,
280289 } )
281290 mockAuthorizedApps ( [ { apps : [ ] , nextCursor : 'older-apps' } ] )
282- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
291+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
283292 expect ( container . textContent ) . not . toContain ( 'Connect Sim Search MCP' )
284293 expect ( container . textContent ) . toContain ( 'Connect an integration' )
285294 expect ( mocks . authorizedApps ) . toHaveBeenCalledWith ( '' , { enabled : false } )
@@ -295,7 +304,7 @@ describe('organization home', () => {
295304 { scopes : [ 'unrecognized:read' ] , completed : false } ,
296305 ] ) ( 'derives MCP completion from OAuth scopes $scopes' , async ( { scopes, completed } ) => {
297306 mockAuthorizedApps ( [ { apps : [ authorizedApp ( scopes ) ] , nextCursor : null } ] )
298- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
307+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
299308 expect ( hasCompletedMcpStep ( ) ) . toBe ( completed )
300309 } )
301310
@@ -305,15 +314,15 @@ describe('organization home', () => {
305314 nextCursor : 'older-apps' ,
306315 }
307316 mockAuthorizedApps ( [ firstPage ] )
308- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
317+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
309318 expect ( hasCompletedMcpStep ( ) ) . toBe ( false )
310319 expect ( mocks . fetchNextPage ) . toHaveBeenCalledTimes ( 1 )
311320
312321 mockAuthorizedApps ( [
313322 firstPage ,
314323 { apps : [ authorizedApp ( [ 'search:read' ] ) ] , nextCursor : 'even-older-apps' } ,
315324 ] )
316- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
325+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
317326 expect ( hasCompletedMcpStep ( ) ) . toBe ( true )
318327 expect ( mocks . fetchNextPage ) . toHaveBeenCalledTimes ( 1 )
319328 } )
@@ -322,19 +331,19 @@ describe('organization home', () => {
322331 'does not start another authorization page request while %j' ,
323332 async ( state ) => {
324333 mockAuthorizedApps ( [ { apps : [ ] , nextCursor : 'older-apps' } ] , state )
325- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
334+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
326335 expect ( mocks . fetchNextPage ) . not . toHaveBeenCalled ( )
327336 expect ( hasCompletedMcpStep ( ) ) . toBe ( false )
328337 }
329338 )
330339
331340 it ( 'clears MCP completion when the Search authorization is revoked' , async ( ) => {
332341 mockAuthorizedApps ( [ { apps : [ authorizedApp ( [ 'search:read' ] ) ] , nextCursor : null } ] )
333- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
342+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
334343 expect ( hasCompletedMcpStep ( ) ) . toBe ( true )
335344
336345 mockAuthorizedApps ( [ { apps : [ ] , nextCursor : null } ] )
337- await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
346+ await act ( async ( ) => renderHome ( < OrganizationHome requestMode = 'assistant' /> ) )
338347 expect ( hasCompletedMcpStep ( ) ) . toBe ( false )
339348 } )
340349
@@ -459,7 +468,7 @@ describe('organization home', () => {
459468
460469it ( 'uses agent mode on new Home and does not consume a Search handoff' , async ( ) => {
461470 mocks . context . mockReturnValue ( {
462- organization : { id : 'organization-a' } ,
471+ organization : { id : 'organization-a' , name : 'Acme' } ,
463472 mothershipAvailable : true ,
464473 canBuild : true ,
465474 searchAccess : { memberScoped : false } ,
@@ -642,3 +651,22 @@ it('keeps the latest submitted Search query when the next turn is switched to Bu
642651 undefined
643652 )
644653} )
654+
655+ it ( 'fills the Build draft from suggested actions without sending' , async ( ) => {
656+ await act ( async ( ) => renderHome ( < OrganizationHome userName = 'Ada Lovelace' /> ) )
657+ expect ( container . querySelector ( 'h1' ) ?. textContent ) . toBe ( 'What should we get done, Ada?' )
658+ const suggestion = [ ...container . querySelectorAll ( 'button' ) ] . find (
659+ ( button ) => button . textContent === 'Suggested actions'
660+ ) !
661+ await act ( async ( ) => suggestion . click ( ) )
662+ expect ( composerProps ( ) . value ) . toBe ( 'Create a CRM with sample data.' )
663+ expect ( mocks . send ) . not . toHaveBeenCalled ( )
664+ } )
665+
666+ it ( 'shows staging Search heading and setup steps instead of Build suggestions' , async ( ) => {
667+ await act ( async ( ) => renderHome ( < OrganizationHome /> ) )
668+ await act ( async ( ) => composerProps ( ) . onModeChange ?.( 'assistant' ) )
669+ expect ( container . querySelector ( 'h1' ) ?. textContent ) . toBe ( 'Search Acme' )
670+ expect ( container . textContent ) . toContain ( 'Get started' )
671+ expect ( container . textContent ) . not . toContain ( 'Suggested actions' )
672+ } )
0 commit comments