@@ -7,6 +7,8 @@ import { ProjectProvider, useProject } from "../../../src/cli/cmd/tui/context/pr
77import { SDKProvider } from "../../../src/cli/cmd/tui/context/sdk"
88import { useEvent } from "../../../src/cli/cmd/tui/context/event"
99
10+ const projectID = "proj_test"
11+
1012async function wait ( fn : ( ) => boolean , timeout = 2000 ) {
1113 const start = Date . now ( )
1214 while ( ! fn ( ) ) {
@@ -15,9 +17,10 @@ async function wait(fn: () => boolean, timeout = 2000) {
1517 }
1618}
1719
18- function event ( payload : Event , input : { directory : string ; workspace ?: string } ) : GlobalEvent {
20+ function event ( payload : Event , input : { directory : string ; project ?: string ; workspace ?: string } ) : GlobalEvent {
1921 return {
2022 directory : input . directory ,
23+ project : input . project ,
2124 workspace : input . workspace ,
2225 payload,
2326 }
@@ -65,37 +68,56 @@ function createSource() {
6568async function mount ( ) {
6669 const source = createSource ( )
6770 const seen : Event [ ] = [ ]
71+ const workspaces : Array < string | undefined > = [ ]
72+ const fetch = ( async ( input : RequestInfo | URL ) => {
73+ const url = new URL ( input instanceof Request ? input . url : String ( input ) )
74+ if ( url . pathname === "/path" ) return Response . json ( { home : "" , state : "" , config : "" , directory : "/tmp/root" } )
75+ if ( url . pathname === "/project/current" ) return Response . json ( { id : projectID } )
76+ throw new Error ( `unexpected request: ${ url . pathname } ` )
77+ } ) as typeof globalThis . fetch
6878 let project ! : ReturnType < typeof useProject >
6979 let done ! : ( ) => void
7080 const ready = new Promise < void > ( ( resolve ) => {
7181 done = resolve
7282 } )
7383
7484 const app = await testRender ( ( ) => (
75- < SDKProvider url = "http://test" directory = "/tmp/root" events = { source . source } >
85+ < SDKProvider
86+ url = "http://test"
87+ directory = "/tmp/root"
88+ events = { source . source }
89+ fetch = { fetch }
90+ >
7691 < ProjectProvider >
7792 < Probe
78- onReady = { ( ctx ) => {
93+ onReady = { async ( ctx ) => {
7994 project = ctx . project
95+ await project . sync ( )
8096 done ( )
8197 } }
8298 seen = { seen }
99+ workspaces = { workspaces }
83100 />
84101 </ ProjectProvider >
85102 </ SDKProvider >
86103 ) )
87104
88105 await ready
89- return { app, emit : source . emit , project, seen }
106+ return { app, emit : source . emit , project, seen, workspaces }
90107}
91108
92- function Probe ( props : { seen : Event [ ] ; onReady : ( ctx : { project : ReturnType < typeof useProject > } ) => void } ) {
109+ function Probe ( props : {
110+ seen : Event [ ]
111+ workspaces : Array < string | undefined >
112+ onReady : ( ctx : { project : ReturnType < typeof useProject > } ) => void
113+ } ) {
93114 const project = useProject ( )
94115 const event = useEvent ( )
95116
96117 onMount ( ( ) => {
97- event . subscribe ( ( evt ) => {
118+ event . subscribe ( ( evt , { workspace } ) => {
98119 props . seen . push ( evt )
120+ props . workspaces . push ( workspace )
99121 } )
100122 props . onReady ( { project } )
101123 } )
@@ -104,25 +126,26 @@ function Probe(props: { seen: Event[]; onReady: (ctx: { project: ReturnType<type
104126}
105127
106128describe ( "useEvent" , ( ) => {
107- test ( "delivers matching directory events without an active workspace " , async ( ) => {
108- const { app, emit, seen } = await mount ( )
129+ test ( "delivers events for the current project " , async ( ) => {
130+ const { app, emit, seen, workspaces } = await mount ( )
109131
110132 try {
111- emit ( event ( vcs ( "main" ) , { directory : "/tmp/root " } ) )
133+ emit ( event ( vcs ( "main" ) , { directory : "/tmp/other" , project : projectID , workspace : "ws_a " } ) )
112134
113135 await wait ( ( ) => seen . length === 1 )
114136
115137 expect ( seen ) . toEqual ( [ vcs ( "main" ) ] )
138+ expect ( workspaces ) . toEqual ( [ "ws_a" ] )
116139 } finally {
117140 app . renderer . destroy ( )
118141 }
119142 } )
120143
121- test ( "ignores non-matching directory events without an active workspace " , async ( ) => {
144+ test ( "ignores events for other projects " , async ( ) => {
122145 const { app, emit, seen } = await mount ( )
123146
124147 try {
125- emit ( event ( vcs ( "other" ) , { directory : "/tmp/other " } ) )
148+ emit ( event ( vcs ( "other" ) , { directory : "/tmp/root" , project : "proj_other " } ) )
126149 await Bun . sleep ( 30 )
127150
128151 expect ( seen ) . toHaveLength ( 0 )
@@ -131,12 +154,12 @@ describe("useEvent", () => {
131154 }
132155 } )
133156
134- test ( "delivers matching workspace events when a workspace is active" , async ( ) => {
157+ test ( "delivers current project events regardless of active workspace " , async ( ) => {
135158 const { app, emit, project, seen } = await mount ( )
136159
137160 try {
138161 project . workspace . set ( "ws_a" )
139- emit ( event ( vcs ( "ws" ) , { directory : "/tmp/other" , workspace : "ws_a " } ) )
162+ emit ( event ( vcs ( "ws" ) , { directory : "/tmp/other" , project : projectID , workspace : "ws_b " } ) )
140163
141164 await wait ( ( ) => seen . length === 1 )
142165
@@ -146,20 +169,6 @@ describe("useEvent", () => {
146169 }
147170 } )
148171
149- test ( "ignores non-matching workspace events when a workspace is active" , async ( ) => {
150- const { app, emit, project, seen } = await mount ( )
151-
152- try {
153- project . workspace . set ( "ws_a" )
154- emit ( event ( vcs ( "ws" ) , { directory : "/tmp/root" , workspace : "ws_b" } ) )
155- await Bun . sleep ( 30 )
156-
157- expect ( seen ) . toHaveLength ( 0 )
158- } finally {
159- app . renderer . destroy ( )
160- }
161- } )
162-
163172 test ( "delivers truly global events even when a workspace is active" , async ( ) => {
164173 const { app, emit, project, seen } = await mount ( )
165174
0 commit comments