@@ -62,8 +62,10 @@ const STAGES = {
6262 bookmark : 'Existing assistant bookmarks redirect into Search' ,
6363 denied : 'Assistant history rejects a chat from another organization' ,
6464 streamedDenied : 'A streamed denied chat remains navigable after hydration' ,
65- searchOnly :
66- 'Search-only members land on Search with the Assistant and their history, but no agent chat' ,
65+ noWorkspaceCreation :
66+ 'A member who may not create workspaces keeps Home, agent chat and Assistant history' ,
67+ noChat :
68+ 'A member whose permission group withholds chat lands on Search, and chat URLs return there' ,
6769} as const
6870const checks : { name : string ; status : 'passed' | 'failed' ; durationMs : number ; error ?: string } [ ] =
6971 [ ]
@@ -823,7 +825,9 @@ try {
823825 try {
824826 await page . goto ( appUrl ( `${ routes . search } ?q=Launch` ) , { waitUntil : 'commit' } )
825827 await expect ( input ) . toBeVisible ( )
826- await expect ( input ) . toBeDisabled ( )
828+ // The field takes typing before hydration; only submitting waits for it.
829+ await expect ( input ) . toBeEditable ( )
830+ await expect ( input ) . toHaveValue ( 'Launch' )
827831 await expect (
828832 page . getByRole ( 'search' ) . getByRole ( 'button' , { name : 'Search' , exact : true } )
829833 ) . toBeDisabled ( )
@@ -1064,21 +1068,35 @@ try {
10641068 await expect ( sourcesPanel ) . toBeVisible ( )
10651069 await capture ( 'assistant-resources-mobile' )
10661070 } )
1067- await check ( STAGES . searchOnly , async ( ) => {
1071+ const restrictedGroupId = generateId ( )
1072+ await check ( STAGES . noWorkspaceCreation , async ( ) => {
1073+ // Chat follows the permission group's `copilot.use`, never workspace creation.
10681074 await sql `insert into permission_group (id, organization_id, name, config, created_by, is_default)
1069- values (${ generateId ( ) } , ${ organizationId } , 'Search only ',
1075+ values (${ restrictedGroupId } , ${ organizationId } , 'No workspace creation ',
10701076 ${ sql . json ( { disableWorkspaceCreation : true } ) } , ${ userId } , true)`
10711077 await page . goto ( appUrl ( routes . home ) )
1072- await expect ( page ) . toHaveURL ( ( url ) => url . pathname === routes . search )
1073- await expect ( searchMode ( page , 'Assistant' ) ) . toBeVisible ( )
1078+ await expect ( homeComposer ( page ) ) . toBeVisible ( )
1079+ assert . equal ( new URL ( page . url ( ) ) . pathname , routes . home )
10741080 await page . goto ( appUrl ( routes . chat ( agentId ) ) )
1075- await expect ( page ) . toHaveURL ( ( url ) => url . pathname === routes . search )
1081+ await expect ( page ) . toHaveURL ( ( url ) => url . pathname === routes . chat ( agentId ) )
10761082 await page . goto ( appUrl ( routes . searchChat ( assistantId ) ) )
10771083 await expect ( page . getByText ( answer , { exact : true } ) ) . toBeVisible ( )
10781084 } )
1079- await check ( 'Search-only members recover legacy drafts without a remembered mode ' , ( ) =>
1080- verifyLegacyDraft ( undefined , 'assistant ' )
1085+ await check ( 'A member who may not create workspaces recovers legacy drafts on Home ' , ( ) =>
1086+ verifyLegacyDraft ( undefined , 'agent ' )
10811087 )
1088+ await check ( STAGES . noChat , async ( ) => {
1089+ await sql `update permission_group set config = ${ sql . json ( { hideCopilot : true } ) }
1090+ where id = ${ restrictedGroupId } `
1091+ await page . goto ( appUrl ( routes . home ) )
1092+ await expect ( page ) . toHaveURL ( ( url ) => url . pathname === routes . search )
1093+ await expect ( searchField ( page ) ) . toBeVisible ( )
1094+ await page . goto ( appUrl ( routes . chat ( agentId ) ) )
1095+ await expect ( page ) . toHaveURL ( ( url ) => url . pathname === routes . search )
1096+ await page . goto ( appUrl ( routes . searchChat ( assistantId ) ) )
1097+ await expect ( page ) . toHaveURL ( ( url ) => url . pathname === routes . search )
1098+ await expect ( page . getByText ( answer , { exact : true } ) ) . toHaveCount ( 0 )
1099+ } )
10821100 await check ( 'The UI completes without unexpected browser runtime errors' , async ( ) => {
10831101 // Server redirects inside a streamed page surface as a handled React error on the client.
10841102 const handledRedirects = [
@@ -1088,8 +1106,9 @@ try {
10881106 from : routes . chat ( assistantId ) ,
10891107 to : routes . searchChat ( assistantId ) ,
10901108 } ,
1091- { stage : STAGES . searchOnly , from : routes . home , to : routes . search } ,
1092- { stage : STAGES . searchOnly , from : routes . chat ( agentId ) , to : routes . search } ,
1109+ { stage : STAGES . noChat , from : routes . home , to : routes . search } ,
1110+ { stage : STAGES . noChat , from : routes . chat ( agentId ) , to : routes . search } ,
1111+ { stage : STAGES . noChat , from : routes . searchChat ( assistantId ) , to : routes . search } ,
10931112 ]
10941113 const handledRedirectCount = ( ) =>
10951114 browserErrorEvents . reduce (
0 commit comments