• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.tscWatch {
2    import Tsc_WatchDirectory = TestFSWithWatch.Tsc_WatchDirectory;
3    describe("unittests:: tsc-watch:: watchEnvironment:: tsc-watch with different polling/non polling options", () => {
4        const scenario = "watchEnvironment";
5        verifyTscWatch({
6            scenario,
7            subScenario: "watchFile/using dynamic priority polling",
8            commandLineArgs: ["--w", `/a/username/project/typescript.ts`],
9            sys: () => {
10                const projectFolder = "/a/username/project";
11                const file1: File = {
12                    path: `${projectFolder}/typescript.ts`,
13                    content: "var z = 10;"
14                };
15                const environmentVariables = new Map<string, string>();
16                environmentVariables.set("TSC_WATCHFILE", TestFSWithWatch.Tsc_WatchFile.DynamicPolling);
17                return createWatchedSystem([file1, libFile], { environmentVariables });
18            },
19            changes: [
20                {
21                    caption: "Time spent to Transition libFile and file1 to low priority queue",
22                    change: noop,
23                    timeouts: (sys, programs) => {
24                        const initialProgram = programs[0][0];
25                        const mediumPollingIntervalThreshold = unchangedPollThresholds[PollingInterval.Medium];
26                        for (let index = 0; index < mediumPollingIntervalThreshold; index++) {
27                            // Transition libFile and file1 to low priority queue
28                            sys.checkTimeoutQueueLengthAndRun(1);
29                            assert.deepEqual(programs[0][0], initialProgram);
30                        }
31                        return;
32                    },
33                },
34                {
35                    caption: "Make change to file",
36                    // Make a change to file
37                    change: sys => sys.writeFile("/a/username/project/typescript.ts", "var zz30 = 100;"),
38                    // During this timeout the file would be detected as unchanged
39                    timeouts: checkSingleTimeoutQueueLengthAndRun,
40                },
41                {
42                    caption: "Callbacks: medium priority + high priority queue and scheduled program update",
43                    change: noop,
44                    // Callbacks: medium priority + high priority queue and scheduled program update
45                    // This should detect change in the file
46                    timeouts: sys => sys.checkTimeoutQueueLengthAndRun(3),
47                },
48                {
49                    caption: "Polling queues polled and everything is in the high polling queue",
50                    change: noop,
51                    timeouts: (sys, programs) => {
52                        const initialProgram = programs[0][0];
53                        const mediumPollingIntervalThreshold = unchangedPollThresholds[PollingInterval.Medium];
54                        const newThreshold = unchangedPollThresholds[PollingInterval.Low] + mediumPollingIntervalThreshold;
55                        for (let fileUnchangeDetected = 1; fileUnchangeDetected < newThreshold; fileUnchangeDetected++) {
56                            // For high + Medium/low polling interval
57                            sys.checkTimeoutQueueLengthAndRun(2);
58                            assert.deepEqual(programs[0][0], initialProgram);
59                        }
60
61                        // Everything goes in high polling interval queue
62                        sys.checkTimeoutQueueLengthAndRun(1);
63                        return;
64                    },
65                }
66            ]
67        });
68
69        verifyTscWatch({
70            scenario,
71            subScenario: "watchFile/using fixed chunk size polling",
72            commandLineArgs: ["-w", "-p", "/a/b/tsconfig.json"],
73            sys: () => {
74                const configFile: File = {
75                    path: "/a/b/tsconfig.json",
76                    content: JSON.stringify({
77                        watchOptions: {
78                            watchFile: "FixedChunkSizePolling"
79                        }
80                    })
81                };
82                const files = [libFile, commonFile1, commonFile2, configFile];
83                return createWatchedSystem(files);
84            },
85            changes: [
86                {
87                    caption: "The timeout is to check the status of all files",
88                    change: noop,
89                    timeouts: (sys, programs) => {
90                        // On each timeout file does not change
91                        const initialProgram = programs[0][0];
92                        for (let index = 0; index < 4; index++) {
93                            sys.checkTimeoutQueueLengthAndRun(1);
94                            assert.deepEqual(programs[0][0], initialProgram);
95                        }
96                    },
97                },
98                {
99                    caption: "Make change to file but should detect as changed and schedule program update",
100                    // Make a change to file
101                    change: sys => sys.writeFile(commonFile1.path, "var zz30 = 100;"),
102                    timeouts: checkSingleTimeoutQueueLengthAndRun,
103                },
104                {
105                    caption: "Callbacks: queue and scheduled program update",
106                    change: noop,
107                    // Callbacks: scheduled program update and queue for the polling
108                    timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
109                },
110                {
111                    caption: "The timeout is to check the status of all files",
112                    change: noop,
113                    timeouts: (sys, programs) => {
114                        // On each timeout file does not change
115                        const initialProgram = programs[0][0];
116                        sys.checkTimeoutQueueLengthAndRun(1);
117                        assert.deepEqual(programs[0][0], initialProgram);
118                    },
119                },
120            ]
121        });
122
123        describe("tsc-watch when watchDirectories implementation", () => {
124            function verifyRenamingFileInSubFolder(subScenario: string, tscWatchDirectory: Tsc_WatchDirectory) {
125                const projectFolder = "/a/username/project";
126                const projectSrcFolder = `${projectFolder}/src`;
127                const configFile: File = {
128                    path: `${projectFolder}/tsconfig.json`,
129                    content: JSON.stringify({
130                        watchOptions: {
131                            synchronousWatchDirectory: true
132                        }
133                    })
134                };
135                const file: File = {
136                    path: `${projectSrcFolder}/file1.ts`,
137                    content: ""
138                };
139                verifyTscWatch({
140                    scenario,
141                    subScenario: `watchDirectories/${subScenario}`,
142                    commandLineArgs: ["--w", "-p", configFile.path],
143                    sys: () => {
144                        const files = [file, configFile, libFile];
145                        const environmentVariables = new Map<string, string>();
146                        environmentVariables.set("TSC_WATCHDIRECTORY", tscWatchDirectory);
147                        return createWatchedSystem(files, { environmentVariables });
148                    },
149                    changes: [
150                        {
151                            caption: "Rename file1 to file2",
152                            // Rename the file:
153                            change: sys => sys.renameFile(file.path, file.path.replace("file1.ts", "file2.ts")),
154                            timeouts: sys => {
155                                if (tscWatchDirectory === Tsc_WatchDirectory.DynamicPolling) {
156                                    // With dynamic polling the fs change would be detected only by running timeouts
157                                    sys.runQueuedTimeoutCallbacks();
158                                }
159                                // Delayed update program
160                                sys.runQueuedTimeoutCallbacks();
161                                return;
162                            },
163                        },
164                    ],
165                });
166            }
167
168            verifyRenamingFileInSubFolder("uses watchFile when renaming file in subfolder", Tsc_WatchDirectory.WatchFile);
169
170            verifyRenamingFileInSubFolder("uses non recursive watchDirectory when renaming file in subfolder", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
171
172            verifyRenamingFileInSubFolder("uses non recursive dynamic polling when renaming file in subfolder", Tsc_WatchDirectory.DynamicPolling);
173
174            verifyTscWatch({
175                scenario,
176                subScenario: "watchDirectories/when there are symlinks to folders in recursive folders",
177                commandLineArgs: ["--w"],
178                sys: () => {
179                    const cwd = "/home/user/projects/myproject";
180                    const file1: File = {
181                        path: `${cwd}/src/file.ts`,
182                        content: `import * as a from "a"`
183                    };
184                    const tsconfig: File = {
185                        path: `${cwd}/tsconfig.json`,
186                        content: `{ "compilerOptions": { "extendedDiagnostics": true, "traceResolution": true }}`
187                    };
188                    const realA: File = {
189                        path: `${cwd}/node_modules/reala/index.d.ts`,
190                        content: `export {}`
191                    };
192                    const realB: File = {
193                        path: `${cwd}/node_modules/realb/index.d.ts`,
194                        content: `export {}`
195                    };
196                    const symLinkA: SymLink = {
197                        path: `${cwd}/node_modules/a`,
198                        symLink: `${cwd}/node_modules/reala`
199                    };
200                    const symLinkB: SymLink = {
201                        path: `${cwd}/node_modules/b`,
202                        symLink: `${cwd}/node_modules/realb`
203                    };
204                    const symLinkBInA: SymLink = {
205                        path: `${cwd}/node_modules/reala/node_modules/b`,
206                        symLink: `${cwd}/node_modules/b`
207                    };
208                    const symLinkAInB: SymLink = {
209                        path: `${cwd}/node_modules/realb/node_modules/a`,
210                        symLink: `${cwd}/node_modules/a`
211                    };
212                    const files = [libFile, file1, tsconfig, realA, realB, symLinkA, symLinkB, symLinkBInA, symLinkAInB];
213                    const environmentVariables = new Map<string, string>();
214                    environmentVariables.set("TSC_WATCHDIRECTORY", Tsc_WatchDirectory.NonRecursiveWatchDirectory);
215                    return createWatchedSystem(files, { environmentVariables, currentDirectory: cwd });
216                },
217                changes: emptyArray
218            });
219
220            verifyTscWatch({
221                scenario,
222                subScenario: "watchDirectories/with non synchronous watch directory",
223                commandLineArgs: ["--w", "-p", `${projectRoot}/tsconfig.json`],
224                sys: () => {
225                    const configFile: File = {
226                        path: `${projectRoot}/tsconfig.json`,
227                        content: "{}"
228                    };
229                    const file1: File = {
230                        path: `${projectRoot}/src/file1.ts`,
231                        content: `import { x } from "file2";`
232                    };
233                    const file2: File = {
234                        path: `${projectRoot}/node_modules/file2/index.d.ts`,
235                        content: `export const x = 10;`
236                    };
237                    const files = [libFile, file1, file2, configFile];
238                    return createWatchedSystem(files, { runWithoutRecursiveWatches: true });
239                },
240                changes: [
241                    {
242                        caption: "Directory watch updates because of file1.js creation",
243                        change: noop,
244                        timeouts: sys => {
245                            sys.checkTimeoutQueueLengthAndRun(1); // To update directory callbacks for file1.js output
246                            sys.checkTimeoutQueueLength(0);
247                        },
248                    },
249                    {
250                        caption: "Remove directory node_modules",
251                        // Remove directory node_modules
252                        change: sys => sys.deleteFolder(`${projectRoot}/node_modules`, /*recursive*/ true),
253                        timeouts: sys => {
254                            sys.checkTimeoutQueueLength(3); // 1. Failed lookup invalidation 2. For updating program and 3. for updating child watches
255                            sys.runQueuedTimeoutCallbacks(sys.getNextTimeoutId() - 2); // Update program
256                        },
257                    },
258                    {
259                        caption: "Pending directory watchers and program update",
260                        change: noop,
261                        timeouts: sys => {
262                            sys.checkTimeoutQueueLengthAndRun(1); // To update directory watchers
263                            sys.checkTimeoutQueueLengthAndRun(2); // To Update program and failed lookup update
264                            sys.checkTimeoutQueueLengthAndRun(1); // Actual program update
265                            sys.checkTimeoutQueueLength(0);
266                        },
267                    },
268                    {
269                        caption: "Start npm install",
270                        // npm install
271                        change: sys => sys.createDirectory(`${projectRoot}/node_modules`),
272                        timeouts: sys => sys.checkTimeoutQueueLength(1), // To update folder structure
273                    },
274                    {
275                        caption: "npm install folder creation of file2",
276                        change: sys => sys.createDirectory(`${projectRoot}/node_modules/file2`),
277                        timeouts: sys => sys.checkTimeoutQueueLength(1), // To update folder structure
278                    },
279                    {
280                        caption: "npm install index file in file2",
281                        change: sys => sys.writeFile(`${projectRoot}/node_modules/file2/index.d.ts`, `export const x = 10;`),
282                        timeouts: sys => sys.checkTimeoutQueueLength(1), // To update folder structure
283                    },
284                    {
285                        caption: "Updates the program",
286                        change: noop,
287                        timeouts: sys => {
288                            sys.runQueuedTimeoutCallbacks();
289                            sys.checkTimeoutQueueLength(2); // To Update program and failed lookup update
290                        },
291                    },
292                    {
293                        caption: "Invalidates module resolution cache",
294                        change: noop,
295                        timeouts: sys => {
296                            sys.runQueuedTimeoutCallbacks();
297                            sys.checkTimeoutQueueLength(1); // To Update program
298                        },
299                    },
300                    {
301                        caption: "Pending updates",
302                        change: noop,
303                        timeouts: sys => {
304                            sys.runQueuedTimeoutCallbacks();
305                            sys.checkTimeoutQueueLength(0);
306                        },
307                    },
308                ],
309            });
310
311            verifyTscWatch({
312                scenario,
313                subScenario: "watchDirectories/with non synchronous watch directory with outDir and declaration enabled",
314                commandLineArgs: ["--w", "-p", `${projectRoot}/tsconfig.json`],
315                sys: () => {
316                    const configFile: File = {
317                        path: `${projectRoot}/tsconfig.json`,
318                        content: JSON.stringify({ compilerOptions: { outDir: "dist", declaration: true } })
319                    };
320                    const file1: File = {
321                        path: `${projectRoot}/src/file1.ts`,
322                        content: `import { x } from "file2";`
323                    };
324                    const file2: File = {
325                        path: `${projectRoot}/node_modules/file2/index.d.ts`,
326                        content: `export const x = 10;`
327                    };
328                    const files = [libFile, file1, file2, configFile];
329                    return createWatchedSystem(files, { runWithoutRecursiveWatches: true });
330                },
331                changes: [
332                    noopChange,
333                    {
334                        caption: "Add new file, should schedule and run timeout to update directory watcher",
335                        change: sys => sys.writeFile(`${projectRoot}/src/file3.ts`, `export const y = 10;`),
336                        timeouts: checkSingleTimeoutQueueLengthAndRun, // Update the child watch
337                    },
338                    {
339                        caption: "Actual program update to include new file",
340                        change: noop,
341                        timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2), // Scheduling failed lookup update and program update
342                    },
343                    {
344                        caption: "After program emit with new file, should schedule and run timeout to update directory watcher",
345                        change: noop,
346                        timeouts: checkSingleTimeoutQueueLengthAndRun, // Update the child watch
347                    },
348                    noopChange,
349                ],
350            });
351
352            verifyTscWatch({
353                scenario,
354                subScenario: "watchDirectories/with non synchronous watch directory renaming a file",
355                commandLineArgs: ["--w", "-p", `${projectRoot}/tsconfig.json`],
356                sys: () => {
357                    const configFile: File = {
358                        path: `${projectRoot}/tsconfig.json`,
359                        content: JSON.stringify({ compilerOptions: { outDir: "dist" } })
360                    };
361                    const file1: File = {
362                        path: `${projectRoot}/src/file1.ts`,
363                        content: `import { x } from "./file2";`
364                    };
365                    const file2: File = {
366                        path: `${projectRoot}/src/file2.ts`,
367                        content: `export const x = 10;`
368                    };
369                    const files = [libFile, file1, file2, configFile];
370                    return createWatchedSystem(files, { runWithoutRecursiveWatches: true });
371                },
372                changes: [
373                    noopChange,
374                    {
375                        caption: "rename the file",
376                        change: sys => sys.renameFile(`${projectRoot}/src/file2.ts`, `${projectRoot}/src/renamed.ts`),
377                        timeouts: sys => {
378                            sys.checkTimeoutQueueLength(2); // 1. For updating program and 2. for updating child watches
379                            sys.runQueuedTimeoutCallbacks(1); // Update program
380                        },
381                    },
382                    {
383                        caption: "Pending directory watchers and program update",
384                        change: noop,
385                        timeouts: sys => {
386                            sys.checkTimeoutQueueLengthAndRun(1); // To update directory watchers
387                            sys.checkTimeoutQueueLengthAndRun(2); // To Update program and failed lookup update
388                            sys.checkTimeoutQueueLengthAndRun(1); // Actual program update
389                            sys.checkTimeoutQueueLength(0);
390                        },
391                    },
392                ],
393            });
394        });
395
396        describe("handles watch compiler options", () => {
397            verifyTscWatch({
398                scenario,
399                subScenario: "watchOptions/with watchFile option",
400                commandLineArgs: ["-w", "-p", "/a/b/tsconfig.json"],
401                sys: () => {
402                    const configFile: File = {
403                        path: "/a/b/tsconfig.json",
404                        content: JSON.stringify({
405                            watchOptions: {
406                                watchFile: "UseFsEvents"
407                            }
408                        })
409                    };
410                    const files = [libFile, commonFile1, commonFile2, configFile];
411                    return createWatchedSystem(files);
412                },
413                changes: emptyArray
414            });
415
416            verifyTscWatch({
417                scenario,
418                subScenario: "watchOptions/with watchDirectory option",
419                commandLineArgs: ["-w", "-p", "/a/b/tsconfig.json"],
420                sys: () => {
421                    const configFile: File = {
422                        path: "/a/b/tsconfig.json",
423                        content: JSON.stringify({
424                            watchOptions: {
425                                watchDirectory: "UseFsEvents"
426                            }
427                        })
428                    };
429                    const files = [libFile, commonFile1, commonFile2, configFile];
430                    return createWatchedSystem(files, { runWithoutRecursiveWatches: true });
431                },
432                changes: emptyArray
433            });
434
435            verifyTscWatch({
436                scenario,
437                subScenario: "watchOptions/with fallbackPolling option",
438                commandLineArgs: ["-w", "-p", "/a/b/tsconfig.json"],
439                sys: () => {
440                    const configFile: File = {
441                        path: "/a/b/tsconfig.json",
442                        content: JSON.stringify({
443                            watchOptions: {
444                                fallbackPolling: "PriorityInterval"
445                            }
446                        })
447                    };
448                    const files = [libFile, commonFile1, commonFile2, configFile];
449                    return createWatchedSystem(files, { runWithoutRecursiveWatches: true, runWithFallbackPolling: true });
450                },
451                changes: emptyArray
452            });
453
454            verifyTscWatch({
455                scenario,
456                subScenario: "watchOptions/with watchFile as watch options to extend",
457                commandLineArgs: ["-w", "-p", "/a/b/tsconfig.json", "--watchFile", "UseFsEvents"],
458                sys: () => {
459                    const configFile: File = {
460                        path: "/a/b/tsconfig.json",
461                        content: "{}"
462                    };
463                    const files = [libFile, commonFile1, commonFile2, configFile];
464                    return createWatchedSystem(files);
465                },
466                changes: emptyArray
467            });
468
469            describe("exclude options", () => {
470                function sys(watchOptions: WatchOptions, runWithoutRecursiveWatches?: boolean): WatchedSystem {
471                    const configFile: File = {
472                        path: `${projectRoot}/tsconfig.json`,
473                        content: JSON.stringify({ exclude: ["node_modules"], watchOptions })
474                    };
475                    const main: File = {
476                        path: `${projectRoot}/src/main.ts`,
477                        content: `import { foo } from "bar"; foo();`
478                    };
479                    const bar: File = {
480                        path: `${projectRoot}/node_modules/bar/index.d.ts`,
481                        content: `export { foo } from "./foo";`
482                    };
483                    const foo: File = {
484                        path: `${projectRoot}/node_modules/bar/foo.d.ts`,
485                        content: `export function foo(): string;`
486                    };
487                    const fooBar: File = {
488                        path: `${projectRoot}/node_modules/bar/fooBar.d.ts`,
489                        content: `export function fooBar(): string;`
490                    };
491                    const temp: File = {
492                        path: `${projectRoot}/node_modules/bar/temp/index.d.ts`,
493                        content: "export function temp(): string;"
494                    };
495                    const files = [libFile, main, bar, foo, fooBar, temp, configFile];
496                    return createWatchedSystem(files, { currentDirectory: projectRoot, runWithoutRecursiveWatches });
497                }
498
499                function verifyWorker(...additionalFlags: string[]) {
500                    verifyTscWatch({
501                        scenario,
502                        subScenario: `watchOptions/with excludeFiles option${additionalFlags.join("")}`,
503                        commandLineArgs: ["-w", ...additionalFlags],
504                        sys: () => sys({ excludeFiles: ["node_modules/*"] }),
505                        changes: [
506                            {
507                                caption: "Change foo",
508                                change: sys => replaceFileText(sys, `${projectRoot}/node_modules/bar/foo.d.ts`, "foo", "fooBar"),
509                                timeouts: sys => sys.checkTimeoutQueueLength(0),
510                            }
511                        ]
512                    });
513
514                    verifyTscWatch({
515                        scenario,
516                        subScenario: `watchOptions/with excludeDirectories option${additionalFlags.join("")}`,
517                        commandLineArgs: ["-w", ...additionalFlags],
518                        sys: () => sys({ excludeDirectories: ["node_modules"] }),
519                        changes: [
520                            {
521                                caption: "delete fooBar",
522                                change: sys => sys.deleteFile(`${projectRoot}/node_modules/bar/fooBar.d.ts`),
523                                timeouts: sys => sys.checkTimeoutQueueLength(0),                            }
524                        ]
525                    });
526
527                    verifyTscWatch({
528                        scenario,
529                        subScenario: `watchOptions/with excludeDirectories option with recursive directory watching${additionalFlags.join("")}`,
530                        commandLineArgs: ["-w", ...additionalFlags],
531                        sys: () => sys({ excludeDirectories: ["**/temp"] }, /*runWithoutRecursiveWatches*/ true),
532                        changes: [
533                            {
534                                caption: "Directory watch updates because of main.js creation",
535                                change: noop,
536                                timeouts: sys => {
537                                    sys.checkTimeoutQueueLengthAndRun(1); // To update directory callbacks for main.js output
538                                    sys.checkTimeoutQueueLength(0);
539                                },
540                            },
541                            {
542                                caption: "add new folder to temp",
543                                change: sys => sys.ensureFileOrFolder({ path: `${projectRoot}/node_modules/bar/temp/fooBar/index.d.ts`, content: "export function temp(): string;" }),
544                                timeouts: sys => sys.checkTimeoutQueueLength(0),
545                            }
546                        ]
547                    });
548                }
549
550                verifyWorker();
551                verifyWorker("-extendedDiagnostics");
552            });
553        });
554
555        verifyTscWatch({
556            scenario,
557            subScenario: `fsWatch/when using file watching thats when rename occurs when file is still on the disk`,
558            commandLineArgs: ["-w", "--extendedDiagnostics"],
559            sys: () => createWatchedSystem(
560                {
561                    [libFile.path]: libFile.content,
562                    [`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
563                    [`${projectRoot}/foo.ts`]: `export declare function foo(): string;`,
564                    [`${projectRoot}/tsconfig.json`]: JSON.stringify({
565                        watchOptions: { watchFile: "useFsEvents" },
566                        files: ["foo.ts", "main.ts"]
567                    }),
568                },
569                { currentDirectory: projectRoot, }
570            ),
571            changes: [
572                {
573                    caption: "Introduce error such that when callback happens file is already appeared",
574                    // vm's wq generates this kind of event
575                    // Skip delete event so inode changes but when the create's rename occurs file is on disk
576                    change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo2(): string;`, {
577                        invokeFileDeleteCreateAsPartInsteadOfChange: true,
578                        ignoreDelete: true,
579                    }),
580                    timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
581                },
582                {
583                    caption: "Replace file with rename event that fixes error",
584                    change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, }),
585                    timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
586                },
587            ]
588        });
589
590        describe("with fsWatch on inodes", () => {
591            verifyTscWatch({
592                scenario,
593                subScenario: `fsWatch/when using file watching thats on inode`,
594                commandLineArgs: ["-w", "--extendedDiagnostics"],
595                sys: () => createWatchedSystem(
596                    {
597                        [libFile.path]: libFile.content,
598                        [`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
599                        [`${projectRoot}/foo.d.ts`]: `export function foo(): string;`,
600                        [`${projectRoot}/tsconfig.json`]: JSON.stringify({ watchOptions: { watchFile: "useFsEvents" }, files: ["foo.d.ts", "main.ts"] }),
601                    },
602                    {
603                        currentDirectory: projectRoot,
604                        inodeWatching: true
605                    }
606                ),
607                changes: [
608                    {
609                        caption: "Replace file with rename event that introduces error",
610                        change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo2(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true }),
611                        timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
612                    },
613                    {
614                        caption: "Replace file with rename event that fixes error",
615                        change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true }),
616                        timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
617                    },
618                ]
619            });
620
621            verifyTscWatch({
622                scenario,
623                subScenario: `fsWatch/when using file watching thats on inode when rename event ends with tilde`,
624                commandLineArgs: ["-w", "--extendedDiagnostics"],
625                sys: () => createWatchedSystem(
626                    {
627                        [libFile.path]: libFile.content,
628                        [`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
629                        [`${projectRoot}/foo.d.ts`]: `export function foo(): string;`,
630                        [`${projectRoot}/tsconfig.json`]: JSON.stringify({ watchOptions: { watchFile: "useFsEvents" }, files: ["foo.d.ts", "main.ts"] }),
631                    },
632                    {
633                        currentDirectory: projectRoot,
634                        inodeWatching: true
635                    }
636                ),
637                changes: [
638                    {
639                        caption: "Replace file with rename event that introduces error",
640                        change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo2(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, useTildeAsSuffixInRenameEventFileName: true }),
641                        timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
642                    },
643                    {
644                        caption: "Replace file with rename event that fixes error",
645                        change: sys => sys.modifyFile(`${projectRoot}/foo.d.ts`, `export function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, useTildeAsSuffixInRenameEventFileName: true }),
646                        timeouts: sys => sys.checkTimeoutQueueLengthAndRun(2),
647                    },
648                ]
649            });
650
651            verifyTscWatch({
652                scenario,
653                subScenario: `fsWatch/when using file watching thats on inode when rename occurs when file is still on the disk`,
654                commandLineArgs: ["-w", "--extendedDiagnostics"],
655                sys: () => createWatchedSystem(
656                    {
657                        [libFile.path]: libFile.content,
658                        [`${projectRoot}/main.ts`]: `import { foo } from "./foo"; foo();`,
659                        [`${projectRoot}/foo.ts`]: `export declare function foo(): string;`,
660                        [`${projectRoot}/tsconfig.json`]: JSON.stringify({
661                            watchOptions: { watchFile: "useFsEvents" },
662                            files: ["foo.ts", "main.ts"]
663                        }),
664                    },
665                    {
666                        currentDirectory: projectRoot,
667                        inodeWatching: true,
668                    }
669                ),
670                changes: [
671                    {
672                        caption: "Introduce error such that when callback happens file is already appeared",
673                        // vm's wq generates this kind of event
674                        // Skip delete event so inode changes but when the create's rename occurs file is on disk
675                        change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo2(): string;`, {
676                            invokeFileDeleteCreateAsPartInsteadOfChange: true,
677                            ignoreDelete: true,
678                            skipInodeCheckOnCreate: true
679                        }),
680                        timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
681                    },
682                    {
683                        caption: "Replace file with rename event that fixes error",
684                        change: sys => sys.modifyFile(`${projectRoot}/foo.ts`, `export declare function foo(): string;`, { invokeFileDeleteCreateAsPartInsteadOfChange: true, }),
685                        timeouts: sys => sys.checkTimeoutQueueLengthAndRun(1),
686                    },
687                ]
688            });
689        });
690
691        verifyTscWatch({
692            scenario,
693            subScenario: "fsEvent for change is repeated",
694            commandLineArgs: ["-w", "main.ts", "--extendedDiagnostics"],
695            sys: () => createWatchedSystem({
696                "/user/username/projects/project/main.ts": `let a: string = "Hello"`,
697                [libFile.path]: libFile.content,
698            }, { currentDirectory: "/user/username/projects/project" }),
699            changes: [
700                {
701                    caption: "change main.ts",
702                    change: sys => replaceFileText(sys, "/user/username/projects/project/main.ts", "Hello", "Hello World"),
703                    timeouts: sys => sys.runQueuedTimeoutCallbacks(),
704                },
705                {
706                    caption: "receive another change event without modifying the file",
707                    change: sys => sys.invokeFsWatches("/user/username/projects/project/main.ts", "change", /*modifiedTime*/ undefined, /*useTildeSuffix*/ undefined),
708                    timeouts: sys => sys.runQueuedTimeoutCallbacks(),
709                },
710                {
711                    caption: "change main.ts to empty text",
712                    change: sys => sys.writeFile("/user/username/projects/project/main.ts", ""),
713                    timeouts: sys => sys.runQueuedTimeoutCallbacks(),
714                },
715                {
716                    caption: "receive another change event without modifying the file",
717                    change: sys => sys.invokeFsWatches("/user/username/projects/project/main.ts", "change", /*modifiedTime*/ undefined, /*useTildeSuffix*/ undefined),
718                    timeouts: sys => sys.runQueuedTimeoutCallbacks(),
719                }
720            ]
721        });
722    });
723}
724