• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.tscWatch {
2    import projectsLocation = TestFSWithWatch.tsbuildProjectsLocation;
3    describe("unittests:: tsbuildWatch:: watchMode:: program updates", () => {
4        const enum SubProject {
5            core = "core",
6            logic = "logic",
7            tests = "tests",
8            ui = "ui"
9        }
10        type ReadonlyFile = Readonly<File>;
11        /** [tsconfig, index] | [tsconfig, index, anotherModule, someDecl] */
12        type SubProjectFiles = [tsconfig: ReadonlyFile, index: ReadonlyFile] | [tsconfig: ReadonlyFile, index: ReadonlyFile, anotherModule: ReadonlyFile, someDecl: ReadonlyFile];
13        function projectFilePath(subProject: SubProject, baseFileName: string) {
14            return `${TestFSWithWatch.getTsBuildProjectFilePath("sample1", subProject)}/${baseFileName.toLowerCase()}`;
15        }
16
17        function projectFile(subProject: SubProject, baseFileName: string): File {
18            return TestFSWithWatch.getTsBuildProjectFile("sample1", `${subProject}/${baseFileName}`);
19        }
20
21        function subProjectFiles(subProject: SubProject, anotherModuleAndSomeDecl?: true): SubProjectFiles {
22            const tsconfig = projectFile(subProject, "tsconfig.json");
23            const index = projectFile(subProject, "index.ts");
24            if (!anotherModuleAndSomeDecl) {
25                return [tsconfig, index];
26            }
27            const anotherModule = projectFile(SubProject.core, "anotherModule.ts");
28            const someDecl = projectFile(SubProject.core, "some_decl.ts");
29            return [tsconfig, index, anotherModule, someDecl];
30        }
31
32        function changeFile(fileName: string | (() => string), content: string | (() => string), caption: string): TscWatchCompileChange {
33            return {
34                caption,
35                change: sys => sys.writeFile(isString(fileName) ? fileName : fileName(), isString(content) ? content : content()),
36                timeouts: checkSingleTimeoutQueueLengthAndRun, // Builds core
37            };
38        }
39
40        function changeCore(content: () => string, caption: string) {
41            return changeFile(() => core[1].path, content, caption);
42        }
43
44        let core: SubProjectFiles;
45        let logic: SubProjectFiles;
46        let tests: SubProjectFiles;
47        let ui: SubProjectFiles;
48        let allFiles: readonly File[];
49
50        before(() => {
51            core = subProjectFiles(SubProject.core, /*anotherModuleAndSomeDecl*/ true);
52            logic = subProjectFiles(SubProject.logic);
53            tests = subProjectFiles(SubProject.tests);
54            ui = subProjectFiles(SubProject.ui);
55            allFiles = [libFile, ...core, ...logic, ...tests, ...ui];
56        });
57
58        after(() => {
59            core = undefined!;
60            logic = undefined!;
61            tests = undefined!;
62            ui = undefined!;
63            allFiles = undefined!;
64        });
65
66        verifyTscWatch({
67            scenario: "programUpdates",
68            subScenario: "creates solution in watch mode",
69            commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`],
70            sys: () => createWatchedSystem(allFiles, { currentDirectory: projectsLocation }),
71            changes: emptyArray
72        });
73
74        it("verify building references watches only those projects", () => {
75            const { sys, baseline, oldSnap, cb, getPrograms } = createBaseline(createWatchedSystem(allFiles, { currentDirectory: projectsLocation }));
76            const host = createSolutionBuilderWithWatchHostForBaseline(sys, cb);
77            const solutionBuilder = createSolutionBuilderWithWatch(host, [`sample1/${SubProject.tests}`], { watch: true });
78            solutionBuilder.buildReferences(`sample1/${SubProject.tests}`);
79            runWatchBaseline({
80                scenario: "programUpdates",
81                subScenario: "verify building references watches only those projects",
82                commandLineArgs: ["--b", "--w"],
83                sys,
84                baseline,
85                oldSnap,
86                getPrograms,
87                changes: emptyArray,
88                watchOrSolution: solutionBuilder
89            });
90        });
91
92        describe("validates the changes and watched files", () => {
93            const newFileWithoutExtension = "newFile";
94            const newFile: File = {
95                path: projectFilePath(SubProject.core, `${newFileWithoutExtension}.ts`),
96                content: `export const newFileConst = 30;`
97            };
98
99            function verifyProjectChanges(subScenario: string, allFilesGetter: () => readonly File[]) {
100                const buildLogicAndTests: TscWatchCompileChange = {
101                    caption: "Build logic and tests",
102                    change: noop,
103                    timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout,
104                };
105
106                verifyTscWatch({
107                    scenario: "programUpdates",
108                    subScenario: `${subScenario}/change builds changes and reports found errors message`,
109                    commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`],
110                    sys: () => createWatchedSystem(
111                        allFilesGetter(),
112                        { currentDirectory: projectsLocation }
113                    ),
114                    changes: [
115                        changeCore(() => `${core[1].content}
116export class someClass { }`, "Make change to core"),
117                        buildLogicAndTests,
118                        // Another change requeues and builds it
119                        changeCore(() => core[1].content, "Revert core file"),
120                        buildLogicAndTests,
121                        {
122                            caption: "Make two changes",
123                            change: sys => {
124                                const change1 = `${core[1].content}
125export class someClass { }`;
126                                sys.writeFile(core[1].path, change1);
127                                assert.equal(sys.writtenFiles.size, 1);
128                                sys.writtenFiles.clear();
129                                sys.writeFile(core[1].path, `${change1}
130export class someClass2 { }`);
131                            },
132                            timeouts: checkSingleTimeoutQueueLengthAndRun, // Builds core
133                        },
134                        buildLogicAndTests,
135                    ]
136                });
137
138                verifyTscWatch({
139                    scenario: "programUpdates",
140                    subScenario: `${subScenario}/non local change does not start build of referencing projects`,
141                    commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`],
142                    sys: () => createWatchedSystem(
143                        allFilesGetter(),
144                        { currentDirectory: projectsLocation }
145                    ),
146                    changes: [
147                        changeCore(() => `${core[1].content}
148function foo() { }`, "Make local change to core"),
149                    ]
150                });
151
152                function changeNewFile(newFileContent: string) {
153                    return changeFile(newFile.path, newFileContent, "Change to new File and build core");
154                }
155                verifyTscWatch({
156                    scenario: "programUpdates",
157                    subScenario: `${subScenario}/builds when new file is added, and its subsequent updates`,
158                    commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`],
159                    sys: () => createWatchedSystem(
160                        allFilesGetter(),
161                        { currentDirectory: projectsLocation }
162                    ),
163                    changes: [
164                        changeNewFile(newFile.content),
165                        buildLogicAndTests,
166                        changeNewFile(`${newFile.content}
167export class someClass2 { }`),
168                        buildLogicAndTests,
169                    ]
170                });
171            }
172
173            describe("with simple project reference graph", () => {
174                verifyProjectChanges(
175                    "with simple project reference graph",
176                    () => allFiles
177                );
178            });
179
180            describe("with circular project reference", () => {
181                verifyProjectChanges(
182                    "with circular project reference",
183                    () => {
184                        const [coreTsconfig, ...otherCoreFiles] = core;
185                        const circularCoreConfig: File = {
186                            path: coreTsconfig.path,
187                            content: JSON.stringify({
188                                compilerOptions: { composite: true, declaration: true },
189                                references: [{ path: "../tests", circular: true }]
190                            })
191                        };
192                        return [libFile, circularCoreConfig, ...otherCoreFiles, ...logic, ...tests];
193                    }
194                );
195            });
196        });
197
198        verifyTscWatch({
199            scenario: "programUpdates",
200            subScenario: "watches config files that are not present",
201            commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`],
202            sys: () => createWatchedSystem(
203                [libFile, ...core, logic[1], ...tests],
204                { currentDirectory: projectsLocation }
205            ),
206            changes: [
207                {
208                    caption: "Write logic tsconfig and build logic",
209                    change: sys => sys.writeFile(logic[0].path, logic[0].content),
210                    timeouts: checkSingleTimeoutQueueLengthAndRun, // Builds logic
211                },
212                {
213                    caption: "Build Tests",
214                    change: noop,
215                    // Build tests
216                    timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout,
217                }
218            ]
219        });
220
221        describe("when referenced using prepend, builds referencing project even for non local change", () => {
222            let coreIndex: File;
223            before(() => {
224                coreIndex = {
225                    path: core[1].path,
226                    content: `function foo() { return 10; }`
227                };
228            });
229            after(() => {
230                coreIndex = undefined!;
231            });
232            const buildLogic: TscWatchCompileChange = {
233                caption: "Build logic",
234                change: noop,
235                // Builds logic
236                timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout,
237            };
238            verifyTscWatch({
239                scenario: "programUpdates",
240                subScenario: "when referenced using prepend builds referencing project even for non local change",
241                commandLineArgs: ["-b", "-w", `sample1/${SubProject.logic}`],
242                sys: () => {
243                    const coreTsConfig: File = {
244                        path: core[0].path,
245                        content: JSON.stringify({
246                            compilerOptions: { composite: true, declaration: true, outFile: "index.js" }
247                        })
248                    };
249                    const logicTsConfig: File = {
250                        path: logic[0].path,
251                        content: JSON.stringify({
252                            compilerOptions: { composite: true, declaration: true, outFile: "index.js" },
253                            references: [{ path: "../core", prepend: true }]
254                        })
255                    };
256                    const logicIndex: File = {
257                        path: logic[1].path,
258                        content: `function bar() { return foo() + 1 };`
259                    };
260                    return createWatchedSystem([libFile, coreTsConfig, coreIndex, logicTsConfig, logicIndex], { currentDirectory: projectsLocation });
261                },
262                changes: [
263                    changeCore(() => `${coreIndex.content}
264function myFunc() { return 10; }`, "Make non local change and build core"),
265                    buildLogic,
266                    changeCore(() => `${coreIndex.content}
267function myFunc() { return 100; }`, "Make local change and build core"),
268                    buildLogic,
269                ]
270            });
271        });
272
273        describe("when referenced project change introduces error in the down stream project and then fixes it", () => {
274            const subProjectLibrary = `${projectsLocation}/sample1/Library`;
275            const libraryTs: File = {
276                path: `${subProjectLibrary}/library.ts`,
277                content: `
278interface SomeObject
279{
280    message: string;
281}
282
283export function createSomeObject(): SomeObject
284{
285    return {
286        message: "new Object"
287    };
288}`
289            };
290            verifyTscWatch({
291                scenario: "programUpdates",
292                subScenario: "when referenced project change introduces error in the down stream project and then fixes it",
293                commandLineArgs: ["-b", "-w", "App"],
294                sys: () => {
295                    const libraryTsconfig: File = {
296                        path: `${subProjectLibrary}/tsconfig.json`,
297                        content: JSON.stringify({ compilerOptions: { composite: true } })
298                    };
299                    const subProjectApp = `${projectsLocation}/sample1/App`;
300                    const appTs: File = {
301                        path: `${subProjectApp}/app.ts`,
302                        content: `import { createSomeObject } from "../Library/library";
303createSomeObject().message;`
304                    };
305                    const appTsconfig: File = {
306                        path: `${subProjectApp}/tsconfig.json`,
307                        content: JSON.stringify({ references: [{ path: "../Library" }] })
308                    };
309
310                    const files = [libFile, libraryTs, libraryTsconfig, appTs, appTsconfig];
311                    return createWatchedSystem(files, { currentDirectory: `${projectsLocation}/sample1` });
312                },
313                changes: [
314                    {
315                        caption: "Introduce error",
316                        // Change message in library to message2
317                        change: sys => sys.writeFile(libraryTs.path, libraryTs.content.replace(/message/g, "message2")),
318                        timeouts: sys => {
319                            sys.checkTimeoutQueueLengthAndRun(1); // Build library
320                            sys.checkTimeoutQueueLengthAndRun(1); // Build App
321                        },
322                    },
323                    {
324                        caption: "Fix error",
325                        // Revert library changes
326                        change: sys => sys.writeFile(libraryTs.path, libraryTs.content),
327                        timeouts: sys => {
328                            sys.checkTimeoutQueueLengthAndRun(1); // Build library
329                            sys.checkTimeoutQueueLengthAndRun(1); // Build App
330                        },
331                    },
332                ]
333            });
334
335        });
336
337        describe("reports errors in all projects on incremental compile", () => {
338            function verifyIncrementalErrors(subScenario: string, buildOptions: readonly string[]) {
339                verifyTscWatch({
340                    scenario: "programUpdates",
341                    subScenario: `reportErrors/${subScenario}`,
342                    commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`, ...buildOptions],
343                    sys: () => createWatchedSystem(allFiles, { currentDirectory: projectsLocation }),
344                    changes: [
345                        {
346                            caption: "change logic",
347                            change: sys => sys.writeFile(logic[1].path, `${logic[1].content}
348let y: string = 10;`),
349                            // Builds logic
350                            timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout,
351                        },
352                        {
353                            caption: "change core",
354                            change: sys => sys.writeFile(core[1].path, `${core[1].content}
355let x: string = 10;`),
356                            // Builds core
357                            timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout,
358                        }
359                    ]
360                });
361            }
362            verifyIncrementalErrors("when preserveWatchOutput is not used", emptyArray);
363            verifyIncrementalErrors("when preserveWatchOutput is passed on command line", ["--preserveWatchOutput"]);
364
365            describe("when declaration emit errors are present", () => {
366                const solution = "solution";
367                const subProject = "app";
368                const subProjectLocation = `${projectsLocation}/${solution}/${subProject}`;
369                const fileWithError: File = {
370                    path: `${subProjectLocation}/fileWithError.ts`,
371                    content: `export var myClassWithError = class {
372        tags() { }
373        private p = 12
374    };`
375                };
376                const fileWithFixedError: File = {
377                    path: fileWithError.path,
378                    content: fileWithError.content.replace("private p = 12", "")
379                };
380                const fileWithoutError: File = {
381                    path: `${subProjectLocation}/fileWithoutError.ts`,
382                    content: `export class myClass { }`
383                };
384                const tsconfig: File = {
385                    path: `${subProjectLocation}/tsconfig.json`,
386                    content: JSON.stringify({ compilerOptions: { composite: true } })
387                };
388
389                function incrementalBuild(sys: WatchedSystem) {
390                    sys.checkTimeoutQueueLengthAndRun(1); // Build the app
391                    sys.checkTimeoutQueueLength(0);
392                }
393
394                const fixError: TscWatchCompileChange = {
395                    caption: "Fix error in fileWithError",
396                    // Fix error
397                    change: sys => sys.writeFile(fileWithError.path, fileWithFixedError.content),
398                    timeouts: incrementalBuild
399                };
400
401                const changeFileWithoutError: TscWatchCompileChange = {
402                    caption: "Change fileWithoutError",
403                    change: sys => sys.writeFile(fileWithoutError.path, fileWithoutError.content.replace(/myClass/g, "myClass2")),
404                    timeouts: incrementalBuild
405                };
406
407                verifyTscWatch({
408                    scenario: "programUpdates",
409                    subScenario: "reportErrors/declarationEmitErrors/when fixing error files all files are emitted",
410                    commandLineArgs: ["-b", "-w", subProject],
411                    sys: () => createWatchedSystem(
412                        [libFile, fileWithError, fileWithoutError, tsconfig],
413                        { currentDirectory: `${projectsLocation}/${solution}` }
414                    ),
415                    changes: [
416                        fixError
417                    ]
418                });
419
420                verifyTscWatch({
421                    scenario: "programUpdates",
422                    subScenario: "reportErrors/declarationEmitErrors/when file with no error changes",
423                    commandLineArgs: ["-b", "-w", subProject],
424                    sys: () => createWatchedSystem(
425                        [libFile, fileWithError, fileWithoutError, tsconfig],
426                        { currentDirectory: `${projectsLocation}/${solution}` }
427                    ),
428                    changes: [
429                        changeFileWithoutError
430                    ]
431                });
432
433                describe("when reporting errors on introducing error", () => {
434                    const introduceError: TscWatchCompileChange = {
435                        caption: "Introduce error",
436                        change: sys => sys.writeFile(fileWithError.path, fileWithError.content),
437                        timeouts: incrementalBuild,
438                    };
439
440                    verifyTscWatch({
441                        scenario: "programUpdates",
442                        subScenario: "reportErrors/declarationEmitErrors/introduceError/when fixing errors only changed file is emitted",
443                        commandLineArgs: ["-b", "-w", subProject],
444                        sys: () => createWatchedSystem(
445                            [libFile, fileWithFixedError, fileWithoutError, tsconfig],
446                            { currentDirectory: `${projectsLocation}/${solution}` }
447                        ),
448                        changes: [
449                            introduceError,
450                            fixError
451                        ]
452                    });
453
454                    verifyTscWatch({
455                        scenario: "programUpdates",
456                        subScenario: "reportErrors/declarationEmitErrors/introduceError/when file with no error changes",
457                        commandLineArgs: ["-b", "-w", subProject],
458                        sys: () => createWatchedSystem(
459                            [libFile, fileWithFixedError, fileWithoutError, tsconfig],
460                            { currentDirectory: `${projectsLocation}/${solution}` }
461                        ),
462                        changes: [
463                            introduceError,
464                            changeFileWithoutError
465                        ]
466                    });
467                });
468            });
469        });
470
471        verifyTscWatch({
472            scenario: "programUpdates",
473            subScenario: "incremental updates in verbose mode",
474            commandLineArgs: ["-b", "-w", `sample1/${SubProject.tests}`, "-verbose"],
475            sys: () => createWatchedSystem(allFiles, { currentDirectory: projectsLocation }),
476            changes: [
477                {
478                    caption: "Make non dts change",
479                    change: sys => sys.writeFile(logic[1].path, `${logic[1].content}
480function someFn() { }`),
481                    timeouts: sys => {
482                        sys.checkTimeoutQueueLengthAndRun(1); // build logic and updates tests
483                        sys.checkTimeoutQueueLength(0);
484                    },
485                },
486                {
487                    caption: "Make dts change",
488                    change: sys => sys.writeFile(logic[1].path, `${logic[1].content}
489export function someFn() { }`),
490                    timeouts: sys => {
491                        sys.checkTimeoutQueueLengthAndRun(1); // build logic
492                        sys.checkTimeoutQueueLengthAndRun(1); // build tests
493                    },
494                }
495            ],
496        });
497
498        verifyTscWatch({
499            scenario: "programUpdates",
500            subScenario: "works when noUnusedParameters changes to false",
501            commandLineArgs: ["-b", "-w"],
502            sys: () => {
503                const index: File = {
504                    path: `${projectRoot}/index.ts`,
505                    content: `const fn = (a: string, b: string) => b;`
506                };
507                const configFile: File = {
508                    path: `${projectRoot}/tsconfig.json`,
509                    content: JSON.stringify({
510                        compilerOptions: {
511                            noUnusedParameters: true
512                        }
513                    })
514                };
515                return createWatchedSystem([index, configFile, libFile], { currentDirectory: projectRoot });
516            },
517            changes: [
518                {
519                    caption: "Change tsconfig to set noUnusedParameters to false",
520                    change: sys => sys.writeFile(`${projectRoot}/tsconfig.json`, JSON.stringify({
521                        compilerOptions: {
522                            noUnusedParameters: false
523                        }
524                    })),
525                    timeouts: runQueuedTimeoutCallbacks,
526                },
527            ]
528        });
529
530        verifyTscWatch({
531            scenario: "programUpdates",
532            subScenario: "should not trigger recompilation because of program emit",
533            commandLineArgs: ["-b", "-w", `sample1/${SubProject.core}`, "-verbose"],
534            sys: () => createWatchedSystem([libFile, ...core], { currentDirectory: projectsLocation }),
535            changes: [
536                noopChange,
537                {
538                    caption: "Add new file",
539                    change: sys => sys.writeFile(`sample1/${SubProject.core}/file3.ts`, `export const y = 10;`),
540                    timeouts: checkSingleTimeoutQueueLengthAndRun
541                },
542                noopChange,
543            ]
544        });
545
546        verifyTscWatch({
547            scenario: "programUpdates",
548            subScenario: "should not trigger recompilation because of program emit with outDir specified",
549            commandLineArgs: ["-b", "-w", `sample1/${SubProject.core}`, "-verbose"],
550            sys: () => {
551                const [coreConfig, ...rest] = core;
552                const newCoreConfig: File = { path: coreConfig.path, content: JSON.stringify({ compilerOptions: { composite: true, outDir: "outDir" } }) };
553                return createWatchedSystem([libFile, newCoreConfig, ...rest], { currentDirectory: projectsLocation });
554            },
555            changes: [
556                noopChange,
557                {
558                    caption: "Add new file",
559                    change: sys => sys.writeFile(`sample1/${SubProject.core}/file3.ts`, `export const y = 10;`),
560                    timeouts: checkSingleTimeoutQueueLengthAndRun
561                },
562                noopChange
563            ]
564        });
565
566        verifyTscWatch({
567            scenario: "programUpdates",
568            subScenario: "works with extended source files",
569            commandLineArgs: ["-b", "-w", "-v", "project1.tsconfig.json", "project2.tsconfig.json"],
570            sys: () => {
571                const alphaExtendedConfigFile: File = {
572                    path: "/a/b/alpha.tsconfig.json",
573                    content: "{}"
574                };
575                const project1Config: File = {
576                    path: "/a/b/project1.tsconfig.json",
577                    content: JSON.stringify({
578                        extends: "./alpha.tsconfig.json",
579                        compilerOptions: {
580                            composite: true,
581                        },
582                        files: [commonFile1.path, commonFile2.path]
583                    })
584                };
585                const bravoExtendedConfigFile: File = {
586                    path: "/a/b/bravo.tsconfig.json",
587                    content: JSON.stringify({
588                        extends: "./alpha.tsconfig.json"
589                    })
590                };
591                const otherFile: File = {
592                    path: "/a/b/other.ts",
593                    content: "let z = 0;",
594                };
595                const project2Config: File = {
596                    path: "/a/b/project2.tsconfig.json",
597                    content: JSON.stringify({
598                        extends: "./bravo.tsconfig.json",
599                        compilerOptions: {
600                            composite: true,
601                        },
602                        files: [otherFile.path]
603                    })
604                };
605                return createWatchedSystem([
606                    libFile,
607                    alphaExtendedConfigFile, project1Config, commonFile1, commonFile2,
608                    bravoExtendedConfigFile, project2Config, otherFile
609                ], { currentDirectory: "/a/b" });
610            },
611            changes: [
612                {
613                    caption: "Modify alpha config",
614                    change: sys => sys.writeFile("/a/b/alpha.tsconfig.json", JSON.stringify({
615                        compilerOptions: { strict: true }
616                    })),
617                    timeouts: checkSingleTimeoutQueueLengthAndRun // Build project1
618                },
619                {
620                    caption: "Build project 2",
621                    change: noop,
622                    timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2
623                },
624                {
625                    caption: "change bravo config",
626                    change: sys => sys.writeFile("/a/b/bravo.tsconfig.json", JSON.stringify({
627                        extends: "./alpha.tsconfig.json",
628                        compilerOptions: { strict: false }
629                    })),
630                    timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2
631                },
632                {
633                    caption: "project 2 extends alpha",
634                    change: sys => sys.writeFile("/a/b/project2.tsconfig.json", JSON.stringify({
635                        extends: "./alpha.tsconfig.json",
636                    })),
637                    timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2
638                },
639                {
640                    caption: "update aplha config",
641                    change: sys => sys.writeFile("/a/b/alpha.tsconfig.json", "{}"),
642                    timeouts: checkSingleTimeoutQueueLengthAndRun, // build project1
643                },
644                {
645                    caption: "Build project 2",
646                    change: noop,
647                    timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout // Build project2
648                },
649            ]
650        });
651
652        verifyTscWatch({
653            scenario: "programUpdates",
654            subScenario: "works correctly when project with extended config is removed",
655            commandLineArgs: ["-b", "-w", "-v"],
656            sys: () => {
657                const alphaExtendedConfigFile: File = {
658                    path: "/a/b/alpha.tsconfig.json",
659                    content: JSON.stringify({
660                        compilerOptions: {
661                            strict: true
662                        }
663                    })
664                };
665                const project1Config: File = {
666                    path: "/a/b/project1.tsconfig.json",
667                    content: JSON.stringify({
668                        extends: "./alpha.tsconfig.json",
669                        compilerOptions: {
670                            composite: true,
671                        },
672                        files: [commonFile1.path, commonFile2.path]
673                    })
674                };
675                const bravoExtendedConfigFile: File = {
676                    path: "/a/b/bravo.tsconfig.json",
677                    content: JSON.stringify({
678                        compilerOptions: {
679                            strict: true
680                        }
681                    })
682                };
683                const otherFile: File = {
684                    path: "/a/b/other.ts",
685                    content: "let z = 0;",
686                };
687                const project2Config: File = {
688                    path: "/a/b/project2.tsconfig.json",
689                    content: JSON.stringify({
690                        extends: "./bravo.tsconfig.json",
691                        compilerOptions: {
692                            composite: true,
693                        },
694                        files: [otherFile.path]
695                    })
696                };
697                const configFile: File = {
698                    path: "/a/b/tsconfig.json",
699                    content: JSON.stringify({
700                        references: [
701                            {
702                                path: "./project1.tsconfig.json",
703                            },
704                            {
705                                path: "./project2.tsconfig.json",
706                            },
707                        ],
708                        files: [],
709                    })
710                };
711                return createWatchedSystem([
712                    libFile, configFile,
713                    alphaExtendedConfigFile, project1Config, commonFile1, commonFile2,
714                    bravoExtendedConfigFile, project2Config, otherFile
715                ], { currentDirectory: "/a/b" });
716            },
717            changes: [
718                {
719                    caption: "Remove project2 from base config",
720                    change: sys => sys.modifyFile("/a/b/tsconfig.json", JSON.stringify({
721                        references: [
722                            {
723                                path: "./project1.tsconfig.json",
724                            },
725                        ],
726                        files: [],
727                    })),
728                    timeouts: checkSingleTimeoutQueueLengthAndRunAndVerifyNoTimeout,
729                }
730            ]
731        });
732
733        verifyTscWatch({
734            scenario: "programUpdates",
735            subScenario: "tsbuildinfo has error",
736            sys: () => createWatchedSystem({
737                "/src/project/main.ts": "export const x = 10;",
738                "/src/project/tsconfig.json": "{}",
739                "/src/project/tsconfig.tsbuildinfo": "Some random string",
740                [libFile.path]: libFile.content,
741            }),
742            commandLineArgs: ["--b", "src/project", "-i", "-w"],
743            changes: emptyArray
744        });
745    });
746}