@@ -73,6 +73,8 @@ import { OrganizationSearch } from '@/app/o/[organizationId]/search/search'
7373import { KnowledgeSearchResults } from '@/app/workspace/[workspaceId]/home/components/knowledge-search-results/knowledge-search-results'
7474import { searchHistoryKeys } from '@/hooks/queries/search-history'
7575import { knowledgeKeys } from '@/hooks/queries/utils/knowledge-keys'
76+ import { SearchHistory } from '@/platform/app/(shell)/search/_components/search-surface/search-history'
77+ import { OrganizationSearchSurface } from '@/platform/app/(shell)/search/_components/search-surface/search-surface'
7678import { useMothershipDraftsStore } from '@/stores/mothership-drafts/store'
7779
7880nextNavigationMockFns . mockUsePathname . mockReturnValue ( '/o/organization/search' )
@@ -141,23 +143,23 @@ async function render({
141143 scope = { kind : 'organization' , organizationId : 'organization' } ,
142144 query = 'launch' ,
143145 params = '' ,
144- organizationPage = false ,
146+ organizationPage : OrganizationPage ,
145147 filters,
146148 topK,
147149} : {
148150 scope ?: ResourceScope
149151 query ?: string
150152 params ?: string
151- organizationPage ?: boolean
153+ organizationPage ?: typeof OrganizationSearch
152154 filters ?: import ( '@/lib/api/contracts/knowledge' ) . WorkspaceSearchFilters
153155 topK ?: number
154156} = { } ) {
155157 await act ( async ( ) => {
156158 root . render (
157159 < QueryClientProvider client = { client } >
158160 < NuqsTestingAdapter hasMemory searchParams = { params } onUrlUpdate = { mocks . urlUpdate } >
159- { organizationPage ? (
160- < OrganizationSearch userId = 'reader' />
161+ { OrganizationPage ? (
162+ < OrganizationPage userId = 'reader' />
161163 ) : (
162164 < KnowledgeSearchResults
163165 scope = { scope }
@@ -209,13 +211,16 @@ async function complete(
209211 } )
210212}
211213
212- describe ( 'live search submission feedback' , ( ) => {
214+ describe . each ( [
215+ [ 'the app chrome' , OrganizationSearch ] ,
216+ [ 'the organization route' , OrganizationSearchSurface ] ,
217+ ] ) ( 'live search submission feedback in %s' , ( _view , organizationPage ) => {
213218 it . each ( [ 'launch' , 'edited draft' , '' ] ) (
214219 'cancels with draft %j, ignores late results, and allows a fresh submission' ,
215220 async ( draft ) => {
216221 const shape = resolveDeploymentShape ( )
217222 seedDeploymentShape ( { ...shape , features : shape . features } )
218- await render ( { organizationPage : true , params : '?q=launch' } )
223+ await render ( { organizationPage, params : '?q=launch' } )
219224 await act ( async ( ) => {
220225 await vi . advanceTimersByTimeAsync ( 1 )
221226 } )
@@ -267,16 +272,12 @@ describe('live search submission feedback', () => {
267272 it ( 'acknowledges the submitted query before exposing refinement controls' , async ( ) => {
268273 const shape = resolveDeploymentShape ( )
269274 seedDeploymentShape ( { ...shape , features : shape . features } )
270- await render ( { organizationPage : true , params : '?q=launch' } )
275+ await render ( { organizationPage, params : '?q=launch' } )
271276 await act ( async ( ) => {
272277 await vi . advanceTimersByTimeAsync ( 1 )
273278 } )
274279 expect ( requests ) . toHaveLength ( 1 )
275- expect (
276- [ ...container . querySelectorAll ( 'button' ) ] . some (
277- ( element ) => element . textContent === 'All sources'
278- )
279- ) . toBe ( false )
280+ expect ( container . querySelector ( '[role="group"][aria-label="Search filters"]' ) ) . toBeNull ( )
280281 expect (
281282 container . querySelector < HTMLButtonElement > ( 'button[aria-label="Stop search"]' ) ?. disabled
282283 ) . toBe ( false )
@@ -312,55 +313,76 @@ describe('live search submission feedback', () => {
312313 } )
313314} )
314315
315- it ( 'removes cached private history when access is denied during revalidation' , async ( ) => {
316- client . setQueryData ( searchHistoryKeys . list ( 'organization' , 'reader' ) , {
317- sources : [
318- {
319- url : 'https://example.com/private' ,
320- title : 'Private document' ,
321- viewedAt : new Date ( ) . toISOString ( ) ,
322- } ,
323- ] ,
324- queries : [ { query : 'private query' , searchedAt : new Date ( ) . toISOString ( ) } ] ,
325- } )
326- await act ( async ( ) => {
327- root . render (
328- < QueryClientProvider client = { client } >
329- < SearchLandingHistory organizationId = 'organization' userId = 'reader' onSearch = { ( ) => { } } >
330- < input aria-label = 'Search' />
331- </ SearchLandingHistory >
332- </ QueryClientProvider >
333- )
334- await vi . advanceTimersByTimeAsync ( 1 )
335- } )
336- expect ( container . querySelector ( 'a[href="https://example.com/private"]' ) ) . not . toBeNull ( )
337- mockRequestJson . mockRejectedValue ( new Error ( 'Forbidden' ) )
338- await act ( async ( ) => {
339- await client . invalidateQueries ( { queryKey : searchHistoryKeys . list ( 'organization' , 'reader' ) } )
340- await vi . advanceTimersByTimeAsync ( 1 )
341- } )
342- expect ( container . querySelector ( 'a[href="https://example.com/private"]' ) ) . toBeNull ( )
343- expect ( container . querySelector ( '[aria-label="Recent activity"]' ) ) . toBeNull ( )
344- } )
316+ it . each ( [
317+ [
318+ 'the app chrome' ,
319+ < SearchLandingHistory
320+ key = 'chrome'
321+ organizationId = 'organization'
322+ userId = 'reader'
323+ onSearch = { ( ) => { } }
324+ >
325+ < input aria-label = 'Search' />
326+ </ SearchLandingHistory > ,
327+ ] ,
328+ [
329+ 'the organization route' ,
330+ < SearchHistory key = 'route' userId = 'reader' onSearch = { ( ) => { } } >
331+ < input aria-label = 'Search' />
332+ </ SearchHistory > ,
333+ ] ,
334+ ] ) (
335+ 'removes cached private history when access is denied during revalidation in %s' ,
336+ async ( _view , history ) => {
337+ client . setQueryData ( searchHistoryKeys . list ( 'organization' , 'reader' ) , {
338+ sources : [
339+ {
340+ url : 'https://example.com/private' ,
341+ title : 'Private document' ,
342+ viewedAt : new Date ( ) . toISOString ( ) ,
343+ } ,
344+ ] ,
345+ queries : [ { query : 'private query' , searchedAt : new Date ( ) . toISOString ( ) } ] ,
346+ } )
347+ await act ( async ( ) => {
348+ root . render ( < QueryClientProvider client = { client } > { history } </ QueryClientProvider > )
349+ await vi . advanceTimersByTimeAsync ( 1 )
350+ } )
351+ expect ( container . querySelector ( 'a[href="https://example.com/private"]' ) ) . not . toBeNull ( )
352+ mockRequestJson . mockRejectedValue ( new Error ( 'Forbidden' ) )
353+ await act ( async ( ) => {
354+ await client . invalidateQueries ( { queryKey : searchHistoryKeys . list ( 'organization' , 'reader' ) } )
355+ await vi . advanceTimersByTimeAsync ( 1 )
356+ } )
357+ expect ( container . querySelector ( 'a[href="https://example.com/private"]' ) ) . toBeNull ( )
358+ expect ( container . querySelector ( '[aria-label="Recent activity"]' ) ) . toBeNull ( )
359+ }
360+ )
345361
346- it ( 'keeps an unsent draft when a different recent search is opened' , async ( ) => {
347- const draftKey = 'reader:organization:organization:search'
348- useMothershipDraftsStore
349- . getState ( )
350- . setDraft ( draftKey , { text : 'unfinished question' , searchQuery : '' } )
351- client . setQueryData ( searchHistoryKeys . list ( 'organization' , 'reader' ) , {
352- sources : [ ] ,
353- queries : [ { query : 'launch' , searchedAt : new Date ( ) . toISOString ( ) } ] ,
354- } )
355- await render ( { organizationPage : true } )
356- const recent = [ ...container . querySelectorAll ( 'button' ) ] . find (
357- ( button ) => button . textContent === 'launch'
358- )
359- expect ( recent ) . not . toBeUndefined ( )
360- await act ( async ( ) => {
361- recent ! . click ( )
362- await vi . advanceTimersByTimeAsync ( 1 )
363- } )
364- expect ( useMothershipDraftsStore . getState ( ) . drafts [ draftKey ] ?. text ) . toBe ( 'unfinished question' )
365- expect ( requests . at ( - 1 ) ?. body . query ) . toBe ( 'launch' )
366- } )
362+ it . each ( [
363+ [ 'the app chrome' , OrganizationSearch ] ,
364+ [ 'the organization route' , OrganizationSearchSurface ] ,
365+ ] ) (
366+ 'keeps an unsent draft when a different recent search is opened in %s' ,
367+ async ( _view , organizationPage ) => {
368+ const draftKey = 'reader:organization:organization:search'
369+ useMothershipDraftsStore
370+ . getState ( )
371+ . setDraft ( draftKey , { text : 'unfinished question' , searchQuery : '' } )
372+ client . setQueryData ( searchHistoryKeys . list ( 'organization' , 'reader' ) , {
373+ sources : [ ] ,
374+ queries : [ { query : 'launch' , searchedAt : new Date ( ) . toISOString ( ) } ] ,
375+ } )
376+ await render ( { organizationPage } )
377+ const recent = [ ...container . querySelectorAll ( 'button' ) ] . find (
378+ ( button ) => button . textContent === 'launch'
379+ )
380+ expect ( recent ) . not . toBeUndefined ( )
381+ await act ( async ( ) => {
382+ recent ! . click ( )
383+ await vi . advanceTimersByTimeAsync ( 1 )
384+ } )
385+ expect ( useMothershipDraftsStore . getState ( ) . drafts [ draftKey ] ?. text ) . toBe ( 'unfinished question' )
386+ expect ( requests . at ( - 1 ) ?. body . query ) . toBe ( 'launch' )
387+ }
388+ )
0 commit comments