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