From 57be7ff3f646391d0d3f825d2705100d86a17101 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 6 Nov 2017 12:21:45 -0800 Subject: [PATCH] Add test case when inside wild card watched directory folder is renamed --- .../unittests/tsserverProjectSystem.ts | 53 +++++++++++++++++++ src/harness/virtualFileSystemWithWatch.ts | 37 ++++++++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index c09a1bcb5d..b281f8763a 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -2767,6 +2767,7 @@ namespace ts.projectSystem { watchedRecursiveDirectories.push(`${root}/a/b/src`, `${root}/a/b/node_modules`); checkWatchedDirectories(host, watchedRecursiveDirectories, /*recursive*/ true); }); + }); describe("Proper errors", () => { @@ -2869,6 +2870,58 @@ namespace ts.projectSystem { verifyNonExistentFile(/*useProjectRoot*/ false); }); }); + + it("folder rename updates project structure and reports no errors", () => { + const projectDir = "/a/b/projects/myproject"; + const app: FileOrFolder = { + path: `${projectDir}/bar/app.ts`, + content: "class Bar implements foo.Foo { getFoo() { return ''; } get2() { return 1; } }" + }; + const foo: FileOrFolder = { + path: `${projectDir}/foo/foo.ts`, + content: "declare namespace foo { interface Foo { get2(): number; getFoo(): string; } }" + }; + const configFile: FileOrFolder = { + path: `${projectDir}/tsconfig.json`, + content: JSON.stringify({ compilerOptions: { module: "none", targer: "es5" }, exclude: ["node_modules"] }) + }; + const host = createServerHost([app, foo, configFile]); + const session = createSession(host, { canUseEvents: true, }); + const projectService = session.getProjectService(); + + session.executeCommandSeq({ + command: server.CommandNames.Open, + arguments: { file: app.path, } + }); + checkNumberOfProjects(projectService, { configuredProjects: 1 }); + assert.isDefined(projectService.configuredProjects.get(configFile.path)); + verifyErrorsInApp(); + + host.renameFolder(`${projectDir}/foo`, `${projectDir}/foo2`); + host.runQueuedTimeoutCallbacks(); + host.runQueuedTimeoutCallbacks(); + verifyErrorsInApp(); + + function verifyErrorsInApp() { + host.clearOutput(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: server.CommandNames.Geterr, + arguments: { + delay: 0, + files: [app.path] + } + }); + host.checkTimeoutQueueLengthAndRun(1); + checkErrorMessage(host, "syntaxDiag", { file: app.path, diagnostics: [] }); + host.clearOutput(); + + host.runQueuedImmediateCallbacks(); + checkErrorMessage(host, "semanticDiag", { file: app.path, diagnostics: [] }); + checkCompleteEvent(host, 2, expectedSequenceId); + host.clearOutput(); + } + }); }); describe("autoDiscovery", () => { diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 6c3bd8a635..25581093f3 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -346,6 +346,39 @@ interface Array {}` } } + renameFolder(folderName: string, newFolderName: string) { + const fullPath = getNormalizedAbsolutePath(folderName, this.currentDirectory); + const path = this.toPath(fullPath); + const folder = this.fs.get(path) as Folder; + Debug.assert(!!folder); + + // Only remove the folder + this.removeFileOrFolder(folder, returnFalse, /*isRenaming*/ true); + + // Add updated folder with new folder name + const newFullPath = getNormalizedAbsolutePath(newFolderName, this.currentDirectory); + const newFolder = this.toFolder(newFullPath); + const newPath = newFolder.path; + const basePath = getDirectoryPath(path); + Debug.assert(basePath !== path); + Debug.assert(basePath === getDirectoryPath(newPath)); + const baseFolder = this.fs.get(basePath) as Folder; + this.addFileOrFolderInFolder(baseFolder, newFolder); + + // Invoke watches for files in the folder as deleted (from old path) + for (const entry of folder.entries) { + Debug.assert(isFile(entry)); + this.fs.delete(entry.path); + this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Deleted); + + entry.fullPath = combinePaths(newFullPath, getBaseFileName(entry.fullPath)); + entry.path = this.toPath(entry.fullPath); + newFolder.entries.push(entry); + this.fs.set(entry.path, entry); + this.invokeFileWatcher(entry.fullPath, FileWatcherEventKind.Created); + } + } + ensureFileOrFolder(fileOrDirectory: FileOrFolder, ignoreWatchInvokedWithTriggerAsFileCreate?: boolean) { if (isString(fileOrDirectory.content)) { const file = this.toFile(fileOrDirectory); @@ -393,7 +426,7 @@ interface Array {}` this.invokeDirectoryWatcher(folder.fullPath, fileOrDirectory.fullPath); } - private removeFileOrFolder(fileOrDirectory: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean) { + private removeFileOrFolder(fileOrDirectory: File | Folder, isRemovableLeafFolder: (folder: Folder) => boolean, isRenaming?: boolean) { const basePath = getDirectoryPath(fileOrDirectory.path); const baseFolder = this.fs.get(basePath) as Folder; if (basePath !== fileOrDirectory.path) { @@ -406,7 +439,7 @@ interface Array {}` this.invokeFileWatcher(fileOrDirectory.fullPath, FileWatcherEventKind.Deleted); } else { - Debug.assert(fileOrDirectory.entries.length === 0); + Debug.assert(fileOrDirectory.entries.length === 0 || isRenaming); const relativePath = this.getRelativePathToDirectory(fileOrDirectory.fullPath, fileOrDirectory.fullPath); // Invoke directory and recursive directory watcher for the folder // Here we arent invoking recursive directory watchers for the base folders