mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-13 06:20:23 -06:00
Separate out tests for project loading events into their own unittest file
This commit is contained in:
parent
03d66dd2ef
commit
39ec69e26e
@ -88,6 +88,7 @@
|
||||
"unittests/tsconfigParsing.ts",
|
||||
"unittests/tscWatchEmit.ts",
|
||||
"unittests/tscWatchMode.ts",
|
||||
"unittests/tsserverProjectLoadingEvents.ts",
|
||||
"unittests/tsserverProjectSystem.ts",
|
||||
"unittests/typingsInstaller.ts",
|
||||
"unittests/versionCache.ts",
|
||||
|
||||
@ -15,13 +15,13 @@ namespace ts.projectSystem {
|
||||
export import checkWatchedDirectories = TestFSWithWatch.checkWatchedDirectories;
|
||||
export import checkWatchedDirectoriesDetailed = TestFSWithWatch.checkWatchedDirectoriesDetailed;
|
||||
|
||||
//const outputEventRegex = /Content\-Length: [\d]+\r\n\r\n/;
|
||||
//function mapOutputToJson(s: string) {
|
||||
// return convertToObject(
|
||||
// parseJsonText("json.json", s.replace(outputEventRegex, "")),
|
||||
// []
|
||||
// );
|
||||
//}
|
||||
const outputEventRegex = /Content\-Length: [\d]+\r\n\r\n/;
|
||||
export function mapOutputToJson(s: string) {
|
||||
return convertToObject(
|
||||
parseJsonText("json.json", s.replace(outputEventRegex, "")),
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
export const customTypesMap = {
|
||||
path: <Path>"/typesMap.json",
|
||||
@ -330,39 +330,39 @@ namespace ts.projectSystem {
|
||||
return new TestSession({ ...sessionOptions, ...opts });
|
||||
}
|
||||
|
||||
//function createSessionWithEventTracking<T extends server.ProjectServiceEvent>(host: server.ServerHost, eventName: T["eventName"], ...eventNames: T["eventName"][]) {
|
||||
// const events: T[] = [];
|
||||
// const session = createSession(host, {
|
||||
// eventHandler: e => {
|
||||
// if (e.eventName === eventName || eventNames.some(eventName => e.eventName === eventName)) {
|
||||
// events.push(e as T);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
export function createSessionWithEventTracking<T extends server.ProjectServiceEvent>(host: server.ServerHost, eventName: T["eventName"], ...eventNames: T["eventName"][]) {
|
||||
const events: T[] = [];
|
||||
const session = createSession(host, {
|
||||
eventHandler: e => {
|
||||
if (e.eventName === eventName || eventNames.some(eventName => e.eventName === eventName)) {
|
||||
events.push(e as T);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// return { session, events };
|
||||
//}
|
||||
return { session, events };
|
||||
}
|
||||
|
||||
//function createSessionWithDefaultEventHandler<T extends protocol.AnyEvent>(host: TestServerHost, eventNames: T["event"] | T["event"][], opts: Partial<server.SessionOptions> = {}) {
|
||||
// const session = createSession(host, { canUseEvents: true, ...opts });
|
||||
export function createSessionWithDefaultEventHandler<T extends protocol.AnyEvent>(host: TestServerHost, eventNames: T["event"] | T["event"][], opts: Partial<server.SessionOptions> = {}) {
|
||||
const session = createSession(host, { canUseEvents: true, ...opts });
|
||||
|
||||
// return {
|
||||
// session,
|
||||
// getEvents,
|
||||
// clearEvents
|
||||
// };
|
||||
return {
|
||||
session,
|
||||
getEvents,
|
||||
clearEvents
|
||||
};
|
||||
|
||||
// function getEvents() {
|
||||
// return mapDefined(host.getOutput(), s => {
|
||||
// const e = mapOutputToJson(s);
|
||||
// return (isArray(eventNames) ? eventNames.some(eventName => e.event === eventName) : e.event === eventNames) ? e as T : undefined;
|
||||
// });
|
||||
// }
|
||||
function getEvents() {
|
||||
return mapDefined(host.getOutput(), s => {
|
||||
const e = mapOutputToJson(s);
|
||||
return (isArray(eventNames) ? eventNames.some(eventName => e.event === eventName) : e.event === eventNames) ? e as T : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
// function clearEvents() {
|
||||
// session.clearMessages();
|
||||
// }
|
||||
//}
|
||||
function clearEvents() {
|
||||
session.clearMessages();
|
||||
}
|
||||
}
|
||||
|
||||
export interface CreateProjectServiceParameters {
|
||||
cancellationToken?: HostCancellationToken;
|
||||
@ -467,23 +467,25 @@ namespace ts.projectSystem {
|
||||
// checkArray("Open files", arrayFrom(projectService.openFiles.keys(), path => projectService.getScriptInfoForPath(path as Path)!.fileName), expectedFiles.map(file => file.path));
|
||||
//}
|
||||
|
||||
//function protocolLocationFromSubstring(str: string, substring: string): protocol.Location {
|
||||
// const start = str.indexOf(substring);
|
||||
// Debug.assert(start !== -1);
|
||||
// return protocolToLocation(str)(start);
|
||||
//}
|
||||
//function protocolToLocation(text: string): (pos: number) => protocol.Location {
|
||||
// const lineStarts = computeLineStarts(text);
|
||||
// return pos => {
|
||||
// const x = computeLineAndCharacterOfPosition(lineStarts, pos);
|
||||
// return { line: x.line + 1, offset: x.character + 1 };
|
||||
// };
|
||||
//}
|
||||
//function protocolTextSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): protocol.TextSpan {
|
||||
// const span = textSpanFromSubstring(str, substring, options);
|
||||
// const toLocation = protocolToLocation(str);
|
||||
// return { start: toLocation(span.start), end: toLocation(textSpanEnd(span)) };
|
||||
//}
|
||||
export function protocolLocationFromSubstring(str: string, substring: string): protocol.Location {
|
||||
const start = str.indexOf(substring);
|
||||
Debug.assert(start !== -1);
|
||||
return protocolToLocation(str)(start);
|
||||
}
|
||||
|
||||
function protocolToLocation(text: string): (pos: number) => protocol.Location {
|
||||
const lineStarts = computeLineStarts(text);
|
||||
return pos => {
|
||||
const x = computeLineAndCharacterOfPosition(lineStarts, pos);
|
||||
return { line: x.line + 1, offset: x.character + 1 };
|
||||
};
|
||||
}
|
||||
|
||||
export function protocolTextSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): protocol.TextSpan {
|
||||
const span = textSpanFromSubstring(str, substring, options);
|
||||
const toLocation = protocolToLocation(str);
|
||||
return { start: toLocation(span.start), end: toLocation(textSpanEnd(span)) };
|
||||
}
|
||||
//function protocolRenameSpanFromSubstring(
|
||||
// str: string,
|
||||
// substring: string,
|
||||
@ -492,11 +494,12 @@ namespace ts.projectSystem {
|
||||
//): protocol.RenameTextSpan {
|
||||
// return { ...protocolTextSpanFromSubstring(str, substring, options), ...prefixSuffixText };
|
||||
//}
|
||||
//function textSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): TextSpan {
|
||||
// const start = nthIndexOf(str, substring, options ? options.index : 0);
|
||||
// Debug.assert(start !== -1);
|
||||
// return createTextSpan(start, substring.length);
|
||||
//}
|
||||
|
||||
export function textSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): TextSpan {
|
||||
const start = nthIndexOf(str, substring, options ? options.index : 0);
|
||||
Debug.assert(start !== -1);
|
||||
return createTextSpan(start, substring.length);
|
||||
}
|
||||
//function protocolFileLocationFromSubstring(file: File, substring: string): protocol.FileLocationRequestArgs {
|
||||
// return { file: file.path, ...protocolLocationFromSubstring(file.content, substring) };
|
||||
//}
|
||||
@ -509,18 +512,19 @@ namespace ts.projectSystem {
|
||||
//function renameLocation(file: File, substring: string, options?: SpanFromSubstringOptions): RenameLocation {
|
||||
// return documentSpanFromSubstring(file, substring, options);
|
||||
//}
|
||||
//interface SpanFromSubstringOptions {
|
||||
// readonly index: number;
|
||||
//}
|
||||
|
||||
//function nthIndexOf(str: string, substr: string, n: number): number {
|
||||
// let index = -1;
|
||||
// for (; n >= 0; n--) {
|
||||
// index = str.indexOf(substr, index + 1);
|
||||
// if (index === -1) return -1;
|
||||
// }
|
||||
// return index;
|
||||
//}
|
||||
export interface SpanFromSubstringOptions {
|
||||
readonly index: number;
|
||||
}
|
||||
|
||||
function nthIndexOf(str: string, substr: string, n: number): number {
|
||||
let index = -1;
|
||||
for (; n >= 0; n--) {
|
||||
index = str.indexOf(substr, index + 1);
|
||||
if (index === -1) return -1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test server cancellation token used to mock host token cancellation requests.
|
||||
|
||||
193
src/testRunner/unittests/tsserverProjectLoadingEvents.ts
Normal file
193
src/testRunner/unittests/tsserverProjectLoadingEvents.ts
Normal file
@ -0,0 +1,193 @@
|
||||
namespace ts.projectSystem {
|
||||
describe("tsserverProjectLoadingEvents:: ProjectLoadingStart and ProjectLoadingFinish events", () => {
|
||||
const projectRoot = "/user/username/projects";
|
||||
const aTs: File = {
|
||||
path: `${projectRoot}/a/a.ts`,
|
||||
content: "export class A { }"
|
||||
};
|
||||
const configA: File = {
|
||||
path: `${projectRoot}/a/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const bTsPath = `${projectRoot}/b/b.ts`;
|
||||
const configBPath = `${projectRoot}/b/tsconfig.json`;
|
||||
const files = [libFile, aTs, configA];
|
||||
|
||||
function verifyProjectLoadingStartAndFinish(createSession: (host: TestServerHost) => {
|
||||
session: TestSession;
|
||||
getNumberOfEvents: () => number;
|
||||
clearEvents: () => void;
|
||||
verifyProjectLoadEvents: (expected: [server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent]) => void;
|
||||
}) {
|
||||
function createSessionToVerifyEvent(files: ReadonlyArray<File>) {
|
||||
const host = createServerHost(files);
|
||||
const originalReadFile = host.readFile;
|
||||
const { session, getNumberOfEvents, clearEvents, verifyProjectLoadEvents } = createSession(host);
|
||||
host.readFile = file => {
|
||||
if (file === configA.path || file === configBPath) {
|
||||
assert.equal(getNumberOfEvents(), 1, "Event for loading is sent before reading config file");
|
||||
}
|
||||
return originalReadFile.call(host, file);
|
||||
};
|
||||
const service = session.getProjectService();
|
||||
return { host, session, verifyEvent, verifyEventWithOpenTs, service, getNumberOfEvents };
|
||||
|
||||
function verifyEvent(project: server.Project, reason: string) {
|
||||
verifyProjectLoadEvents([
|
||||
{ eventName: server.ProjectLoadingStartEvent, data: { project, reason } },
|
||||
{ eventName: server.ProjectLoadingFinishEvent, data: { project } }
|
||||
]);
|
||||
clearEvents();
|
||||
}
|
||||
|
||||
function verifyEventWithOpenTs(file: File, configPath: string, configuredProjects: number) {
|
||||
openFilesForSession([file], session);
|
||||
checkNumberOfProjects(service, { configuredProjects });
|
||||
const project = service.configuredProjects.get(configPath)!;
|
||||
assert.isDefined(project);
|
||||
verifyEvent(project, `Creating possible configured project for ${file.path} to open`);
|
||||
}
|
||||
}
|
||||
|
||||
it("when project is created by open file", () => {
|
||||
const bTs: File = {
|
||||
path: bTsPath,
|
||||
content: "export class B {}"
|
||||
};
|
||||
const configB: File = {
|
||||
path: configBPath,
|
||||
content: "{}"
|
||||
};
|
||||
const { verifyEventWithOpenTs } = createSessionToVerifyEvent(files.concat(bTs, configB));
|
||||
verifyEventWithOpenTs(aTs, configA.path, 1);
|
||||
verifyEventWithOpenTs(bTs, configB.path, 2);
|
||||
});
|
||||
|
||||
it("when change is detected in the config file", () => {
|
||||
const { host, verifyEvent, verifyEventWithOpenTs, service } = createSessionToVerifyEvent(files);
|
||||
verifyEventWithOpenTs(aTs, configA.path, 1);
|
||||
|
||||
host.writeFile(configA.path, configA.content);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
const project = service.configuredProjects.get(configA.path)!;
|
||||
verifyEvent(project, `Change in config file detected`);
|
||||
});
|
||||
|
||||
it("when opening original location project", () => {
|
||||
const aDTs: File = {
|
||||
path: `${projectRoot}/a/a.d.ts`,
|
||||
content: `export declare class A {
|
||||
}
|
||||
//# sourceMappingURL=a.d.ts.map
|
||||
`
|
||||
};
|
||||
const aDTsMap: File = {
|
||||
path: `${projectRoot}/a/a.d.ts.map`,
|
||||
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
|
||||
};
|
||||
const bTs: File = {
|
||||
path: bTsPath,
|
||||
content: `import {A} from "../a/a"; new A();`
|
||||
};
|
||||
const configB: File = {
|
||||
path: configBPath,
|
||||
content: JSON.stringify({
|
||||
references: [{ path: "../a" }]
|
||||
})
|
||||
};
|
||||
|
||||
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
|
||||
verifyEventWithOpenTs(bTs, configB.path, 1);
|
||||
|
||||
session.executeCommandSeq<protocol.ReferencesRequest>({
|
||||
command: protocol.CommandTypes.References,
|
||||
arguments: {
|
||||
file: bTs.path,
|
||||
...protocolLocationFromSubstring(bTs.content, "A()")
|
||||
}
|
||||
});
|
||||
|
||||
checkNumberOfProjects(service, { configuredProjects: 2 });
|
||||
const project = service.configuredProjects.get(configA.path)!;
|
||||
assert.isDefined(project);
|
||||
verifyEvent(project, `Creating project for original file: ${aTs.path} for location: ${aDTs.path}`);
|
||||
});
|
||||
|
||||
describe("with external projects and config files ", () => {
|
||||
const projectFileName = `${projectRoot}/a/project.csproj`;
|
||||
|
||||
function createSession(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const { session, service, verifyEvent: verifyEventWorker, getNumberOfEvents } = createSessionToVerifyEvent(files);
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([aTs.path, configA.path]),
|
||||
options: {}
|
||||
});
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
return { session, service, verifyEvent, getNumberOfEvents };
|
||||
|
||||
function verifyEvent() {
|
||||
const projectA = service.configuredProjects.get(configA.path)!;
|
||||
assert.isDefined(projectA);
|
||||
verifyEventWorker(projectA, `Creating configured project in external project: ${projectFileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject is false", () => {
|
||||
const { verifyEvent } = createSession(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
verifyEvent();
|
||||
});
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject is true and file is opened", () => {
|
||||
const { verifyEvent, getNumberOfEvents, session } = createSession(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
assert.equal(getNumberOfEvents(), 0);
|
||||
|
||||
openFilesForSession([aTs], session);
|
||||
verifyEvent();
|
||||
});
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject is disabled", () => {
|
||||
const { verifyEvent, getNumberOfEvents, service } = createSession(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
assert.equal(getNumberOfEvents(), 0);
|
||||
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: false } });
|
||||
verifyEvent();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("when using event handler", () => {
|
||||
verifyProjectLoadingStartAndFinish(host => {
|
||||
const { session, events } = createSessionWithEventTracking<server.ProjectLoadingStartEvent | server.ProjectLoadingFinishEvent>(host, server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent);
|
||||
return {
|
||||
session,
|
||||
getNumberOfEvents: () => events.length,
|
||||
clearEvents: () => events.length = 0,
|
||||
verifyProjectLoadEvents: expected => assert.deepEqual(events, expected)
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
describe("when using default event handler", () => {
|
||||
verifyProjectLoadingStartAndFinish(host => {
|
||||
const { session, getEvents, clearEvents } = createSessionWithDefaultEventHandler<protocol.ProjectLoadingStartEvent | protocol.ProjectLoadingFinishEvent>(host, [server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent]);
|
||||
return {
|
||||
session,
|
||||
getNumberOfEvents: () => getEvents().length,
|
||||
clearEvents,
|
||||
verifyProjectLoadEvents
|
||||
};
|
||||
|
||||
function verifyProjectLoadEvents(expected: [server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent]) {
|
||||
const actual = getEvents().map(e => ({ eventName: e.event, data: e.body }));
|
||||
const mappedExpected = expected.map(e => {
|
||||
const { project, ...rest } = e.data;
|
||||
return { eventName: e.eventName, data: { projectName: project.getProjectName(), ...rest } };
|
||||
});
|
||||
assert.deepEqual(actual, mappedExpected);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1,46 +1,4 @@
|
||||
namespace ts.projectSystem {
|
||||
const outputEventRegex = /Content\-Length: [\d]+\r\n\r\n/;
|
||||
function mapOutputToJson(s: string) {
|
||||
return convertToObject(
|
||||
parseJsonText("json.json", s.replace(outputEventRegex, "")),
|
||||
[]
|
||||
);
|
||||
}
|
||||
|
||||
function createSessionWithEventTracking<T extends server.ProjectServiceEvent>(host: server.ServerHost, eventName: T["eventName"], ...eventNames: T["eventName"][]) {
|
||||
const events: T[] = [];
|
||||
const session = createSession(host, {
|
||||
eventHandler: e => {
|
||||
if (e.eventName === eventName || eventNames.some(eventName => e.eventName === eventName)) {
|
||||
events.push(e as T);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { session, events };
|
||||
}
|
||||
|
||||
function createSessionWithDefaultEventHandler<T extends protocol.AnyEvent>(host: TestServerHost, eventNames: T["event"] | T["event"][], opts: Partial<server.SessionOptions> = {}) {
|
||||
const session = createSession(host, { canUseEvents: true, ...opts });
|
||||
|
||||
return {
|
||||
session,
|
||||
getEvents,
|
||||
clearEvents
|
||||
};
|
||||
|
||||
function getEvents() {
|
||||
return mapDefined(host.getOutput(), s => {
|
||||
const e = mapOutputToJson(s);
|
||||
return (isArray(eventNames) ? eventNames.some(eventName => e.event === eventName) : e.event === eventNames) ? e as T : undefined;
|
||||
});
|
||||
}
|
||||
|
||||
function clearEvents() {
|
||||
session.clearMessages();
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeModuleDirectories(dir: string) {
|
||||
return getRootsToWatchWithAncestorDirectory(dir, nodeModules);
|
||||
}
|
||||
@ -57,23 +15,6 @@ namespace ts.projectSystem {
|
||||
checkArray("ScriptInfos files", arrayFrom(projectService.filenameToScriptInfo.values(), info => info.fileName), expectedFiles);
|
||||
}
|
||||
|
||||
function protocolLocationFromSubstring(str: string, substring: string): protocol.Location {
|
||||
const start = str.indexOf(substring);
|
||||
Debug.assert(start !== -1);
|
||||
return protocolToLocation(str)(start);
|
||||
}
|
||||
function protocolToLocation(text: string): (pos: number) => protocol.Location {
|
||||
const lineStarts = computeLineStarts(text);
|
||||
return pos => {
|
||||
const x = computeLineAndCharacterOfPosition(lineStarts, pos);
|
||||
return { line: x.line + 1, offset: x.character + 1 };
|
||||
};
|
||||
}
|
||||
function protocolTextSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): protocol.TextSpan {
|
||||
const span = textSpanFromSubstring(str, substring, options);
|
||||
const toLocation = protocolToLocation(str);
|
||||
return { start: toLocation(span.start), end: toLocation(textSpanEnd(span)) };
|
||||
}
|
||||
function protocolRenameSpanFromSubstring(
|
||||
str: string,
|
||||
substring: string,
|
||||
@ -82,11 +23,6 @@ namespace ts.projectSystem {
|
||||
): protocol.RenameTextSpan {
|
||||
return { ...protocolTextSpanFromSubstring(str, substring, options), ...prefixSuffixText };
|
||||
}
|
||||
function textSpanFromSubstring(str: string, substring: string, options?: SpanFromSubstringOptions): TextSpan {
|
||||
const start = nthIndexOf(str, substring, options ? options.index : 0);
|
||||
Debug.assert(start !== -1);
|
||||
return createTextSpan(start, substring.length);
|
||||
}
|
||||
function protocolFileLocationFromSubstring(file: File, substring: string): protocol.FileLocationRequestArgs {
|
||||
return { file: file.path, ...protocolLocationFromSubstring(file.content, substring) };
|
||||
}
|
||||
@ -99,18 +35,6 @@ namespace ts.projectSystem {
|
||||
function renameLocation(file: File, substring: string, options?: SpanFromSubstringOptions): RenameLocation {
|
||||
return documentSpanFromSubstring(file, substring, options);
|
||||
}
|
||||
interface SpanFromSubstringOptions {
|
||||
readonly index: number;
|
||||
}
|
||||
|
||||
function nthIndexOf(str: string, substr: string, n: number): number {
|
||||
let index = -1;
|
||||
for (; n >= 0; n--) {
|
||||
index = str.indexOf(substr, index + 1);
|
||||
if (index === -1) return -1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
interface ErrorInformation {
|
||||
diagnosticMessage: DiagnosticMessage;
|
||||
@ -7843,198 +7767,6 @@ new C();`
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem ProjectLoadingStart and ProjectLoadingFinish events", () => {
|
||||
const projectRoot = "/user/username/projects";
|
||||
const aTs: File = {
|
||||
path: `${projectRoot}/a/a.ts`,
|
||||
content: "export class A { }"
|
||||
};
|
||||
const configA: File = {
|
||||
path: `${projectRoot}/a/tsconfig.json`,
|
||||
content: "{}"
|
||||
};
|
||||
const bTsPath = `${projectRoot}/b/b.ts`;
|
||||
const configBPath = `${projectRoot}/b/tsconfig.json`;
|
||||
const files = [libFile, aTs, configA];
|
||||
|
||||
function verifyProjectLoadingStartAndFinish(createSession: (host: TestServerHost) => {
|
||||
session: TestSession;
|
||||
getNumberOfEvents: () => number;
|
||||
clearEvents: () => void;
|
||||
verifyProjectLoadEvents: (expected: [server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent]) => void;
|
||||
}) {
|
||||
function createSessionToVerifyEvent(files: ReadonlyArray<File>) {
|
||||
const host = createServerHost(files);
|
||||
const originalReadFile = host.readFile;
|
||||
const { session, getNumberOfEvents, clearEvents, verifyProjectLoadEvents } = createSession(host);
|
||||
host.readFile = file => {
|
||||
if (file === configA.path || file === configBPath) {
|
||||
assert.equal(getNumberOfEvents(), 1, "Event for loading is sent before reading config file");
|
||||
}
|
||||
return originalReadFile.call(host, file);
|
||||
};
|
||||
const service = session.getProjectService();
|
||||
return { host, session, verifyEvent, verifyEventWithOpenTs, service, getNumberOfEvents };
|
||||
|
||||
function verifyEvent(project: server.Project, reason: string) {
|
||||
verifyProjectLoadEvents([
|
||||
{ eventName: server.ProjectLoadingStartEvent, data: { project, reason } },
|
||||
{ eventName: server.ProjectLoadingFinishEvent, data: { project } }
|
||||
]);
|
||||
clearEvents();
|
||||
}
|
||||
|
||||
function verifyEventWithOpenTs(file: File, configPath: string, configuredProjects: number) {
|
||||
openFilesForSession([file], session);
|
||||
checkNumberOfProjects(service, { configuredProjects });
|
||||
const project = service.configuredProjects.get(configPath)!;
|
||||
assert.isDefined(project);
|
||||
verifyEvent(project, `Creating possible configured project for ${file.path} to open`);
|
||||
}
|
||||
}
|
||||
|
||||
it("when project is created by open file", () => {
|
||||
const bTs: File = {
|
||||
path: bTsPath,
|
||||
content: "export class B {}"
|
||||
};
|
||||
const configB: File = {
|
||||
path: configBPath,
|
||||
content: "{}"
|
||||
};
|
||||
const { verifyEventWithOpenTs } = createSessionToVerifyEvent(files.concat(bTs, configB));
|
||||
verifyEventWithOpenTs(aTs, configA.path, 1);
|
||||
verifyEventWithOpenTs(bTs, configB.path, 2);
|
||||
});
|
||||
|
||||
it("when change is detected in the config file", () => {
|
||||
const { host, verifyEvent, verifyEventWithOpenTs, service } = createSessionToVerifyEvent(files);
|
||||
verifyEventWithOpenTs(aTs, configA.path, 1);
|
||||
|
||||
host.writeFile(configA.path, configA.content);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
const project = service.configuredProjects.get(configA.path)!;
|
||||
verifyEvent(project, `Change in config file detected`);
|
||||
});
|
||||
|
||||
it("when opening original location project", () => {
|
||||
const aDTs: File = {
|
||||
path: `${projectRoot}/a/a.d.ts`,
|
||||
content: `export declare class A {
|
||||
}
|
||||
//# sourceMappingURL=a.d.ts.map
|
||||
`
|
||||
};
|
||||
const aDTsMap: File = {
|
||||
path: `${projectRoot}/a/a.d.ts.map`,
|
||||
content: `{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["./a.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;CAAI"}`
|
||||
};
|
||||
const bTs: File = {
|
||||
path: bTsPath,
|
||||
content: `import {A} from "../a/a"; new A();`
|
||||
};
|
||||
const configB: File = {
|
||||
path: configBPath,
|
||||
content: JSON.stringify({
|
||||
references: [{ path: "../a" }]
|
||||
})
|
||||
};
|
||||
|
||||
const { service, session, verifyEventWithOpenTs, verifyEvent } = createSessionToVerifyEvent(files.concat(aDTs, aDTsMap, bTs, configB));
|
||||
verifyEventWithOpenTs(bTs, configB.path, 1);
|
||||
|
||||
session.executeCommandSeq<protocol.ReferencesRequest>({
|
||||
command: protocol.CommandTypes.References,
|
||||
arguments: {
|
||||
file: bTs.path,
|
||||
...protocolLocationFromSubstring(bTs.content, "A()")
|
||||
}
|
||||
});
|
||||
|
||||
checkNumberOfProjects(service, { configuredProjects: 2 });
|
||||
const project = service.configuredProjects.get(configA.path)!;
|
||||
assert.isDefined(project);
|
||||
verifyEvent(project, `Creating project for original file: ${aTs.path} for location: ${aDTs.path}`);
|
||||
});
|
||||
|
||||
describe("with external projects and config files ", () => {
|
||||
const projectFileName = `${projectRoot}/a/project.csproj`;
|
||||
|
||||
function createSession(lazyConfiguredProjectsFromExternalProject: boolean) {
|
||||
const { session, service, verifyEvent: verifyEventWorker, getNumberOfEvents } = createSessionToVerifyEvent(files);
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
|
||||
service.openExternalProject(<protocol.ExternalProject>{
|
||||
projectFileName,
|
||||
rootFiles: toExternalFiles([aTs.path, configA.path]),
|
||||
options: {}
|
||||
});
|
||||
checkNumberOfProjects(service, { configuredProjects: 1 });
|
||||
return { session, service, verifyEvent, getNumberOfEvents };
|
||||
|
||||
function verifyEvent() {
|
||||
const projectA = service.configuredProjects.get(configA.path)!;
|
||||
assert.isDefined(projectA);
|
||||
verifyEventWorker(projectA, `Creating configured project in external project: ${projectFileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject is false", () => {
|
||||
const { verifyEvent } = createSession(/*lazyConfiguredProjectsFromExternalProject*/ false);
|
||||
verifyEvent();
|
||||
});
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject is true and file is opened", () => {
|
||||
const { verifyEvent, getNumberOfEvents, session } = createSession(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
assert.equal(getNumberOfEvents(), 0);
|
||||
|
||||
openFilesForSession([aTs], session);
|
||||
verifyEvent();
|
||||
});
|
||||
|
||||
it("when lazyConfiguredProjectsFromExternalProject is disabled", () => {
|
||||
const { verifyEvent, getNumberOfEvents, service } = createSession(/*lazyConfiguredProjectsFromExternalProject*/ true);
|
||||
assert.equal(getNumberOfEvents(), 0);
|
||||
|
||||
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject: false } });
|
||||
verifyEvent();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe("when using event handler", () => {
|
||||
verifyProjectLoadingStartAndFinish(host => {
|
||||
const { session, events } = createSessionWithEventTracking<server.ProjectLoadingStartEvent | server.ProjectLoadingFinishEvent>(host, server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent);
|
||||
return {
|
||||
session,
|
||||
getNumberOfEvents: () => events.length,
|
||||
clearEvents: () => events.length = 0,
|
||||
verifyProjectLoadEvents: expected => assert.deepEqual(events, expected)
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
describe("when using default event handler", () => {
|
||||
verifyProjectLoadingStartAndFinish(host => {
|
||||
const { session, getEvents, clearEvents } = createSessionWithDefaultEventHandler<protocol.ProjectLoadingStartEvent | protocol.ProjectLoadingFinishEvent>(host, [server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent]);
|
||||
return {
|
||||
session,
|
||||
getNumberOfEvents: () => getEvents().length,
|
||||
clearEvents,
|
||||
verifyProjectLoadEvents
|
||||
};
|
||||
|
||||
function verifyProjectLoadEvents(expected: [server.ProjectLoadingStartEvent, server.ProjectLoadingFinishEvent]) {
|
||||
const actual = getEvents().map(e => ({ eventName: e.event, data: e.body }));
|
||||
const mappedExpected = expected.map(e => {
|
||||
const { project, ...rest } = e.data;
|
||||
return { eventName: e.eventName, data: { projectName: project.getProjectName(), ...rest } };
|
||||
});
|
||||
assert.deepEqual(actual, mappedExpected);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("tsserverProjectSystem syntax operations", () => {
|
||||
function navBarFull(session: TestSession, file: File) {
|
||||
return JSON.stringify(session.executeCommandSeq<protocol.FileRequest>({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user