• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1namespace ts.tscWatch {
2    describe("unittests:: tsc-watch:: Emit times and Error updates in builder after program changes", () => {
3        const config: File = {
4            path: `${projectRoot}/tsconfig.json`,
5            content: `{}`
6        };
7        interface VerifyEmitAndErrorUpdates {
8            subScenario: string
9            files: () => File[];
10            currentDirectory?: string;
11            changes: TscWatchCompileChange[];
12        }
13        function verifyEmitAndErrorUpdates({
14            subScenario,
15            files,
16            currentDirectory,
17            changes,
18        }: VerifyEmitAndErrorUpdates) {
19            verifyTscWatch({
20                scenario: "emitAndErrorUpdates",
21                subScenario: `default/${subScenario}`,
22                commandLineArgs: ["--w"],
23                sys: () => createWatchedSystem(
24                    files(),
25                    { currentDirectory: currentDirectory || projectRoot }
26                ),
27                changes,
28                baselineIncremental: true
29            });
30
31            verifyTscWatch({
32                scenario: "emitAndErrorUpdates",
33                subScenario: `defaultAndD/${subScenario}`,
34                commandLineArgs: ["--w", "--d"],
35                sys: () => createWatchedSystem(
36                    files(),
37                    { currentDirectory: currentDirectory || projectRoot }
38                ),
39                changes,
40                baselineIncremental: true
41            });
42
43            verifyTscWatch({
44                scenario: "emitAndErrorUpdates",
45                subScenario: `isolatedModules/${subScenario}`,
46                commandLineArgs: ["--w", "--isolatedModules"],
47                sys: () => createWatchedSystem(
48                    files(),
49                    { currentDirectory: currentDirectory || projectRoot }
50                ),
51                changes,
52                baselineIncremental: true
53            });
54
55            verifyTscWatch({
56                scenario: "emitAndErrorUpdates",
57                subScenario: `isolatedModulesAndD/${subScenario}`,
58                commandLineArgs: ["--w", "--isolatedModules", "--d"],
59                sys: () => createWatchedSystem(
60                    files(),
61                    { currentDirectory: currentDirectory || projectRoot }
62                ),
63                changes,
64                baselineIncremental: true
65            });
66
67            verifyTscWatch({
68                scenario: "emitAndErrorUpdates",
69                subScenario: `assumeChangesOnlyAffectDirectDependencies/${subScenario}`,
70                commandLineArgs: ["--w", "--assumeChangesOnlyAffectDirectDependencies"],
71                sys: () => createWatchedSystem(
72                    files(),
73                    { currentDirectory: currentDirectory || projectRoot }
74                ),
75                changes,
76                baselineIncremental: true
77            });
78
79            verifyTscWatch({
80                scenario: "emitAndErrorUpdates",
81                subScenario: `assumeChangesOnlyAffectDirectDependenciesAndD/${subScenario}`,
82                commandLineArgs: ["--w", "--assumeChangesOnlyAffectDirectDependencies", "--d"],
83                sys: () => createWatchedSystem(
84                    files(),
85                    { currentDirectory: currentDirectory || projectRoot }
86                ),
87                changes,
88                baselineIncremental: true
89            });
90        }
91
92        describe("deep import changes", () => {
93            const aFile: File = {
94                path: `${projectRoot}/a.ts`,
95                content: `import {B} from './b';
96declare var console: any;
97let b = new B();
98console.log(b.c.d);`
99            };
100
101            function verifyDeepImportChange(subScenario: string, bFile: File, cFile: File) {
102                verifyEmitAndErrorUpdates({
103                    subScenario: `deepImportChanges/${subScenario}`,
104                    files: () => [aFile, bFile, cFile, config, libFile],
105                    changes: [
106                        {
107                            caption: "Rename property d to d2 of class C to initialize signatures",
108                            change: sys => sys.writeFile(cFile.path, cFile.content.replace("d", "d2")),
109                            timeouts: runQueuedTimeoutCallbacks,
110                        },
111                        {
112                            caption: "Rename property d2 to d of class C to revert back to original text",
113                            change: sys => sys.writeFile(cFile.path, cFile.content.replace("d2", "d")),
114                            timeouts: runQueuedTimeoutCallbacks,
115                        },
116                        {
117                            caption: "Rename property d to d2 of class C",
118                            change: sys => sys.writeFile(cFile.path, cFile.content.replace("d", "d2")),
119                            timeouts: runQueuedTimeoutCallbacks,
120                        }
121                    ],
122                });
123            }
124            describe("updates errors when deep import file changes", () => {
125                const bFile: File = {
126                    path: `${projectRoot}/b.ts`,
127                    content: `import {C} from './c';
128export class B
129{
130    c = new C();
131}`
132                };
133                const cFile: File = {
134                    path: `${projectRoot}/c.ts`,
135                    content: `export class C
136{
137    d = 1;
138}`
139                };
140                verifyDeepImportChange(
141                    "errors for .ts change",
142                    bFile,
143                    cFile
144                );
145            });
146            describe("updates errors when deep import through declaration file changes", () => {
147                const bFile: File = {
148                    path: `${projectRoot}/b.d.ts`,
149                    content: `import {C} from './c';
150export class B
151{
152    c: C;
153}`
154                };
155                const cFile: File = {
156                    path: `${projectRoot}/c.d.ts`,
157                    content: `export class C
158{
159    d: number;
160}`
161                };
162                verifyDeepImportChange(
163                    "errors for .d.ts change",
164                    bFile,
165                    cFile
166                );
167            });
168        });
169
170        describe("updates errors in file not exporting a deep multilevel import that changes", () => {
171            const aFile: File = {
172                path: `${projectRoot}/a.ts`,
173                content: `export interface Point {
174    name: string;
175    c: Coords;
176}
177export interface Coords {
178    x2: number;
179    y: number;
180}`
181            };
182            const bFile: File = {
183                path: `${projectRoot}/b.ts`,
184                content: `import { Point } from "./a";
185export interface PointWrapper extends Point {
186}`
187            };
188            const cFile: File = {
189                path: `${projectRoot}/c.ts`,
190                content: `import { PointWrapper } from "./b";
191export function getPoint(): PointWrapper {
192    return {
193        name: "test",
194        c: {
195            x: 1,
196            y: 2
197        }
198    }
199};`
200            };
201            const dFile: File = {
202                path: `${projectRoot}/d.ts`,
203                content: `import { getPoint } from "./c";
204getPoint().c.x;`
205            };
206            const eFile: File = {
207                path: `${projectRoot}/e.ts`,
208                content: `import "./d";`
209            };
210            verifyEmitAndErrorUpdates({
211                subScenario: "file not exporting a deep multilevel import that changes",
212                files: () => [aFile, bFile, cFile, dFile, eFile, config, libFile],
213                changes: [
214                    {
215                        caption: "Rename property x2 to x of interface Coords to initialize signatures",
216                        change: sys => sys.writeFile(aFile.path, aFile.content.replace("x2", "x")),
217                        timeouts: runQueuedTimeoutCallbacks,
218                    },
219                    {
220                        caption: "Rename property x to x2 of interface Coords to revert back to original text",
221                        change: sys => sys.writeFile(aFile.path, aFile.content.replace("x: number", "x2: number")),
222                        timeouts: runQueuedTimeoutCallbacks,
223                    },
224                    {
225                        caption: "Rename property x2 to x of interface Coords",
226                        change: sys => sys.writeFile(aFile.path, aFile.content.replace("x2", "x")),
227                        timeouts: runQueuedTimeoutCallbacks,
228                    },
229                ]
230            });
231        });
232        describe("updates errors when file transitively exported file changes", () => {
233            const config: File = {
234                path: `${projectRoot}/tsconfig.json`,
235                content: JSON.stringify({
236                    files: ["app.ts"],
237                    compilerOptions: { baseUrl: "." }
238                })
239            };
240            const app: File = {
241                path: `${projectRoot}/app.ts`,
242                content: `import { Data } from "lib2/public";
243export class App {
244    public constructor() {
245        new Data().test();
246    }
247}`
248            };
249            const lib2Public: File = {
250                path: `${projectRoot}/lib2/public.ts`,
251                content: `export * from "./data";`
252            };
253            const lib2Data: File = {
254                path: `${projectRoot}/lib2/data.ts`,
255                content: `import { ITest } from "lib1/public";
256export class Data {
257    public test() {
258        const result: ITest = {
259            title: "title"
260        }
261        return result;
262    }
263}`
264            };
265            const lib1Public: File = {
266                path: `${projectRoot}/lib1/public.ts`,
267                content: `export * from "./tools/public";`
268            };
269            const lib1ToolsPublic: File = {
270                path: `${projectRoot}/lib1/tools/public.ts`,
271                content: `export * from "./tools.interface";`
272            };
273            const lib1ToolsInterface: File = {
274                path: `${projectRoot}/lib1/tools/tools.interface.ts`,
275                content: `export interface ITest {
276    title: string;
277}`
278            };
279
280            function verifyTransitiveExports(subScenario: string, files: readonly File[]) {
281                verifyEmitAndErrorUpdates({
282                    subScenario: `transitive exports/${subScenario}`,
283                    files: () => [lib1ToolsInterface, lib1ToolsPublic, app, lib2Public, lib1Public, ...files, config, libFile],
284                    changes: [
285                        {
286                            caption: "Rename property title to title2 of interface ITest to initialize signatures",
287                            change: sys => sys.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title", "title2")),
288                            timeouts: runQueuedTimeoutCallbacks,
289                        },
290                        {
291                            caption: "Rename property title2 to title of interface ITest to revert back to original text",
292                            change: sys => sys.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title2", "title")),
293                            timeouts: runQueuedTimeoutCallbacks,
294                        },
295                        {
296                            caption: "Rename property title to title2 of interface ITest",
297                            change: sys => sys.writeFile(lib1ToolsInterface.path, lib1ToolsInterface.content.replace("title", "title2")),
298                            timeouts: runQueuedTimeoutCallbacks,
299                        }
300                    ]
301                });
302            }
303            describe("when there are no circular import and exports", () => {
304                verifyTransitiveExports(
305                    "no circular import/export",
306                    [lib2Data]
307                );
308            });
309            describe("when there are circular import and exports", () => {
310                const lib2Data: File = {
311                    path: `${projectRoot}/lib2/data.ts`,
312                    content: `import { ITest } from "lib1/public"; import { Data2 } from "./data2";
313export class Data {
314    public dat?: Data2; public test() {
315        const result: ITest = {
316            title: "title"
317        }
318        return result;
319    }
320}`
321                };
322                const lib2Data2: File = {
323                    path: `${projectRoot}/lib2/data2.ts`,
324                    content: `import { Data } from "./data";
325export class Data2 {
326    public dat?: Data;
327}`
328                };
329                verifyTransitiveExports(
330                    "yes circular import/exports",
331                    [lib2Data, lib2Data2]
332                );
333            });
334        });
335
336        describe("with noEmitOnError", () => {
337            function change(caption: string, content: string): TscWatchCompileChange {
338                return {
339                    caption,
340                    change: sys => sys.writeFile(`${TestFSWithWatch.tsbuildProjectsLocation}/noEmitOnError/src/main.ts`, content),
341                    // build project
342                    timeouts: checkSingleTimeoutQueueLengthAndRun
343                };
344            }
345            const noChange: TscWatchCompileChange = {
346                caption: "No change",
347                change: sys => sys.writeFile(`${TestFSWithWatch.tsbuildProjectsLocation}/noEmitOnError/src/main.ts`, sys.readFile(`${TestFSWithWatch.tsbuildProjectsLocation}/noEmitOnError/src/main.ts`)!),
348                // build project
349                timeouts: checkSingleTimeoutQueueLengthAndRun,
350            };
351            verifyEmitAndErrorUpdates({
352                subScenario: "with noEmitOnError",
353                currentDirectory: `${TestFSWithWatch.tsbuildProjectsLocation}/noEmitOnError`,
354                files: () => ["shared/types/db.ts", "src/main.ts", "src/other.ts", "tsconfig.json"]
355                    .map(f => TestFSWithWatch.getTsBuildProjectFile("noEmitOnError", f)).concat({ path: libFile.path, content: libContent }),
356                changes: [
357                    noChange,
358                    change("Fix Syntax error", `import { A } from "../shared/types/db";
359const a = {
360    lastName: 'sdsd'
361};`),
362                    change("Semantic Error", `import { A } from "../shared/types/db";
363const a: string = 10;`),
364                    noChange,
365                    change("Fix Semantic Error", `import { A } from "../shared/types/db";
366const a: string = "hello";`),
367                    noChange,
368                ],
369            });
370        });
371    });
372}
373