Move error tests from tsserver project system to projectErrors

This commit is contained in:
Sheetal Nandi 2018-12-07 12:22:14 -08:00
parent 53a6968f06
commit 1b6db32ecd
3 changed files with 303 additions and 332 deletions

View File

@ -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 = `/// <reference path="${refPathNotFound1}" />
/// <reference path="${refPathNotFound2}" />`;
session.executeCommandSeq<protocol.OpenRequest>({
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<protocol.GeterrRequest>({
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<protocol.OpenRequest>({
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<protocol.GeterrRequest>({
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<protocol.GeterrRequest>({
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<protocol.GeterrRequest>({
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();
}
});
});
}

View File

@ -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<string>) {
// checkArray(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles);
//}
export function checkProjectRootFiles(project: server.Project, expectedFiles: ReadonlyArray<string>) {
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<string> = [], 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<string> = [], 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 {

View File

@ -41,10 +41,6 @@ namespace ts.projectSystem {
}
}
function checkProjectRootFiles(project: server.Project, expectedFiles: ReadonlyArray<string>) {
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<string> = [], 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 = `/// <reference path="${refPathNotFound1}" />
/// <reference path="${refPathNotFound2}" />`;
session.executeCommandSeq<protocol.OpenRequest>({
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<protocol.GeterrRequest>({
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<protocol.OpenRequest>({
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<protocol.GeterrRequest>({
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<protocol.GeterrRequest>({
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<protocol.GeterrRequest>({
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 = {