diff --git a/src/testRunner/tsconfig.json b/src/testRunner/tsconfig.json index daad5a29b6a..8a5aab050c7 100644 --- a/src/testRunner/tsconfig.json +++ b/src/testRunner/tsconfig.json @@ -93,6 +93,7 @@ "unittests/tsserverProjectLoadingEvents.ts", "unittests/tsserverProjectSystem.ts", "unittests/tsserverProjectUpdatedInBackgroundEvent.ts", + "unittests/tsserverSymLinks.ts", "unittests/typingsInstaller.ts", "unittests/versionCache.ts", "unittests/watchEnvironment.ts", diff --git a/src/testRunner/unittests/tsserverHelpers.ts b/src/testRunner/unittests/tsserverHelpers.ts index f042a43738a..86dfddf3bb3 100644 --- a/src/testRunner/unittests/tsserverHelpers.ts +++ b/src/testRunner/unittests/tsserverHelpers.ts @@ -482,14 +482,15 @@ namespace ts.projectSystem { const toLocation = protocolToLocation(str); return { start: toLocation(span.start), end: toLocation(textSpanEnd(span)) }; } - //function protocolRenameSpanFromSubstring( - // str: string, - // substring: string, - // options?: SpanFromSubstringOptions, - // prefixSuffixText?: { readonly prefixText?: string, readonly suffixText?: string }, - //): protocol.RenameTextSpan { - // return { ...protocolTextSpanFromSubstring(str, substring, options), ...prefixSuffixText }; - //} + + export function protocolRenameSpanFromSubstring( + str: string, + substring: string, + options?: SpanFromSubstringOptions, + prefixSuffixText?: { readonly prefixText?: string, readonly suffixText?: string }, + ): protocol.RenameTextSpan { + return { ...protocolTextSpanFromSubstring(str, substring, options), ...prefixSuffixText }; + } export function textSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): TextSpan { const start = nthIndexOf(str, substring, options ? options.index : 0); diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index 996b81d4c2d..56493c01e35 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -7,14 +7,6 @@ namespace ts.projectSystem { checkArray("ScriptInfos files", arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles); } - function protocolRenameSpanFromSubstring( - str: string, - substring: string, - options?: SpanFromSubstringOptions, - prefixSuffixText?: { readonly prefixText?: string, readonly suffixText?: string }, - ): protocol.RenameTextSpan { - return { ...protocolTextSpanFromSubstring(str, substring, options), ...prefixSuffixText }; - } function protocolFileLocationFromSubstring(file: File, substring: string): protocol.FileLocationRequestArgs { return { file: file.path, ...protocolLocationFromSubstring(file.content, substring) }; } @@ -5144,287 +5136,6 @@ var x = 10;` }); }); - describe("tsserverProjectSystem with symLinks", () => { - it("rename in common file renames all project", () => { - const projects = "/users/username/projects"; - const folderA = `${projects}/a`; - const aFile: File = { - path: `${folderA}/a.ts`, - content: `import {C} from "./c/fc"; console.log(C)` - }; - const aTsconfig: File = { - path: `${folderA}/tsconfig.json`, - content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) - }; - const aC: SymLink = { - path: `${folderA}/c`, - symLink: "../c" - }; - const aFc = `${folderA}/c/fc.ts`; - - const folderB = `${projects}/b`; - const bFile: File = { - path: `${folderB}/b.ts`, - content: `import {C} from "./c/fc"; console.log(C)` - }; - const bTsconfig: File = { - path: `${folderB}/tsconfig.json`, - content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) - }; - const bC: SymLink = { - path: `${folderB}/c`, - symLink: "../c" - }; - const bFc = `${folderB}/c/fc.ts`; - - const folderC = `${projects}/c`; - const cFile: File = { - path: `${folderC}/fc.ts`, - content: `export const C = 8` - }; - - const files = [cFile, libFile, aFile, aTsconfig, aC, bFile, bTsconfig, bC]; - const host = createServerHost(files); - const session = createSession(host); - const projectService = session.getProjectService(); - openFilesForSession( - [ - { file: aFile, projectRootPath: folderA }, - { file: bFile, projectRootPath: folderB }, - { file: aFc, projectRootPath: folderA }, - { file: bFc, projectRootPath: folderB }, - ], - session); - checkNumberOfProjects(projectService, { configuredProjects: 2 }); - assert.isDefined(projectService.configuredProjects.get(aTsconfig.path)); - assert.isDefined(projectService.configuredProjects.get(bTsconfig.path)); - - const response = executeSessionRequest(session, protocol.CommandTypes.Rename, { file: aFc, ...protocolLocationFromSubstring(cFile.content, "C") }); - - assert.equal(aFile.content, bFile.content); - const abLocs: protocol.RenameTextSpan[] = [ - protocolRenameSpanFromSubstring(aFile.content, "C"), - protocolRenameSpanFromSubstring(aFile.content, "C", { index: 1 }), - ]; - const span = protocolRenameSpanFromSubstring(cFile.content, "C"); - const cLocs: protocol.RenameTextSpan[] = [span]; - assert.deepEqual(response, { - info: { - canRename: true, - displayName: "C", - fileToRename: undefined, - fullDisplayName: '"/users/username/projects/a/c/fc".C', - kind: ScriptElementKind.constElement, - kindModifiers: ScriptElementKindModifier.exportedModifier, - triggerSpan: protocolTextSpanFromSubstring(cFile.content, "C"), - }, - locs: [ - { file: aFc, locs: cLocs }, - { file: aFile.path, locs: abLocs }, - { file: bFc, locs: cLocs }, - { file: bFile.path, locs: abLocs }, - ], - }); - }); - - describe("module resolution when symlinked folder contents change and resolve modules", () => { - const projectRootPath = "/users/username/projects/myproject"; - const packages = `${projectRootPath}/javascript/packages`; - const recognizersDateTime = `${packages}/recognizers-date-time`; - const recognizersText = `${packages}/recognizers-text`; - const recognizersTextDist = `${recognizersText}/dist`; - const moduleName = "@microsoft/recognizers-text"; - const moduleNameInFile = `"${moduleName}"`; - const recognizersDateTimeSrcFile: File = { - path: `${recognizersDateTime}/src/datetime/baseDate.ts`, - content: `import {C} from ${moduleNameInFile}; -new C();` - }; - const recognizerDateTimeTsconfigPath = `${recognizersDateTime}/tsconfig.json`; - const recognizerDateTimeTsconfigWithoutPathMapping: File = { - path: recognizerDateTimeTsconfigPath, - content: JSON.stringify({ - include: ["src"] - }) - }; - const recognizerDateTimeTsconfigWithPathMapping: File = { - path: recognizerDateTimeTsconfigPath, - content: JSON.stringify({ - compilerOptions: { - rootDir: "src", - baseUrl: "./", - paths: { - "@microsoft/*": ["../*"] - } - }, - include: ["src"] - }) - }; - const nodeModulesRecorgnizersText: SymLink = { - path: `${recognizersDateTime}/node_modules/@microsoft/recognizers-text`, - symLink: recognizersText - }; - const recognizerTextSrcFile: File = { - path: `${recognizersText}/src/recognizers-text.ts`, - content: `export class C { method () { return 10; } }` - }; - const recongnizerTextDistTypingFile: File = { - path: `${recognizersTextDist}/types/recognizers-text.d.ts`, - content: `export class C { method(): number; }` - }; - const recongnizerTextPackageJson: File = { - path: `${recognizersText}/package.json`, - content: JSON.stringify({ - typings: "dist/types/recognizers-text.d.ts" - }) - }; - const filesInProjectWithUnresolvedModule = [recognizerDateTimeTsconfigPath, libFile.path, recognizersDateTimeSrcFile.path]; - const filesInProjectWithResolvedModule = [...filesInProjectWithUnresolvedModule, recongnizerTextDistTypingFile.path]; - - function verifyErrors(session: TestSession, semanticErrors: protocol.Diagnostic[]) { - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [recognizersDateTimeSrcFile.path], - } - }); - - const host = session.host; - host.checkTimeoutQueueLengthAndRun(1); - - checkErrorMessage(session, "syntaxDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "semanticDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: semanticErrors }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - - checkErrorMessage(session, "suggestionDiag", { - file: recognizersDateTimeSrcFile.path, - diagnostics: [], - }); - checkCompleteEvent(session, 2, expectedSequenceId); - } - - function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], recursiveDirectories: ReadonlyMap, nonRecursiveDirectories: string[]) { - checkWatchedFilesDetailed(host, files.filter(f => f !== recognizersDateTimeSrcFile.path), 1); - checkWatchedDirectoriesDetailed(host, nonRecursiveDirectories, 1, /*recursive*/ false); - checkWatchedDirectoriesDetailed(host, recursiveDirectories, /*recursive*/ true); - } - - function createSessionAndOpenFile(host: TestServerHost) { - const session = createSession(host, { canUseEvents: true }); - session.executeCommandSeq({ - command: protocol.CommandTypes.Open, - arguments: { - file: recognizersDateTimeSrcFile.path, - projectRootPath - } - }); - return session; - } - - function verifyModuleResolution(withPathMapping: boolean) { - describe(withPathMapping ? "when tsconfig file contains path mapping" : "when tsconfig does not contain path mapping", () => { - const filesWithSources = [libFile, recognizersDateTimeSrcFile, withPathMapping ? recognizerDateTimeTsconfigWithPathMapping : recognizerDateTimeTsconfigWithoutPathMapping, recognizerTextSrcFile, recongnizerTextPackageJson]; - const filesWithNodeModulesSetup = [...filesWithSources, nodeModulesRecorgnizersText]; - const filesAfterCompilation = [...filesWithNodeModulesSetup, recongnizerTextDistTypingFile]; - - const watchedDirectoriesWithResolvedModule = arrayToMap(getTypeRootsFromLocation(recognizersDateTime), k => k, () => 1); - watchedDirectoriesWithResolvedModule.set(`${recognizersDateTime}/src`, withPathMapping ? 1 : 2); // wild card + failed lookups - if (!withPathMapping) { - watchedDirectoriesWithResolvedModule.set(`${recognizersDateTime}/node_modules`, 1); // failed lookups - } - const watchedDirectoriesWithUnresolvedModule = cloneMap(watchedDirectoriesWithResolvedModule); - watchedDirectoriesWithUnresolvedModule.set(`${recognizersDateTime}/src`, 2); // wild card + failed lookups - [`${recognizersDateTime}/node_modules`, ...(withPathMapping ? [recognizersText] : emptyArray), ...getNodeModuleDirectories(packages)].forEach(d => { - watchedDirectoriesWithUnresolvedModule.set(d, 1); - }); - const nonRecursiveWatchedDirectories = withPathMapping ? [packages] : emptyArray; - - function verifyProjectWithResolvedModule(session: TestSession) { - const projectService = session.getProjectService(); - const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!; - checkProjectActualFiles(project, filesInProjectWithResolvedModule); - verifyWatchedFilesAndDirectories(session.host, filesInProjectWithResolvedModule, watchedDirectoriesWithResolvedModule, nonRecursiveWatchedDirectories); - verifyErrors(session, []); - } - - function verifyProjectWithUnresolvedModule(session: TestSession) { - const projectService = session.getProjectService(); - const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!; - checkProjectActualFiles(project, filesInProjectWithUnresolvedModule); - verifyWatchedFilesAndDirectories(session.host, filesInProjectWithUnresolvedModule, watchedDirectoriesWithUnresolvedModule, nonRecursiveWatchedDirectories); - const startOffset = recognizersDateTimeSrcFile.content.indexOf('"') + 1; - verifyErrors(session, [ - createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + moduleNameInFile.length }, Diagnostics.Cannot_find_module_0, [moduleName]) - ]); - } - - it("when project compiles from sources", () => { - const host = createServerHost(filesWithSources); - const session = createSessionAndOpenFile(host); - verifyProjectWithUnresolvedModule(session); - - host.reloadFS(filesAfterCompilation); - host.runQueuedTimeoutCallbacks(); - - verifyProjectWithResolvedModule(session); - }); - - it("when project has node_modules setup but doesnt have modules in typings folder and then recompiles", () => { - const host = createServerHost(filesWithNodeModulesSetup); - const session = createSessionAndOpenFile(host); - verifyProjectWithUnresolvedModule(session); - - host.reloadFS(filesAfterCompilation); - host.runQueuedTimeoutCallbacks(); - - if (withPathMapping) { - verifyProjectWithResolvedModule(session); - } - else { - // Cannot handle the resolution update - verifyProjectWithUnresolvedModule(session); - } - }); - - it("when project recompiles after deleting generated folders", () => { - const host = createServerHost(filesAfterCompilation); - const session = createSessionAndOpenFile(host); - - verifyProjectWithResolvedModule(session); - - host.deleteFolder(recognizersTextDist, /*recursive*/ true); - host.runQueuedTimeoutCallbacks(); - - verifyProjectWithUnresolvedModule(session); - - host.ensureFileOrFolder(recongnizerTextDistTypingFile); - host.runQueuedTimeoutCallbacks(); - - if (withPathMapping) { - verifyProjectWithResolvedModule(session); - } - else { - // Cannot handle the resolution update - verifyProjectWithUnresolvedModule(session); - } - }); - }); - } - - verifyModuleResolution(/*withPathMapping*/ false); - verifyModuleResolution(/*withPathMapping*/ true); - }); - }); - describe("tsserverProjectSystem forceConsistentCasingInFileNames", () => { it("works when extends is specified with a case insensitive file system", () => { const rootPath = "/Users/username/dev/project"; diff --git a/src/testRunner/unittests/tsserverSymLinks.ts b/src/testRunner/unittests/tsserverSymLinks.ts new file mode 100644 index 00000000000..c9bad7ea678 --- /dev/null +++ b/src/testRunner/unittests/tsserverSymLinks.ts @@ -0,0 +1,282 @@ +namespace ts.projectSystem { + describe("tsserverProjectSystem with symLinks", () => { + it("rename in common file renames all project", () => { + const projects = "/users/username/projects"; + const folderA = `${projects}/a`; + const aFile: File = { + path: `${folderA}/a.ts`, + content: `import {C} from "./c/fc"; console.log(C)` + }; + const aTsconfig: File = { + path: `${folderA}/tsconfig.json`, + content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) + }; + const aC: SymLink = { + path: `${folderA}/c`, + symLink: "../c" + }; + const aFc = `${folderA}/c/fc.ts`; + + const folderB = `${projects}/b`; + const bFile: File = { + path: `${folderB}/b.ts`, + content: `import {C} from "./c/fc"; console.log(C)` + }; + const bTsconfig: File = { + path: `${folderB}/tsconfig.json`, + content: JSON.stringify({ compilerOptions: { module: "commonjs" } }) + }; + const bC: SymLink = { + path: `${folderB}/c`, + symLink: "../c" + }; + const bFc = `${folderB}/c/fc.ts`; + + const folderC = `${projects}/c`; + const cFile: File = { + path: `${folderC}/fc.ts`, + content: `export const C = 8` + }; + + const files = [cFile, libFile, aFile, aTsconfig, aC, bFile, bTsconfig, bC]; + const host = createServerHost(files); + const session = createSession(host); + const projectService = session.getProjectService(); + openFilesForSession( + [ + { file: aFile, projectRootPath: folderA }, + { file: bFile, projectRootPath: folderB }, + { file: aFc, projectRootPath: folderA }, + { file: bFc, projectRootPath: folderB }, + ], + session); + checkNumberOfProjects(projectService, { configuredProjects: 2 }); + assert.isDefined(projectService.configuredProjects.get(aTsconfig.path)); + assert.isDefined(projectService.configuredProjects.get(bTsconfig.path)); + + const response = executeSessionRequest(session, protocol.CommandTypes.Rename, { file: aFc, ...protocolLocationFromSubstring(cFile.content, "C") }); + + assert.equal(aFile.content, bFile.content); + const abLocs: protocol.RenameTextSpan[] = [ + protocolRenameSpanFromSubstring(aFile.content, "C"), + protocolRenameSpanFromSubstring(aFile.content, "C", { index: 1 }), + ]; + const span = protocolRenameSpanFromSubstring(cFile.content, "C"); + const cLocs: protocol.RenameTextSpan[] = [span]; + assert.deepEqual(response, { + info: { + canRename: true, + displayName: "C", + fileToRename: undefined, + fullDisplayName: '"/users/username/projects/a/c/fc".C', + kind: ScriptElementKind.constElement, + kindModifiers: ScriptElementKindModifier.exportedModifier, + triggerSpan: protocolTextSpanFromSubstring(cFile.content, "C"), + }, + locs: [ + { file: aFc, locs: cLocs }, + { file: aFile.path, locs: abLocs }, + { file: bFc, locs: cLocs }, + { file: bFile.path, locs: abLocs }, + ], + }); + }); + + describe("module resolution when symlinked folder contents change and resolve modules", () => { + const projectRootPath = "/users/username/projects/myproject"; + const packages = `${projectRootPath}/javascript/packages`; + const recognizersDateTime = `${packages}/recognizers-date-time`; + const recognizersText = `${packages}/recognizers-text`; + const recognizersTextDist = `${recognizersText}/dist`; + const moduleName = "@microsoft/recognizers-text"; + const moduleNameInFile = `"${moduleName}"`; + const recognizersDateTimeSrcFile: File = { + path: `${recognizersDateTime}/src/datetime/baseDate.ts`, + content: `import {C} from ${moduleNameInFile}; +new C();` + }; + const recognizerDateTimeTsconfigPath = `${recognizersDateTime}/tsconfig.json`; + const recognizerDateTimeTsconfigWithoutPathMapping: File = { + path: recognizerDateTimeTsconfigPath, + content: JSON.stringify({ + include: ["src"] + }) + }; + const recognizerDateTimeTsconfigWithPathMapping: File = { + path: recognizerDateTimeTsconfigPath, + content: JSON.stringify({ + compilerOptions: { + rootDir: "src", + baseUrl: "./", + paths: { + "@microsoft/*": ["../*"] + } + }, + include: ["src"] + }) + }; + const nodeModulesRecorgnizersText: SymLink = { + path: `${recognizersDateTime}/node_modules/@microsoft/recognizers-text`, + symLink: recognizersText + }; + const recognizerTextSrcFile: File = { + path: `${recognizersText}/src/recognizers-text.ts`, + content: `export class C { method () { return 10; } }` + }; + const recongnizerTextDistTypingFile: File = { + path: `${recognizersTextDist}/types/recognizers-text.d.ts`, + content: `export class C { method(): number; }` + }; + const recongnizerTextPackageJson: File = { + path: `${recognizersText}/package.json`, + content: JSON.stringify({ + typings: "dist/types/recognizers-text.d.ts" + }) + }; + const filesInProjectWithUnresolvedModule = [recognizerDateTimeTsconfigPath, libFile.path, recognizersDateTimeSrcFile.path]; + const filesInProjectWithResolvedModule = [...filesInProjectWithUnresolvedModule, recongnizerTextDistTypingFile.path]; + + function verifyErrors(session: TestSession, semanticErrors: protocol.Diagnostic[]) { + session.clearMessages(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: server.CommandNames.Geterr, + arguments: { + delay: 0, + files: [recognizersDateTimeSrcFile.path], + } + }); + + const host = session.host; + host.checkTimeoutQueueLengthAndRun(1); + + checkErrorMessage(session, "syntaxDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + + checkErrorMessage(session, "semanticDiag", { file: recognizersDateTimeSrcFile.path, diagnostics: semanticErrors }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + + checkErrorMessage(session, "suggestionDiag", { + file: recognizersDateTimeSrcFile.path, + diagnostics: [], + }); + checkCompleteEvent(session, 2, expectedSequenceId); + } + + function verifyWatchedFilesAndDirectories(host: TestServerHost, files: string[], recursiveDirectories: ReadonlyMap, nonRecursiveDirectories: string[]) { + checkWatchedFilesDetailed(host, files.filter(f => f !== recognizersDateTimeSrcFile.path), 1); + checkWatchedDirectoriesDetailed(host, nonRecursiveDirectories, 1, /*recursive*/ false); + checkWatchedDirectoriesDetailed(host, recursiveDirectories, /*recursive*/ true); + } + + function createSessionAndOpenFile(host: TestServerHost) { + const session = createSession(host, { canUseEvents: true }); + session.executeCommandSeq({ + command: protocol.CommandTypes.Open, + arguments: { + file: recognizersDateTimeSrcFile.path, + projectRootPath + } + }); + return session; + } + + function verifyModuleResolution(withPathMapping: boolean) { + describe(withPathMapping ? "when tsconfig file contains path mapping" : "when tsconfig does not contain path mapping", () => { + const filesWithSources = [libFile, recognizersDateTimeSrcFile, withPathMapping ? recognizerDateTimeTsconfigWithPathMapping : recognizerDateTimeTsconfigWithoutPathMapping, recognizerTextSrcFile, recongnizerTextPackageJson]; + const filesWithNodeModulesSetup = [...filesWithSources, nodeModulesRecorgnizersText]; + const filesAfterCompilation = [...filesWithNodeModulesSetup, recongnizerTextDistTypingFile]; + + const watchedDirectoriesWithResolvedModule = arrayToMap(getTypeRootsFromLocation(recognizersDateTime), k => k, () => 1); + watchedDirectoriesWithResolvedModule.set(`${recognizersDateTime}/src`, withPathMapping ? 1 : 2); // wild card + failed lookups + if (!withPathMapping) { + watchedDirectoriesWithResolvedModule.set(`${recognizersDateTime}/node_modules`, 1); // failed lookups + } + const watchedDirectoriesWithUnresolvedModule = cloneMap(watchedDirectoriesWithResolvedModule); + watchedDirectoriesWithUnresolvedModule.set(`${recognizersDateTime}/src`, 2); // wild card + failed lookups + [`${recognizersDateTime}/node_modules`, ...(withPathMapping ? [recognizersText] : emptyArray), ...getNodeModuleDirectories(packages)].forEach(d => { + watchedDirectoriesWithUnresolvedModule.set(d, 1); + }); + const nonRecursiveWatchedDirectories = withPathMapping ? [packages] : emptyArray; + + function verifyProjectWithResolvedModule(session: TestSession) { + const projectService = session.getProjectService(); + const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!; + checkProjectActualFiles(project, filesInProjectWithResolvedModule); + verifyWatchedFilesAndDirectories(session.host, filesInProjectWithResolvedModule, watchedDirectoriesWithResolvedModule, nonRecursiveWatchedDirectories); + verifyErrors(session, []); + } + + function verifyProjectWithUnresolvedModule(session: TestSession) { + const projectService = session.getProjectService(); + const project = projectService.configuredProjects.get(recognizerDateTimeTsconfigPath)!; + checkProjectActualFiles(project, filesInProjectWithUnresolvedModule); + verifyWatchedFilesAndDirectories(session.host, filesInProjectWithUnresolvedModule, watchedDirectoriesWithUnresolvedModule, nonRecursiveWatchedDirectories); + const startOffset = recognizersDateTimeSrcFile.content.indexOf('"') + 1; + verifyErrors(session, [ + createDiagnostic({ line: 1, offset: startOffset }, { line: 1, offset: startOffset + moduleNameInFile.length }, Diagnostics.Cannot_find_module_0, [moduleName]) + ]); + } + + it("when project compiles from sources", () => { + const host = createServerHost(filesWithSources); + const session = createSessionAndOpenFile(host); + verifyProjectWithUnresolvedModule(session); + + host.reloadFS(filesAfterCompilation); + host.runQueuedTimeoutCallbacks(); + + verifyProjectWithResolvedModule(session); + }); + + it("when project has node_modules setup but doesnt have modules in typings folder and then recompiles", () => { + const host = createServerHost(filesWithNodeModulesSetup); + const session = createSessionAndOpenFile(host); + verifyProjectWithUnresolvedModule(session); + + host.reloadFS(filesAfterCompilation); + host.runQueuedTimeoutCallbacks(); + + if (withPathMapping) { + verifyProjectWithResolvedModule(session); + } + else { + // Cannot handle the resolution update + verifyProjectWithUnresolvedModule(session); + } + }); + + it("when project recompiles after deleting generated folders", () => { + const host = createServerHost(filesAfterCompilation); + const session = createSessionAndOpenFile(host); + + verifyProjectWithResolvedModule(session); + + host.deleteFolder(recognizersTextDist, /*recursive*/ true); + host.runQueuedTimeoutCallbacks(); + + verifyProjectWithUnresolvedModule(session); + + host.ensureFileOrFolder(recongnizerTextDistTypingFile); + host.runQueuedTimeoutCallbacks(); + + if (withPathMapping) { + verifyProjectWithResolvedModule(session); + } + else { + // Cannot handle the resolution update + verifyProjectWithUnresolvedModule(session); + } + }); + }); + } + + verifyModuleResolution(/*withPathMapping*/ false); + verifyModuleResolution(/*withPathMapping*/ true); + }); + }); +}