mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-13 06:20:23 -06:00
Move error tests from tsserver project system to projectErrors
This commit is contained in:
parent
53a6968f06
commit
1b6db32ecd
@ -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();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user