mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-11 06:02:53 -05:00
More tsserver tests refactoring
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
1010
src/testRunner/unittests/tsserver/configuredProjects.ts
Normal file
1010
src/testRunner/unittests/tsserver/configuredProjects.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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");
|
||||
});
|
||||
});
|
||||
}
|
||||
111
src/testRunner/unittests/tsserver/events/surveyReady.ts
Normal file
111
src/testRunner/unittests/tsserver/events/surveyReady.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
12
src/testRunner/unittests/tsserver/getApplicableRefactors.ts
Normal file
12
src/testRunner/unittests/tsserver/getApplicableRefactors.ts
Normal 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, []);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
1426
src/testRunner/unittests/tsserver/projects.ts
Normal file
1426
src/testRunner/unittests/tsserver/projects.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user