From 1b6db32ecd07121063d9ee4eafa2a1795c7dd070 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 7 Dec 2018 12:22:14 -0800 Subject: [PATCH] Move error tests from tsserver project system to projectErrors --- src/testRunner/unittests/projectErrors.ts | 281 ++++++++++++++++ src/testRunner/unittests/tsserverHelpers.ts | 44 +-- .../unittests/tsserverProjectSystem.ts | 310 ------------------ 3 files changed, 303 insertions(+), 332 deletions(-) diff --git a/src/testRunner/unittests/projectErrors.ts b/src/testRunner/unittests/projectErrors.ts index d243e9d5ea4..6eb364b569d 100644 --- a/src/testRunner/unittests/projectErrors.ts +++ b/src/testRunner/unittests/projectErrors.ts @@ -199,4 +199,285 @@ namespace ts.projectSystem { } }); }); + + describe("tsserver:: Project Errors are reported as appropriate", () => { + function createErrorLogger() { + let hasError = false; + const errorLogger: server.Logger = { + close: noop, + hasLevel: () => true, + loggingEnabled: () => true, + perftrc: noop, + info: noop, + msg: (_s, type) => { + if (type === server.Msg.Err) { + hasError = true; + } + }, + startGroup: noop, + endGroup: noop, + getLogFileName: () => undefined + }; + return { + errorLogger, + hasError: () => hasError + }; + } + + it("document is not contained in project", () => { + const file1 = { + path: "/a/b/app.ts", + content: "" + }; + const corruptedConfig = { + path: "/a/b/tsconfig.json", + content: "{" + }; + const host = createServerHost([file1, corruptedConfig]); + const projectService = createProjectService(host); + + projectService.openClientFile(file1.path); + projectService.checkNumberOfProjects({ configuredProjects: 1 }); + + const project = projectService.findProject(corruptedConfig.path)!; + checkProjectRootFiles(project, [file1.path]); + }); + + describe("when opening new file that doesnt exist on disk yet", () => { + function verifyNonExistentFile(useProjectRoot: boolean) { + const folderPath = "/user/someuser/projects/someFolder"; + const fileInRoot: File = { + path: `/src/somefile.d.ts`, + content: "class c { }" + }; + const fileInProjectRoot: File = { + path: `${folderPath}/src/somefile.d.ts`, + content: "class c { }" + }; + const host = createServerHost([libFile, fileInRoot, fileInProjectRoot]); + const { hasError, errorLogger } = createErrorLogger(); + const session = createSession(host, { canUseEvents: true, logger: errorLogger, useInferredProjectPerProjectRoot: true }); + + const projectService = session.getProjectService(); + const untitledFile = "untitled:Untitled-1"; + const refPathNotFound1 = "../../../../../../typings/@epic/Core.d.ts"; + const refPathNotFound2 = "./src/somefile.d.ts"; + const fileContent = `/// +/// `; + session.executeCommandSeq({ + command: server.CommandNames.Open, + arguments: { + file: untitledFile, + fileContent, + scriptKindName: "TS", + projectRootPath: useProjectRoot ? folderPath : undefined + } + }); + checkNumberOfProjects(projectService, { inferredProjects: 1 }); + const infoForUntitledAtProjectRoot = projectService.getScriptInfoForPath(`${folderPath.toLowerCase()}/${untitledFile.toLowerCase()}` as Path); + const infoForUnitiledAtRoot = projectService.getScriptInfoForPath(`/${untitledFile.toLowerCase()}` as Path); + const infoForSomefileAtProjectRoot = projectService.getScriptInfoForPath(`/${folderPath.toLowerCase()}/src/somefile.d.ts` as Path); + const infoForSomefileAtRoot = projectService.getScriptInfoForPath(`${fileInRoot.path.toLowerCase()}` as Path); + if (useProjectRoot) { + assert.isDefined(infoForUntitledAtProjectRoot); + assert.isUndefined(infoForUnitiledAtRoot); + } + else { + assert.isDefined(infoForUnitiledAtRoot); + assert.isUndefined(infoForUntitledAtProjectRoot); + } + assert.isUndefined(infoForSomefileAtRoot); + assert.isUndefined(infoForSomefileAtProjectRoot); + + // Since this is not js project so no typings are queued + host.checkTimeoutQueueLength(0); + + const newTimeoutId = host.getNextTimeoutId(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: server.CommandNames.Geterr, + arguments: { + delay: 0, + files: [untitledFile] + } + }); + host.checkTimeoutQueueLength(1); + + // Run the last one = get error request + host.runQueuedTimeoutCallbacks(newTimeoutId); + + assert.isFalse(hasError()); + host.checkTimeoutQueueLength(0); + checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(); + assert.isFalse(hasError()); + const errorOffset = fileContent.indexOf(refPathNotFound1) + 1; + checkErrorMessage(session, "semanticDiag", { + file: untitledFile, + diagnostics: [ + createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"), + createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error") + ] + }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + assert.isFalse(hasError()); + checkErrorMessage(session, "suggestionDiag", { file: untitledFile, diagnostics: [] }); + checkCompleteEvent(session, 2, expectedSequenceId); + session.clearMessages(); + } + + it("has projectRoot", () => { + verifyNonExistentFile(/*useProjectRoot*/ true); + }); + + it("does not have projectRoot", () => { + verifyNonExistentFile(/*useProjectRoot*/ false); + }); + }); + + it("folder rename updates project structure and reports no errors", () => { + const projectDir = "/a/b/projects/myproject"; + const app: File = { + path: `${projectDir}/bar/app.ts`, + content: "class Bar implements foo.Foo { getFoo() { return ''; } get2() { return 1; } }" + }; + const foo: File = { + path: `${projectDir}/foo/foo.ts`, + content: "declare namespace foo { interface Foo { get2(): number; getFoo(): string; } }" + }; + const configFile: File = { + 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() { + session.clearMessages(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: server.CommandNames.Geterr, + arguments: { + delay: 0, + files: [app.path] + } + }); + host.checkTimeoutQueueLengthAndRun(1); + checkErrorMessage(session, "syntaxDiag", { file: app.path, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(); + checkErrorMessage(session, "semanticDiag", { file: app.path, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + checkErrorMessage(session, "suggestionDiag", { file: app.path, diagnostics: [] }); + checkCompleteEvent(session, 2, expectedSequenceId); + session.clearMessages(); + } + }); + + it("Getting errors before opening file", () => { + const file: File = { + path: "/a/b/project/file.ts", + content: "let x: number = false;" + }; + const host = createServerHost([file, libFile]); + const { hasError, errorLogger } = createErrorLogger(); + const session = createSession(host, { canUseEvents: true, logger: errorLogger }); + + session.clearMessages(); + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: server.CommandNames.Geterr, + arguments: { + delay: 0, + files: [file.path] + } + }); + + host.runQueuedImmediateCallbacks(); + assert.isFalse(hasError()); + checkCompleteEvent(session, 1, expectedSequenceId); + session.clearMessages(); + }); + + it("Reports errors correctly when file referenced by inferred project root, is opened right after closing the root file", () => { + const projectRoot = "/user/username/projects/myproject"; + const app: File = { + path: `${projectRoot}/src/client/app.js`, + content: "" + }; + const serverUtilities: File = { + path: `${projectRoot}/src/server/utilities.js`, + content: `function getHostName() { return "hello"; } export { getHostName };` + }; + const backendTest: File = { + path: `${projectRoot}/test/backend/index.js`, + content: `import { getHostName } from '../../src/server/utilities';export default getHostName;` + }; + const files = [libFile, app, serverUtilities, backendTest]; + const host = createServerHost(files); + const session = createSession(host, { useInferredProjectPerProjectRoot: true, canUseEvents: true }); + openFilesForSession([{ file: app, projectRootPath: projectRoot }], session); + const service = session.getProjectService(); + checkNumberOfProjects(service, { inferredProjects: 1 }); + const project = service.inferredProjects[0]; + checkProjectActualFiles(project, [libFile.path, app.path]); + openFilesForSession([{ file: backendTest, projectRootPath: projectRoot }], session); + checkNumberOfProjects(service, { inferredProjects: 1 }); + checkProjectActualFiles(project, files.map(f => f.path)); + checkErrors([backendTest.path, app.path]); + closeFilesForSession([backendTest], session); + openFilesForSession([{ file: serverUtilities.path, projectRootPath: projectRoot }], session); + checkErrors([serverUtilities.path, app.path]); + + function checkErrors(openFiles: [string, string]) { + const expectedSequenceId = session.getNextSeq(); + session.executeCommandSeq({ + command: protocol.CommandTypes.Geterr, + arguments: { + delay: 0, + files: openFiles + } + }); + + for (const openFile of openFiles) { + session.clearMessages(); + host.checkTimeoutQueueLength(3); + host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 1); + + checkErrorMessage(session, "syntaxDiag", { file: openFile, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(); + checkErrorMessage(session, "semanticDiag", { file: openFile, diagnostics: [] }); + session.clearMessages(); + + host.runQueuedImmediateCallbacks(1); + checkErrorMessage(session, "suggestionDiag", { file: openFile, diagnostics: [] }); + } + checkCompleteEvent(session, 2, expectedSequenceId); + session.clearMessages(); + } + }); + }); } diff --git a/src/testRunner/unittests/tsserverHelpers.ts b/src/testRunner/unittests/tsserverHelpers.ts index 5162f17d6ae..f5113b768fc 100644 --- a/src/testRunner/unittests/tsserverHelpers.ts +++ b/src/testRunner/unittests/tsserverHelpers.ts @@ -430,9 +430,9 @@ namespace ts.projectSystem { checkArray(`${server.ProjectKind[project.projectKind]} project, actual files`, project.getFileNames(), expectedFiles); } - //function checkProjectRootFiles(project: server.Project, expectedFiles: ReadonlyArray) { - // checkArray(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles); - //} + export function checkProjectRootFiles(project: server.Project, expectedFiles: ReadonlyArray) { + checkArray(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles); + } export function mapCombinedPathsInAncestor(dir: string, path2: string, mapAncestor: (ancestor: string) => boolean) { dir = normalizePath(dir); @@ -613,17 +613,17 @@ namespace ts.projectSystem { // verifyDiagnostics(actual, []); //} - //function checkErrorMessage(session: TestSession, eventName: protocol.DiagnosticEventKind, diagnostics: protocol.DiagnosticEventBody, isMostRecent = false): void { - // checkNthEvent(session, server.toEvent(eventName, diagnostics), 0, isMostRecent); - //} + export function checkErrorMessage(session: TestSession, eventName: protocol.DiagnosticEventKind, diagnostics: protocol.DiagnosticEventBody, isMostRecent = false): void { + checkNthEvent(session, server.toEvent(eventName, diagnostics), 0, isMostRecent); + } - //function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage, args: ReadonlyArray = [], category = diagnosticCategoryName(message), reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[]): protocol.Diagnostic { - // return { start, end, text: formatStringFromArgs(message.message, args), code: message.code, category, reportsUnnecessary, relatedInformation, source: undefined }; - //} + export function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage, args: ReadonlyArray = [], category = diagnosticCategoryName(message), reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[]): protocol.Diagnostic { + return { start, end, text: formatStringFromArgs(message.message, args), code: message.code, category, reportsUnnecessary, relatedInformation, source: undefined }; + } - //function checkCompleteEvent(session: TestSession, numberOfCurrentEvents: number, expectedSequenceId: number, isMostRecent = true): void { - // checkNthEvent(session, server.toEvent("requestCompleted", { request_seq: expectedSequenceId }), numberOfCurrentEvents - 1, isMostRecent); - //} + export function checkCompleteEvent(session: TestSession, numberOfCurrentEvents: number, expectedSequenceId: number, isMostRecent = true): void { + checkNthEvent(session, server.toEvent("requestCompleted", { request_seq: expectedSequenceId }), numberOfCurrentEvents - 1, isMostRecent); + } //function checkProjectUpdatedInBackgroundEvent(session: TestSession, openFiles: string[]) { // checkNthEvent(session, server.toEvent("projectsUpdatedInBackground", { openFiles }), 0, /*isMostRecent*/ true); @@ -635,18 +635,18 @@ namespace ts.projectSystem { // } //} - //function checkNthEvent(session: TestSession, expectedEvent: protocol.Event, index: number, isMostRecent: boolean) { - // const events = session.events; - // assert.deepEqual(events[index], expectedEvent, `Expected ${JSON.stringify(expectedEvent)} at ${index} in ${JSON.stringify(events)}`); + export function checkNthEvent(session: TestSession, expectedEvent: protocol.Event, index: number, isMostRecent: boolean) { + const events = session.events; + assert.deepEqual(events[index], expectedEvent, `Expected ${JSON.stringify(expectedEvent)} at ${index} in ${JSON.stringify(events)}`); - // const outputs = session.host.getOutput(); - // assert.equal(outputs[index], server.formatMessage(expectedEvent, nullLogger, Utils.byteLength, session.host.newLine)); + const outputs = session.host.getOutput(); + assert.equal(outputs[index], server.formatMessage(expectedEvent, nullLogger, Utils.byteLength, session.host.newLine)); - // if (isMostRecent) { - // assert.strictEqual(events.length, index + 1, JSON.stringify(events)); - // assert.strictEqual(outputs.length, index + 1, JSON.stringify(outputs)); - // } - //} + if (isMostRecent) { + assert.strictEqual(events.length, index + 1, JSON.stringify(events)); + assert.strictEqual(outputs.length, index + 1, JSON.stringify(outputs)); + } + } //function makeReferenceItem(file: File, isDefinition: boolean, text: string, lineText: string, options?: SpanFromSubstringOptions): protocol.ReferencesResponseItem { // return { diff --git a/src/testRunner/unittests/tsserverProjectSystem.ts b/src/testRunner/unittests/tsserverProjectSystem.ts index 122d9459e75..3ccb7b2ffa0 100644 --- a/src/testRunner/unittests/tsserverProjectSystem.ts +++ b/src/testRunner/unittests/tsserverProjectSystem.ts @@ -41,10 +41,6 @@ namespace ts.projectSystem { } } - function checkProjectRootFiles(project: server.Project, expectedFiles: ReadonlyArray) { - checkArray(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles); - } - function getNodeModuleDirectories(dir: string) { return getRootsToWatchWithAncestorDirectory(dir, nodeModules); } @@ -134,18 +130,6 @@ namespace ts.projectSystem { verifyDiagnostics(actual, []); } - function checkErrorMessage(session: TestSession, eventName: protocol.DiagnosticEventKind, diagnostics: protocol.DiagnosticEventBody, isMostRecent = false): void { - checkNthEvent(session, server.toEvent(eventName, diagnostics), 0, isMostRecent); - } - - function createDiagnostic(start: protocol.Location, end: protocol.Location, message: DiagnosticMessage, args: ReadonlyArray = [], category = diagnosticCategoryName(message), reportsUnnecessary?: {}, relatedInformation?: protocol.DiagnosticRelatedInformation[]): protocol.Diagnostic { - return { start, end, text: formatStringFromArgs(message.message, args), code: message.code, category, reportsUnnecessary, relatedInformation, source: undefined }; - } - - function checkCompleteEvent(session: TestSession, numberOfCurrentEvents: number, expectedSequenceId: number, isMostRecent = true): void { - checkNthEvent(session, server.toEvent("requestCompleted", { request_seq: expectedSequenceId }), numberOfCurrentEvents - 1, isMostRecent); - } - function checkProjectUpdatedInBackgroundEvent(session: TestSession, openFiles: string[]) { checkNthEvent(session, server.toEvent("projectsUpdatedInBackground", { openFiles }), 0, /*isMostRecent*/ true); } @@ -156,19 +140,6 @@ namespace ts.projectSystem { } } - function checkNthEvent(session: TestSession, expectedEvent: protocol.Event, index: number, isMostRecent: boolean) { - const events = session.events; - assert.deepEqual(events[index], expectedEvent, `Expected ${JSON.stringify(expectedEvent)} at ${index} in ${JSON.stringify(events)}`); - - const outputs = session.host.getOutput(); - assert.equal(outputs[index], server.formatMessage(expectedEvent, nullLogger, Utils.byteLength, session.host.newLine)); - - if (isMostRecent) { - assert.strictEqual(events.length, index + 1, JSON.stringify(events)); - assert.strictEqual(outputs.length, index + 1, JSON.stringify(outputs)); - } - } - describe("tsserverProjectSystem general functionality", () => { const commonFile1: File = { path: "/a/b/commonFile1.ts", @@ -3377,287 +3348,6 @@ var x = 10;` }); }); - describe("tsserverProjectSystem Proper errors", () => { - function createErrorLogger() { - let hasError = false; - const errorLogger: server.Logger = { - close: noop, - hasLevel: () => true, - loggingEnabled: () => true, - perftrc: noop, - info: noop, - msg: (_s, type) => { - if (type === server.Msg.Err) { - hasError = true; - } - }, - startGroup: noop, - endGroup: noop, - getLogFileName: () => undefined - }; - return { - errorLogger, - hasError: () => hasError - }; - } - - it("document is not contained in project", () => { - const file1 = { - path: "/a/b/app.ts", - content: "" - }; - const corruptedConfig = { - path: "/a/b/tsconfig.json", - content: "{" - }; - const host = createServerHost([file1, corruptedConfig]); - const projectService = createProjectService(host); - - projectService.openClientFile(file1.path); - projectService.checkNumberOfProjects({ configuredProjects: 1 }); - - const project = projectService.findProject(corruptedConfig.path)!; - checkProjectRootFiles(project, [file1.path]); - }); - - describe("when opening new file that doesnt exist on disk yet", () => { - function verifyNonExistentFile(useProjectRoot: boolean) { - const folderPath = "/user/someuser/projects/someFolder"; - const fileInRoot: File = { - path: `/src/somefile.d.ts`, - content: "class c { }" - }; - const fileInProjectRoot: File = { - path: `${folderPath}/src/somefile.d.ts`, - content: "class c { }" - }; - const host = createServerHost([libFile, fileInRoot, fileInProjectRoot]); - const { hasError, errorLogger } = createErrorLogger(); - const session = createSession(host, { canUseEvents: true, logger: errorLogger, useInferredProjectPerProjectRoot: true }); - - const projectService = session.getProjectService(); - const untitledFile = "untitled:Untitled-1"; - const refPathNotFound1 = "../../../../../../typings/@epic/Core.d.ts"; - const refPathNotFound2 = "./src/somefile.d.ts"; - const fileContent = `/// -/// `; - session.executeCommandSeq({ - command: server.CommandNames.Open, - arguments: { - file: untitledFile, - fileContent, - scriptKindName: "TS", - projectRootPath: useProjectRoot ? folderPath : undefined - } - }); - checkNumberOfProjects(projectService, { inferredProjects: 1 }); - const infoForUntitledAtProjectRoot = projectService.getScriptInfoForPath(`${folderPath.toLowerCase()}/${untitledFile.toLowerCase()}` as Path); - const infoForUnitiledAtRoot = projectService.getScriptInfoForPath(`/${untitledFile.toLowerCase()}` as Path); - const infoForSomefileAtProjectRoot = projectService.getScriptInfoForPath(`/${folderPath.toLowerCase()}/src/somefile.d.ts` as Path); - const infoForSomefileAtRoot = projectService.getScriptInfoForPath(`${fileInRoot.path.toLowerCase()}` as Path); - if (useProjectRoot) { - assert.isDefined(infoForUntitledAtProjectRoot); - assert.isUndefined(infoForUnitiledAtRoot); - } - else { - assert.isDefined(infoForUnitiledAtRoot); - assert.isUndefined(infoForUntitledAtProjectRoot); - } - assert.isUndefined(infoForSomefileAtRoot); - assert.isUndefined(infoForSomefileAtProjectRoot); - - // Since this is not js project so no typings are queued - host.checkTimeoutQueueLength(0); - - const newTimeoutId = host.getNextTimeoutId(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [untitledFile] - } - }); - host.checkTimeoutQueueLength(1); - - // Run the last one = get error request - host.runQueuedTimeoutCallbacks(newTimeoutId); - - assert.isFalse(hasError()); - host.checkTimeoutQueueLength(0); - checkErrorMessage(session, "syntaxDiag", { file: untitledFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - assert.isFalse(hasError()); - const errorOffset = fileContent.indexOf(refPathNotFound1) + 1; - checkErrorMessage(session, "semanticDiag", { - file: untitledFile, - diagnostics: [ - createDiagnostic({ line: 1, offset: errorOffset }, { line: 1, offset: errorOffset + refPathNotFound1.length }, Diagnostics.File_0_not_found, [refPathNotFound1], "error"), - createDiagnostic({ line: 2, offset: errorOffset }, { line: 2, offset: errorOffset + refPathNotFound2.length }, Diagnostics.File_0_not_found, [refPathNotFound2.substr(2)], "error") - ] - }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - assert.isFalse(hasError()); - checkErrorMessage(session, "suggestionDiag", { file: untitledFile, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); - } - - it("has projectRoot", () => { - verifyNonExistentFile(/*useProjectRoot*/ true); - }); - - it("does not have projectRoot", () => { - verifyNonExistentFile(/*useProjectRoot*/ false); - }); - }); - - it("folder rename updates project structure and reports no errors", () => { - const projectDir = "/a/b/projects/myproject"; - const app: File = { - path: `${projectDir}/bar/app.ts`, - content: "class Bar implements foo.Foo { getFoo() { return ''; } get2() { return 1; } }" - }; - const foo: File = { - path: `${projectDir}/foo/foo.ts`, - content: "declare namespace foo { interface Foo { get2(): number; getFoo(): string; } }" - }; - const configFile: File = { - 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() { - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [app.path] - } - }); - host.checkTimeoutQueueLengthAndRun(1); - checkErrorMessage(session, "syntaxDiag", { file: app.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: app.path, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: app.path, diagnostics: [] }); - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); - } - }); - - it("Getting errors before opening file", () => { - const file: File = { - path: "/a/b/project/file.ts", - content: "let x: number = false;" - }; - const host = createServerHost([file, libFile]); - const { hasError, errorLogger } = createErrorLogger(); - const session = createSession(host, { canUseEvents: true, logger: errorLogger }); - - session.clearMessages(); - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: server.CommandNames.Geterr, - arguments: { - delay: 0, - files: [file.path] - } - }); - - host.runQueuedImmediateCallbacks(); - assert.isFalse(hasError()); - checkCompleteEvent(session, 1, expectedSequenceId); - session.clearMessages(); - }); - - it("Reports errors correctly when file referenced by inferred project root, is opened right after closing the root file", () => { - const projectRoot = "/user/username/projects/myproject"; - const app: File = { - path: `${projectRoot}/src/client/app.js`, - content: "" - }; - const serverUtilities: File = { - path: `${projectRoot}/src/server/utilities.js`, - content: `function getHostName() { return "hello"; } export { getHostName };` - }; - const backendTest: File = { - path: `${projectRoot}/test/backend/index.js`, - content: `import { getHostName } from '../../src/server/utilities';export default getHostName;` - }; - const files = [libFile, app, serverUtilities, backendTest]; - const host = createServerHost(files); - const session = createSession(host, { useInferredProjectPerProjectRoot: true, canUseEvents: true }); - openFilesForSession([{ file: app, projectRootPath: projectRoot }], session); - const service = session.getProjectService(); - checkNumberOfProjects(service, { inferredProjects: 1 }); - const project = service.inferredProjects[0]; - checkProjectActualFiles(project, [libFile.path, app.path]); - openFilesForSession([{ file: backendTest, projectRootPath: projectRoot }], session); - checkNumberOfProjects(service, { inferredProjects: 1 }); - checkProjectActualFiles(project, files.map(f => f.path)); - checkErrors([backendTest.path, app.path]); - closeFilesForSession([backendTest], session); - openFilesForSession([{ file: serverUtilities.path, projectRootPath: projectRoot }], session); - checkErrors([serverUtilities.path, app.path]); - - function checkErrors(openFiles: [string, string]) { - const expectedSequenceId = session.getNextSeq(); - session.executeCommandSeq({ - command: protocol.CommandTypes.Geterr, - arguments: { - delay: 0, - files: openFiles - } - }); - - for (const openFile of openFiles) { - session.clearMessages(); - host.checkTimeoutQueueLength(3); - host.runQueuedTimeoutCallbacks(host.getNextTimeoutId() - 1); - - checkErrorMessage(session, "syntaxDiag", { file: openFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(); - checkErrorMessage(session, "semanticDiag", { file: openFile, diagnostics: [] }); - session.clearMessages(); - - host.runQueuedImmediateCallbacks(1); - checkErrorMessage(session, "suggestionDiag", { file: openFile, diagnostics: [] }); - } - checkCompleteEvent(session, 2, expectedSequenceId); - session.clearMessages(); - } - }); - }); - describe("tsserverProjectSystem autoDiscovery", () => { it("does not depend on extension", () => { const file1 = {