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 = {