More tsserver tests refactoring

This commit is contained in:
Sheetal Nandi
2018-12-20 14:00:45 -08:00
parent db4d9b3050
commit f117184562
10 changed files with 3294 additions and 3282 deletions

View File

@@ -61,7 +61,6 @@
"unittests/tsbuild.ts",
"unittests/tsbuildWatchMode.ts",
"unittests/tscWatchMode.ts",
"unittests/tsserverProjectSystem.ts",
"unittests/config/commandLineParsing.ts",
"unittests/config/configurationExtension.ts",
"unittests/config/convertCompilerOptionsFromJson.ts",
@@ -98,15 +97,19 @@
"unittests/tsserver/compileOnSave.ts",
"unittests/tsserver/completions.ts",
"unittests/tsserver/configFileSearch.ts",
"unittests/tsserver/configuredProjects.ts",
"unittests/tsserver/declarationFileMaps.ts",
"unittests/tsserver/documentRegistry.ts",
"unittests/tsserver/duplicatePackages.ts",
"unittests/tsserver/events/largeFileReferenced.ts",
"unittests/tsserver/events/projectLanguageServiceState.ts",
"unittests/tsserver/events/projectLoading.ts",
"unittests/tsserver/events/projectUpdatedInBackground.ts",
"unittests/tsserver/events/surveyReady.ts",
"unittests/tsserver/externalProjects.ts",
"unittests/tsserver/forceConsistentCasingInFileNames.ts",
"unittests/tsserver/formatSettings.ts",
"unittests/tsserver/getApplicableRefactors.ts",
"unittests/tsserver/getEditsForFileRename.ts",
"unittests/tsserver/importHelpers.ts",
"unittests/tsserver/inferredProjects.ts",
@@ -118,6 +121,7 @@
"unittests/tsserver/openFile.ts",
"unittests/tsserver/projectErrors.ts",
"unittests/tsserver/projectReferences.ts",
"unittests/tsserver/projects.ts",
"unittests/tsserver/refactors.ts",
"unittests/tsserver/reload.ts",
"unittests/tsserver/rename.ts",

View File

@@ -695,4 +695,99 @@ namespace ts.projectSystem {
});
});
describe("unittests:: tsserver:: compileOnSave:: CompileOnSaveAffectedFileListRequest with and without projectFileName in request", () => {
const projectRoot = "/user/username/projects/myproject";
const core: File = {
path: `${projectRoot}/core/core.ts`,
content: "let z = 10;"
};
const app1: File = {
path: `${projectRoot}/app1/app.ts`,
content: "let x = 10;"
};
const app2: File = {
path: `${projectRoot}/app2/app.ts`,
content: "let y = 10;"
};
const app1Config: File = {
path: `${projectRoot}/app1/tsconfig.json`,
content: JSON.stringify({
files: ["app.ts", "../core/core.ts"],
compilerOptions: { outFile: "build/output.js" },
compileOnSave: true
})
};
const app2Config: File = {
path: `${projectRoot}/app2/tsconfig.json`,
content: JSON.stringify({
files: ["app.ts", "../core/core.ts"],
compilerOptions: { outFile: "build/output.js" },
compileOnSave: true
})
};
const files = [libFile, core, app1, app2, app1Config, app2Config];
function insertString(session: TestSession, file: File) {
session.executeCommandSeq<protocol.ChangeRequest>({
command: protocol.CommandTypes.Change,
arguments: {
file: file.path,
line: 1,
offset: 1,
endLine: 1,
endOffset: 1,
insertString: "let k = 1"
}
});
}
function getSession() {
const host = createServerHost(files);
const session = createSession(host);
openFilesForSession([app1, app2, core], session);
const service = session.getProjectService();
checkNumberOfProjects(session.getProjectService(), { configuredProjects: 2 });
const project1 = service.configuredProjects.get(app1Config.path)!;
const project2 = service.configuredProjects.get(app2Config.path)!;
checkProjectActualFiles(project1, [libFile.path, app1.path, core.path, app1Config.path]);
checkProjectActualFiles(project2, [libFile.path, app2.path, core.path, app2Config.path]);
insertString(session, app1);
insertString(session, app2);
assert.equal(project1.dirty, true);
assert.equal(project2.dirty, true);
return session;
}
it("when projectFile is specified", () => {
const session = getSession();
const response = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
arguments: {
file: core.path,
projectFileName: app1Config.path
}
}).response;
assert.deepEqual(response, [
{ projectFileName: app1Config.path, fileNames: [core.path, app1.path], projectUsesOutFile: true }
]);
assert.equal(session.getProjectService().configuredProjects.get(app1Config.path)!.dirty, false);
assert.equal(session.getProjectService().configuredProjects.get(app2Config.path)!.dirty, true);
});
it("when projectFile is not specified", () => {
const session = getSession();
const response = session.executeCommandSeq<protocol.CompileOnSaveAffectedFileListRequest>({
command: protocol.CommandTypes.CompileOnSaveAffectedFileList,
arguments: {
file: core.path
}
}).response;
assert.deepEqual(response, [
{ projectFileName: app1Config.path, fileNames: [core.path, app1.path], projectUsesOutFile: true },
{ projectFileName: app2Config.path, fileNames: [core.path, app2.path], projectUsesOutFile: true }
]);
assert.equal(session.getProjectService().configuredProjects.get(app1Config.path)!.dirty, false);
assert.equal(session.getProjectService().configuredProjects.get(app2Config.path)!.dirty, false);
});
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: events:: ProjectLanguageServiceStateEvent", () => {
it("language service disabled events are triggered", () => {
const f1 = {
path: "/a/app.js",
content: "let x = 1;"
};
const f2 = {
path: "/a/largefile.js",
content: ""
};
const config = {
path: "/a/jsconfig.json",
content: "{}"
};
const configWithExclude = {
path: config.path,
content: JSON.stringify({ exclude: ["largefile.js"] })
};
const host = createServerHost([f1, f2, config]);
const originalGetFileSize = host.getFileSize;
host.getFileSize = (filePath: string) =>
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
const { session, events } = createSessionWithEventTracking<server.ProjectLanguageServiceStateEvent>(host, server.ProjectLanguageServiceStateEvent);
session.executeCommand(<protocol.OpenRequest>{
seq: 0,
type: "request",
command: "open",
arguments: { file: f1.path }
});
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const project = configuredProjectAt(projectService, 0);
assert.isFalse(project.languageServiceEnabled, "Language service enabled");
assert.equal(events.length, 1, "should receive event");
assert.equal(events[0].data.project, project, "project name");
assert.equal(events[0].data.project.getProjectName(), config.path, "config path");
assert.isFalse(events[0].data.languageServiceEnabled, "Language service state");
host.reloadFS([f1, f2, configWithExclude]);
host.checkTimeoutQueueLengthAndRun(2);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
assert.isTrue(project.languageServiceEnabled, "Language service enabled");
assert.equal(events.length, 2, "should receive event");
assert.equal(events[1].data.project, project, "project");
assert.equal(events[1].data.project.getProjectName(), config.path, "config path");
assert.isTrue(events[1].data.languageServiceEnabled, "Language service state");
});
});
}

View File

@@ -0,0 +1,111 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: events:: SurveyReady", () => {
function createSessionWithEventHandler(host: TestServerHost) {
const { session, events: surveyEvents } = createSessionWithEventTracking<server.SurveyReady>(host, server.SurveyReady);
return { session, verifySurveyReadyEvent };
function verifySurveyReadyEvent(numberOfEvents: number) {
assert.equal(surveyEvents.length, numberOfEvents);
const expectedEvents = numberOfEvents === 0 ? [] : [{
eventName: server.SurveyReady,
data: { surveyId: "checkJs" }
}];
assert.deepEqual(surveyEvents, expectedEvents);
}
}
it("doesn't log an event when checkJs isn't set", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: {} }),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
const service = session.getProjectService();
openFilesForSession([file], session);
checkNumberOfProjects(service, { configuredProjects: 1 });
const project = service.configuredProjects.get(tsconfig.path)!;
checkProjectActualFiles(project, [file.path, tsconfig.path]);
verifySurveyReadyEvent(0);
});
it("logs an event when checkJs is set", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { checkJs: true } }),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
});
it("logs an event when checkJs is set, only the first time", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const rando: File = {
path: `/rando/calrissian.ts`,
content: "export function f() { }"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({ compilerOptions: { checkJs: true } }),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
closeFilesForSession([file], session);
openFilesForSession([rando], session);
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
});
it("logs an event when checkJs is set after closing and reopening", () => {
const projectRoot = "/user/username/projects/project";
const file: File = {
path: `${projectRoot}/src/file.ts`,
content: "export var y = 10;"
};
const rando: File = {
path: `/rando/calrissian.ts`,
content: "export function f() { }"
};
const tsconfig: File = {
path: `${projectRoot}/tsconfig.json`,
content: JSON.stringify({}),
};
const host = createServerHost([file, tsconfig]);
const { session, verifySurveyReadyEvent } = createSessionWithEventHandler(host);
openFilesForSession([file], session);
verifySurveyReadyEvent(0);
closeFilesForSession([file], session);
openFilesForSession([rando], session);
host.writeFile(tsconfig.path, JSON.stringify({ compilerOptions: { checkJs: true } }));
openFilesForSession([file], session);
verifySurveyReadyEvent(1);
});
});
}

View File

@@ -1,5 +1,469 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: ExternalProjects", () => {
describe("can handle tsconfig file name with difference casing", () => {
function verifyConfigFileCasing(lazyConfiguredProjectsFromExternalProject: boolean) {
const f1 = {
path: "/a/b/app.ts",
content: "let x = 1"
};
const config = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({
include: []
})
};
const host = createServerHost([f1, config], { useCaseSensitiveFileNames: false });
const service = createProjectService(host);
service.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path));
service.openExternalProject(<protocol.ExternalProject>{
projectFileName: "/a/b/project.csproj",
rootFiles: toExternalFiles([f1.path, upperCaseConfigFilePath]),
options: {}
});
service.checkNumberOfProjects({ configuredProjects: 1 });
const project = service.configuredProjects.get(config.path)!;
if (lazyConfiguredProjectsFromExternalProject) {
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.Full); // External project referenced configured project pending to be reloaded
checkProjectActualFiles(project, emptyArray);
}
else {
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project loaded
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
}
service.openClientFile(f1.path);
service.checkNumberOfProjects({ configuredProjects: 1, inferredProjects: 1 });
assert.equal(project.pendingReload, ConfigFileProgramReloadLevel.None); // External project referenced configured project is updated
checkProjectActualFiles(project, [upperCaseConfigFilePath]);
checkProjectActualFiles(service.inferredProjects[0], [f1.path]);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyConfigFileCasing(/*lazyConfiguredProjectsFromExternalProject*/ false);
});
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
verifyConfigFileCasing(/*lazyConfiguredProjectsFromExternalProject*/ true);
});
});
it("remove not-listed external projects", () => {
const f1 = {
path: "/a/app.ts",
content: "let x = 1"
};
const f2 = {
path: "/b/app.ts",
content: "let x = 1"
};
const f3 = {
path: "/c/app.ts",
content: "let x = 1"
};
const makeProject = (f: File) => ({ projectFileName: f.path + ".csproj", rootFiles: [toExternalFile(f.path)], options: {} });
const p1 = makeProject(f1);
const p2 = makeProject(f2);
const p3 = makeProject(f3);
const host = createServerHost([f1, f2, f3]);
const session = createSession(host);
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
seq: 1,
type: "request",
command: "openExternalProjects",
arguments: { projects: [p1, p2] }
});
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { externalProjects: 2 });
assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName);
assert.equal(projectService.externalProjects[1].getProjectName(), p2.projectFileName);
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
seq: 2,
type: "request",
command: "openExternalProjects",
arguments: { projects: [p1, p3] }
});
checkNumberOfProjects(projectService, { externalProjects: 2 });
assert.equal(projectService.externalProjects[0].getProjectName(), p1.projectFileName);
assert.equal(projectService.externalProjects[1].getProjectName(), p3.projectFileName);
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
seq: 3,
type: "request",
command: "openExternalProjects",
arguments: { projects: [] }
});
checkNumberOfProjects(projectService, { externalProjects: 0 });
session.executeCommand(<protocol.OpenExternalProjectsRequest>{
seq: 3,
type: "request",
command: "openExternalProjects",
arguments: { projects: [p2] }
});
assert.equal(projectService.externalProjects[0].getProjectName(), p2.projectFileName);
});
it("should not close external project with no open files", () => {
const file1 = {
path: "/a/b/f1.ts",
content: "let x =1;"
};
const file2 = {
path: "/a/b/f2.ts",
content: "let y =1;"
};
const externalProjectName = "externalproject";
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host);
projectService.openExternalProject({
rootFiles: toExternalFiles([file1.path, file2.path]),
options: {},
projectFileName: externalProjectName
});
checkNumberOfExternalProjects(projectService, 1);
checkNumberOfInferredProjects(projectService, 0);
// open client file - should not lead to creation of inferred project
projectService.openClientFile(file1.path, file1.content);
checkNumberOfExternalProjects(projectService, 1);
checkNumberOfInferredProjects(projectService, 0);
// close client file - external project should still exists
projectService.closeClientFile(file1.path);
checkNumberOfExternalProjects(projectService, 1);
checkNumberOfInferredProjects(projectService, 0);
projectService.closeExternalProject(externalProjectName);
checkNumberOfExternalProjects(projectService, 0);
checkNumberOfInferredProjects(projectService, 0);
});
it("external project for dynamic file", () => {
const externalProjectName = "^ScriptDocument1 file1.ts";
const externalFiles = toExternalFiles(["^ScriptDocument1 file1.ts"]);
const host = createServerHost([]);
const projectService = createProjectService(host);
projectService.openExternalProject({
rootFiles: externalFiles,
options: {},
projectFileName: externalProjectName
});
checkNumberOfExternalProjects(projectService, 1);
checkNumberOfInferredProjects(projectService, 0);
externalFiles[0].content = "let x =1;";
projectService.applyChangesInOpenFiles(externalFiles, [], []);
});
it("external project that included config files", () => {
const file1 = {
path: "/a/b/f1.ts",
content: "let x =1;"
};
const config1 = {
path: "/a/b/tsconfig.json",
content: JSON.stringify(
{
compilerOptions: {},
files: ["f1.ts"]
}
)
};
const file2 = {
path: "/a/c/f2.ts",
content: "let y =1;"
};
const config2 = {
path: "/a/c/tsconfig.json",
content: JSON.stringify(
{
compilerOptions: {},
files: ["f2.ts"]
}
)
};
const file3 = {
path: "/a/d/f3.ts",
content: "let z =1;"
};
const externalProjectName = "externalproject";
const host = createServerHost([file1, file2, file3, config1, config2]);
const projectService = createProjectService(host);
projectService.openExternalProject({
rootFiles: toExternalFiles([config1.path, config2.path, file3.path]),
options: {},
projectFileName: externalProjectName
});
checkNumberOfProjects(projectService, { configuredProjects: 2 });
const proj1 = projectService.configuredProjects.get(config1.path);
const proj2 = projectService.configuredProjects.get(config2.path);
assert.isDefined(proj1);
assert.isDefined(proj2);
// open client file - should not lead to creation of inferred project
projectService.openClientFile(file1.path, file1.content);
checkNumberOfProjects(projectService, { configuredProjects: 2 });
assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1);
assert.strictEqual(projectService.configuredProjects.get(config2.path), proj2);
projectService.openClientFile(file3.path, file3.content);
checkNumberOfProjects(projectService, { configuredProjects: 2, inferredProjects: 1 });
assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1);
assert.strictEqual(projectService.configuredProjects.get(config2.path), proj2);
projectService.closeExternalProject(externalProjectName);
// open file 'file1' from configured project keeps project alive
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1);
assert.isUndefined(projectService.configuredProjects.get(config2.path));
projectService.closeClientFile(file3.path);
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1);
assert.isUndefined(projectService.configuredProjects.get(config2.path));
assert.isTrue(projectService.inferredProjects[0].isOrphan());
projectService.closeClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1, inferredProjects: 1 });
assert.strictEqual(projectService.configuredProjects.get(config1.path), proj1);
assert.isUndefined(projectService.configuredProjects.get(config2.path));
assert.isTrue(projectService.inferredProjects[0].isOrphan());
projectService.openClientFile(file2.path, file2.content);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
assert.isUndefined(projectService.configuredProjects.get(config1.path));
assert.isDefined(projectService.configuredProjects.get(config2.path));
});
it("external project with included config file opened after configured project", () => {
const file1 = {
path: "/a/b/f1.ts",
content: "let x = 1"
};
const configFile = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ compilerOptions: {} })
};
const externalProjectName = "externalproject";
const host = createServerHost([file1, configFile]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
projectService.openExternalProject({
rootFiles: toExternalFiles([configFile.path]),
options: {},
projectFileName: externalProjectName
});
checkNumberOfProjects(projectService, { configuredProjects: 1 });
projectService.closeClientFile(file1.path);
// configured project is alive since it is opened as part of external project
checkNumberOfProjects(projectService, { configuredProjects: 1 });
projectService.closeExternalProject(externalProjectName);
checkNumberOfProjects(projectService, { configuredProjects: 0 });
});
it("external project with included config file opened after configured project and then closed", () => {
const file1 = {
path: "/a/b/f1.ts",
content: "let x = 1"
};
const file2 = {
path: "/a/f2.ts",
content: "let x = 1"
};
const configFile = {
path: "/a/b/tsconfig.json",
content: JSON.stringify({ compilerOptions: {} })
};
const externalProjectName = "externalproject";
const host = createServerHost([file1, file2, libFile, configFile]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
const project = projectService.configuredProjects.get(configFile.path);
projectService.openExternalProject({
rootFiles: toExternalFiles([configFile.path]),
options: {},
projectFileName: externalProjectName
});
checkNumberOfProjects(projectService, { configuredProjects: 1 });
assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
projectService.closeExternalProject(externalProjectName);
// configured project is alive since file is still open
checkNumberOfProjects(projectService, { configuredProjects: 1 });
assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
projectService.closeClientFile(file1.path);
checkNumberOfProjects(projectService, { configuredProjects: 1 });
assert.strictEqual(projectService.configuredProjects.get(configFile.path), project);
projectService.openClientFile(file2.path);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
assert.isUndefined(projectService.configuredProjects.get(configFile.path));
});
it("can correctly update external project when set of root files has changed", () => {
const file1 = {
path: "/a/b/f1.ts",
content: "let x = 1"
};
const file2 = {
path: "/a/b/f2.ts",
content: "let y = 1"
};
const host = createServerHost([file1, file2]);
const projectService = createProjectService(host);
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path]) });
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectActualFiles(projectService.externalProjects[0], [file1.path]);
projectService.openExternalProject({ projectFileName: "project", options: {}, rootFiles: toExternalFiles([file1.path, file2.path]) });
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectRootFiles(projectService.externalProjects[0], [file1.path, file2.path]);
});
it("can update external project when set of root files was not changed", () => {
const file1 = {
path: "/a/b/f1.ts",
content: `export * from "m"`
};
const file2 = {
path: "/a/b/f2.ts",
content: "export let y = 1"
};
const file3 = {
path: "/a/m.ts",
content: "export let y = 1"
};
const host = createServerHost([file1, file2, file3]);
const projectService = createProjectService(host);
projectService.openExternalProject({ projectFileName: "project", options: { moduleResolution: ModuleResolutionKind.NodeJs }, rootFiles: toExternalFiles([file1.path, file2.path]) });
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectRootFiles(projectService.externalProjects[0], [file1.path, file2.path]);
checkProjectActualFiles(projectService.externalProjects[0], [file1.path, file2.path]);
projectService.openExternalProject({ projectFileName: "project", options: { moduleResolution: ModuleResolutionKind.Classic }, rootFiles: toExternalFiles([file1.path, file2.path]) });
checkNumberOfProjects(projectService, { externalProjects: 1 });
checkProjectRootFiles(projectService.externalProjects[0], [file1.path, file2.path]);
checkProjectActualFiles(projectService.externalProjects[0], [file1.path, file2.path, file3.path]);
});
it("language service disabled state is updated in external projects", () => {
const f1 = {
path: "/a/app.js",
content: "var x = 1"
};
const f2 = {
path: "/a/largefile.js",
content: ""
};
const host = createServerHost([f1, f2]);
const originalGetFileSize = host.getFileSize;
host.getFileSize = (filePath: string) =>
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
const service = createProjectService(host);
const projectFileName = "/a/proj.csproj";
service.openExternalProject({
projectFileName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {}
});
service.checkNumberOfProjects({ externalProjects: 1 });
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 1");
service.openExternalProject({
projectFileName,
rootFiles: toExternalFiles([f1.path]),
options: {}
});
service.checkNumberOfProjects({ externalProjects: 1 });
assert.isTrue(service.externalProjects[0].languageServiceEnabled, "language service should be enabled");
service.openExternalProject({
projectFileName,
rootFiles: toExternalFiles([f1.path, f2.path]),
options: {}
});
service.checkNumberOfProjects({ externalProjects: 1 });
assert.isFalse(service.externalProjects[0].languageServiceEnabled, "language service should be disabled - 2");
});
describe("deleting config file opened from the external project works", () => {
function verifyDeletingConfigFile(lazyConfiguredProjectsFromExternalProject: boolean) {
const site = {
path: "/user/someuser/project/js/site.js",
content: ""
};
const configFile = {
path: "/user/someuser/project/tsconfig.json",
content: "{}"
};
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
const host = createServerHost([libFile, site, configFile]);
const projectService = createProjectService(host);
projectService.setHostConfiguration({ preferences: { lazyConfiguredProjectsFromExternalProject } });
const externalProject: protocol.ExternalProject = {
projectFileName,
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
options: { allowJs: false },
typeAcquisition: { include: [] }
};
projectService.openExternalProjects([externalProject]);
let knownProjects = projectService.synchronizeProjectList([]);
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
const configProject = configuredProjectAt(projectService, 0);
checkProjectActualFiles(configProject, lazyConfiguredProjectsFromExternalProject ?
emptyArray : // Since no files opened from this project, its not loaded
[configFile.path]);
host.reloadFS([libFile, site]);
host.checkTimeoutQueueLengthAndRun(1);
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info!)); // TODO: GH#18217 GH#20039
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
externalProject.rootFiles.length = 1;
projectService.openExternalProjects([externalProject]);
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
}
it("when lazyConfiguredProjectsFromExternalProject not set", () => {
verifyDeletingConfigFile(/*lazyConfiguredProjectsFromExternalProject*/ false);
});
it("when lazyConfiguredProjectsFromExternalProject is set", () => {
verifyDeletingConfigFile(/*lazyConfiguredProjectsFromExternalProject*/ true);
});
});
describe("correctly handling add/remove tsconfig - 1", () => {
function verifyAddRemoveConfig(lazyConfiguredProjectsFromExternalProject: boolean) {
const f1 = {

View File

@@ -0,0 +1,12 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: getApplicableRefactors", () => {
it("works when taking position", () => {
const aTs: File = { path: "/a.ts", content: "" };
const session = createSession(createServerHost([aTs]));
openFilesForSession([aTs], session);
const response = executeSessionRequest<protocol.GetApplicableRefactorsRequest, protocol.GetApplicableRefactorsResponse>(
session, protocol.CommandTypes.GetApplicableRefactors, { file: aTs.path, line: 1, offset: 1 });
assert.deepEqual<ReadonlyArray<protocol.ApplicableRefactorInfo> | undefined>(response, []);
});
});
}

View File

@@ -1,5 +1,125 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: Inferred projects", () => {
it("create inferred project", () => {
const appFile: File = {
path: "/a/b/c/app.ts",
content: `
import {f} from "./module"
console.log(f)
`
};
const moduleFile: File = {
path: "/a/b/c/module.d.ts",
content: `export let x: number`
};
const host = createServerHost([appFile, moduleFile, libFile]);
const projectService = createProjectService(host);
const { configFileName } = projectService.openClientFile(appFile.path);
assert(!configFileName, `should not find config, got: '${configFileName}`);
checkNumberOfConfiguredProjects(projectService, 0);
checkNumberOfInferredProjects(projectService, 1);
const project = projectService.inferredProjects[0];
checkArray("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]);
const configFileLocations = ["/a/b/c/", "/a/b/", "/a/", "/"];
const configFiles = flatMap(configFileLocations, location => [location + "tsconfig.json", location + "jsconfig.json"]);
checkWatchedFiles(host, configFiles.concat(libFile.path, moduleFile.path));
checkWatchedDirectories(host, ["/a/b/c"], /*recursive*/ false);
checkWatchedDirectories(host, [combinePaths(getDirectoryPath(appFile.path), nodeModulesAtTypes)], /*recursive*/ true);
});
it("should use only one inferred project if 'useOneInferredProject' is set", () => {
const file1 = {
path: "/a/b/main.ts",
content: "let x =1;"
};
const configFile: File = {
path: "/a/b/tsconfig.json",
content: `{
"compilerOptions": {
"target": "es6"
},
"files": [ "main.ts" ]
}`
};
const file2 = {
path: "/a/c/main.ts",
content: "let x =1;"
};
const file3 = {
path: "/a/d/main.ts",
content: "let x =1;"
};
const host = createServerHost([file1, file2, file3, libFile]);
const projectService = createProjectService(host, { useSingleInferredProject: true });
projectService.openClientFile(file1.path);
projectService.openClientFile(file2.path);
projectService.openClientFile(file3.path);
checkNumberOfConfiguredProjects(projectService, 0);
checkNumberOfInferredProjects(projectService, 1);
checkProjectActualFiles(projectService.inferredProjects[0], [file1.path, file2.path, file3.path, libFile.path]);
host.reloadFS([file1, configFile, file2, file3, libFile]);
host.checkTimeoutQueueLengthAndRun(2); // load configured project from disk + ensureProjectsForOpenFiles
checkNumberOfConfiguredProjects(projectService, 1);
checkNumberOfInferredProjects(projectService, 1);
checkProjectActualFiles(projectService.inferredProjects[0], [file2.path, file3.path, libFile.path]);
});
it("disable inferred project", () => {
const file1 = {
path: "/a/b/f1.ts",
content: "let x =1;"
};
const host = createServerHost([file1]);
const projectService = createProjectService(host, { useSingleInferredProject: true }, { syntaxOnly: true });
projectService.openClientFile(file1.path, file1.content);
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const proj = projectService.inferredProjects[0];
assert.isDefined(proj);
assert.isFalse(proj.languageServiceEnabled);
});
it("project settings for inferred projects", () => {
const file1 = {
path: "/a/b/app.ts",
content: `import {x} from "mod"`
};
const modFile = {
path: "/a/mod.ts",
content: "export let x: number"
};
const host = createServerHost([file1, modFile]);
const projectService = createProjectService(host);
projectService.openClientFile(file1.path);
projectService.openClientFile(modFile.path);
checkNumberOfProjects(projectService, { inferredProjects: 2 });
const inferredProjects = projectService.inferredProjects.slice();
checkProjectActualFiles(inferredProjects[0], [file1.path]);
checkProjectActualFiles(inferredProjects[1], [modFile.path]);
projectService.setCompilerOptionsForInferredProjects({ moduleResolution: ModuleResolutionKind.Classic });
host.checkTimeoutQueueLengthAndRun(3);
checkNumberOfProjects(projectService, { inferredProjects: 2 });
assert.strictEqual(projectService.inferredProjects[0], inferredProjects[0]);
assert.strictEqual(projectService.inferredProjects[1], inferredProjects[1]);
checkProjectActualFiles(inferredProjects[0], [file1.path, modFile.path]);
assert.isTrue(inferredProjects[1].isOrphan());
});
it("should support files without extensions", () => {
const f = {
path: "/a/compile",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff