44} from "../../../../harness/tsserverLogger" ;
55import {
66 createWatchUtils ,
7+ Watches ,
78 WatchUtils ,
89} from "../../../../harness/watchUtils" ;
910import * as ts from "../../../_namespaces/ts" ;
@@ -60,11 +61,11 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
6061 }
6162
6263 function watchDirectory ( data : ts . server . protocol . CreateDirectoryWatcherEventBody ) {
63- logger . log ( `Custom watchDirectory: ${ data . id } : ${ data . path } ${ data . recursive } ` ) ;
64+ logger . log ( `Custom watchDirectory: ${ data . id } : ${ data . path } ${ data . recursive } ${ data . ignoreUpdate } ` ) ;
6465 ts . Debug . assert ( ! idToClose . has ( data . id ) ) ;
6566 const result = host . factoryData . watchUtils . fsWatch ( data . path , data . recursive , data ) ;
6667 idToClose . set ( data . id , ( ) => {
67- logger . log ( `Custom watchDirectory:: Close:: ${ data . id } : ${ data . path } ${ data . recursive } ` ) ;
68+ logger . log ( `Custom watchDirectory:: Close:: ${ data . id } : ${ data . path } ${ data . recursive } ${ data . ignoreUpdate } ` ) ;
6869 result . close ( ) ;
6970 } ) ;
7071 }
@@ -89,37 +90,132 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
8990 }
9091 }
9192
92- function updateFileOnHost ( session : TestSession , file : string , log : string ) {
93+ function updateFileOnHost ( session : TestSession , file : string , log : string , content ?: string ) {
9394 // Change b.ts
94- session . logger . log ( log ) ;
95- session . host . writeFile ( file , session . host . readFile ( "/user/username/projects/myproject/a.ts" ) ! ) ;
95+ session . logger . log ( `${ log } : ${ file } ` ) ;
96+ if ( content ) session . host . appendFile ( file , content ) ;
97+ else session . host . writeFile ( file , session . host . readFile ( "/user/username/projects/myproject/a.ts" ) ! ) ;
9698 session . host . runQueuedTimeoutCallbacks ( ) ;
9799 }
98100
101+ function collectWatchChanges < T extends ts . server . protocol . CreateFileWatcherEventBody | ts . server . protocol . CreateDirectoryWatcherEventBody > (
102+ session : TestSession ,
103+ watches : Watches < T > ,
104+ path : string ,
105+ eventPath : string ,
106+ eventType : "created" | "deleted" | "updated" ,
107+ ignoreUpdate ?: ( data : T ) => boolean ,
108+ ) {
109+ session . logger . log ( `Custom watch:: ${ path } ${ eventPath } ${ eventType } ` ) ;
110+ let result : ts . server . protocol . WatchChangeRequestArgs [ ] | undefined ;
111+ watches . forEach (
112+ path ,
113+ data => {
114+ if ( ignoreUpdate ?.( data ) ) return ;
115+ switch ( eventType ) {
116+ case "created" :
117+ ( result ??= [ ] ) . push ( { id : data . id , created : [ eventPath ] } ) ;
118+ break ;
119+ case "deleted" :
120+ ( result ??= [ ] ) . push ( { id : data . id , deleted : [ eventPath ] } ) ;
121+ break ;
122+ case "updated" :
123+ ( result ??= [ ] ) . push ( { id : data . id , updated : [ eventPath ] } ) ;
124+ break ;
125+ default :
126+ ts . Debug . assertNever ( eventType ) ;
127+ }
128+ } ,
129+ ) ;
130+ return result ;
131+ }
132+
133+ function collectDirectoryWatcherChanges (
134+ session : TestSession ,
135+ dir : string ,
136+ eventPath : string ,
137+ eventType : "created" | "deleted" | "updated" ,
138+ ) {
139+ return collectWatchChanges (
140+ session ,
141+ ( session . logger . host as TestServerHostWithCustomWatch ) . factoryData . watchUtils . fsWatchesRecursive ,
142+ dir ,
143+ eventPath ,
144+ eventType ,
145+ data => ! ! data . ignoreUpdate && eventType === "updated" ,
146+ ) ;
147+ }
148+
149+ function collectFileWatcherChanges (
150+ session : TestSession ,
151+ file : string ,
152+ eventType : "created" | "deleted" | "updated" ,
153+ ) {
154+ return collectWatchChanges (
155+ session ,
156+ ( session . logger . host as TestServerHostWithCustomWatch ) . factoryData . watchUtils . pollingWatches ,
157+ file ,
158+ file ,
159+ eventType ,
160+ ) ;
161+ }
162+
163+ function invokeWatchChange (
164+ session : TestSession ,
165+ ...args : ( ts . server . protocol . WatchChangeRequestArgs [ ] | undefined ) [ ]
166+ ) {
167+ let result : Map < number , ts . server . protocol . WatchChangeRequestArgs > | undefined ;
168+ args . forEach ( arg =>
169+ arg ?. forEach ( value => {
170+ result ??= new Map ( ) ;
171+ const valueInResult = result . get ( value . id ) ;
172+ if ( ! valueInResult ) result . set ( value . id , value ) ;
173+ else {
174+ valueInResult . created = ts . combine ( valueInResult . created , value . created ) ;
175+ valueInResult . deleted = ts . combine ( valueInResult . deleted , value . deleted ) ;
176+ valueInResult . updated = ts . combine ( valueInResult . updated , value . updated ) ;
177+ }
178+ } )
179+ ) ;
180+ if ( result ) {
181+ session . executeCommandSeq < ts . server . protocol . WatchChangeRequest > ( {
182+ command : ts . server . protocol . CommandTypes . WatchChange ,
183+ arguments : ts . singleOrMany ( ts . arrayFrom ( result . values ( ) ) ) ,
184+ } ) ;
185+ }
186+ }
187+
99188 function addFile ( session : TestSession , path : string ) {
100189 updateFileOnHost ( session , path , "Add file" ) ;
101- session . logger . log ( "Custom watch" ) ;
102- ( session . logger . host as TestServerHostWithCustomWatch ) . factoryData . watchUtils . fsWatchesRecursive . forEach (
103- "/user/username/projects/myproject" ,
104- data =>
105- session . executeCommandSeq < ts . server . protocol . WatchChangeRequest > ( {
106- command : ts . server . protocol . CommandTypes . WatchChange ,
107- arguments : { id : data . id , path, eventType : "create" } ,
108- } ) ,
190+ invokeWatchChange (
191+ session ,
192+ collectDirectoryWatcherChanges ( session , "/user/username/projects/myproject" , path , "created" ) ,
109193 ) ;
110194 session . host . runQueuedTimeoutCallbacks ( ) ;
111195 }
112196
113- function changeFile ( session : TestSession , path : string ) {
114- updateFileOnHost ( session , path , "Change File" ) ;
115- session . logger . log ( "Custom watch" ) ;
116- ( session . logger . host as TestServerHostWithCustomWatch ) . factoryData . watchUtils . pollingWatches . forEach (
117- path ,
118- data =>
119- session . executeCommandSeq < ts . server . protocol . WatchChangeRequest > ( {
120- command : ts . server . protocol . CommandTypes . WatchChange ,
121- arguments : { id : data . id , path, eventType : "update" } ,
122- } ) ,
197+ function changeFile ( session : TestSession , path : string , content ?: string ) {
198+ updateFileOnHost ( session , path , "Change File" , content ) ;
199+ invokeWatchChange (
200+ session ,
201+ collectFileWatcherChanges ( session , path , "updated" ) ,
202+ collectDirectoryWatcherChanges ( session , ts . getDirectoryPath ( path ) , path , "updated" ) ,
203+ ) ;
204+ session . host . runQueuedTimeoutCallbacks ( ) ;
205+ }
206+
207+ function npmInstall ( session : TestSession ) {
208+ session . logger . log ( "update with npm install" ) ;
209+ session . host . appendFile ( "/user/username/projects/myproject/node_modules/something/index.d.ts" , `export const y = 20;` ) ;
210+ session . host . runQueuedTimeoutCallbacks ( ) ;
211+ invokeWatchChange (
212+ session ,
213+ collectDirectoryWatcherChanges (
214+ session ,
215+ "/user/username/projects/myproject/node_modules" ,
216+ "/user/username/projects/myproject/node_modules/something/index.d.ts" ,
217+ "updated" ,
218+ ) ,
123219 ) ;
124220 session . host . runQueuedTimeoutCallbacks ( ) ;
125221 }
@@ -129,6 +225,8 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
129225 "/user/username/projects/myproject/tsconfig.json" : "{}" ,
130226 "/user/username/projects/myproject/a.ts" : `export class a { prop = "hello"; foo() { return this.prop; } }` ,
131227 "/user/username/projects/myproject/b.ts" : `export class b { prop = "hello"; foo() { return this.prop; } }` ,
228+ "/user/username/projects/myproject/m.ts" : `import { x } from "something"` ,
229+ "/user/username/projects/myproject/node_modules/something/index.d.ts" : `export const x = 10;` ,
132230 [ libFile . path ] : libFile . content ,
133231 } ) ;
134232 const logger = createLoggerWithInMemoryLogs ( inputHost ) ;
@@ -153,6 +251,26 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
153251 // Re watch
154252 closeFilesForSession ( [ "/user/username/projects/myproject/b.ts" ] , session ) ;
155253
254+ // Update c.ts
255+ changeFile ( session , "/user/username/projects/myproject/c.ts" , `export const y = 20;` ) ;
256+
257+ // Update with npm install
258+ npmInstall ( session ) ;
259+ host . runQueuedTimeoutCallbacks ( ) ;
260+
261+ // Add and change multiple files - combine and send multiple requests together
262+ updateFileOnHost ( session , "/user/username/projects/myproject/d.ts" , "Add file" ) ;
263+ updateFileOnHost ( session , "/user/username/projects/myproject/c.ts" , "Change File" , `export const z = 30;` ) ;
264+ updateFileOnHost ( session , "/user/username/projects/myproject/e.ts" , "Add File" ) ;
265+ invokeWatchChange (
266+ session ,
267+ collectDirectoryWatcherChanges ( session , "/user/username/projects/myproject" , "/user/username/projects/myproject/d.ts" , "created" ) ,
268+ collectFileWatcherChanges ( session , "/user/username/projects/myproject/c.ts" , "updated" ) ,
269+ collectDirectoryWatcherChanges ( session , "/user/username/projects/myproject" , "/user/username/projects/myproject/c.ts" , "updated" ) ,
270+ collectDirectoryWatcherChanges ( session , "/user/username/projects/myproject" , "/user/username/projects/myproject/e.ts" , "created" ) ,
271+ ) ;
272+ session . host . runQueuedTimeoutCallbacks ( ) ;
273+
156274 baselineTsserverLogs ( "events/watchEvents" , `canUseWatchEvents` , session ) ;
157275 function handleWatchEvents ( event : ts . server . ProjectServiceEvent ) {
158276 switch ( event . eventName ) {
@@ -192,9 +310,15 @@ describe("unittests:: tsserver:: events:: watchEvents", () => {
192310 logger . msg = ( s , type ) => logger . info ( `${ type } :: ${ s } ` ) ;
193311 session . executeCommandSeq < ts . server . protocol . WatchChangeRequest > ( {
194312 command : ts . server . protocol . CommandTypes . WatchChange ,
195- arguments : { id : 1 , path : "/user/username/projects/myproject/b.ts" , eventType : "update" } ,
313+ arguments : { id : 1 , updated : [ "/user/username/projects/myproject/b.ts" ] } ,
196314 } ) ;
197315
316+ // Update c.ts
317+ changeFile ( session , "/user/username/projects/myproject/c.ts" , `export const y = 20;` ) ;
318+
319+ // Update with npm install
320+ npmInstall ( session ) ;
321+
198322 baselineTsserverLogs ( "events/watchEvents" , `canUseWatchEvents without canUseEvents` , session ) ;
199323 } ) ;
200324} ) ;
0 commit comments