mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-30 01:04:49 -05:00
add tests for tsserver project system
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace ts {
|
||||
export type FileWatcherCallback = (fileName: string, removed?: boolean) => void;
|
||||
export type DirectoryWatcherCallback = (directoryName: string) => void;
|
||||
export type DirectoryWatcherCallback = (fileName: string) => void;
|
||||
export interface WatchedFile {
|
||||
fileName: string;
|
||||
callback: FileWatcherCallback;
|
||||
|
||||
@@ -107,6 +107,30 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function checkConfiguredProjectNumber(projectService: server.ProjectService, expected: number) {
|
||||
assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`);
|
||||
}
|
||||
|
||||
function checkInferredProjectNumber(projectService: server.ProjectService, expected: number) {
|
||||
assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`);
|
||||
}
|
||||
|
||||
function checkWatchedFiles(host: TestServerHost, expectedFiles: string[]) {
|
||||
checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles);
|
||||
}
|
||||
|
||||
function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[]) {
|
||||
checkMapKeys("watchedDirectories", host.watchedDirectories, expectedDirectories);
|
||||
}
|
||||
|
||||
function checkConfiguredProjectActualFiles(project: server.Project, expectedFiles: string[]) {
|
||||
checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), expectedFiles);
|
||||
}
|
||||
|
||||
function checkConfiguredProjectRootFiles(project: server.Project, expectedFiles: string[]) {
|
||||
checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), expectedFiles);
|
||||
}
|
||||
|
||||
class TestServerHost implements server.ServerHost {
|
||||
args: string[] = [];
|
||||
newLine: "\n";
|
||||
@@ -188,6 +212,26 @@ namespace ts {
|
||||
};
|
||||
}
|
||||
|
||||
triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void {
|
||||
const path = this.toPath(directoryName);
|
||||
const callbacks = lookUp(this.watchedDirectories, path);
|
||||
if (callbacks) {
|
||||
for (const callback of callbacks) {
|
||||
callback.cb(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
triggerFileWatcherCallback(fileName: string): void {
|
||||
const path = this.toPath(fileName);
|
||||
const callbacks = lookUp(this.watchedFiles, path);
|
||||
if (callbacks) {
|
||||
for (const callback of callbacks) {
|
||||
callback(path, /*removed*/ true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watchFile(fileName: string, callback: FileWatcherCallback) {
|
||||
const path = this.toPath(fileName);
|
||||
const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []);
|
||||
@@ -204,7 +248,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// TOOD: record and invoke callbacks to simulate timer events
|
||||
readonly setTimeout = (callback: (...args: any[]) => void, ms: number, ...args: any[]): any => void 0;
|
||||
readonly setTimeout = setTimeout;
|
||||
readonly clearTimeout = (timeoutId: any): void => void 0;
|
||||
readonly readFile = (s: string) => (<File>this.fs.get(this.toPath(s))).content;
|
||||
readonly resolvePath = (s: string) => s;
|
||||
@@ -216,7 +260,20 @@ namespace ts {
|
||||
readonly exit = () => notImplemented();
|
||||
}
|
||||
|
||||
describe("tsserver project system:", () => {
|
||||
describe("tsserver-project-system", () => {
|
||||
const commonFile1: FileOrFolder = {
|
||||
path: "/a/b/commonFile1.ts",
|
||||
content: "let x = 1"
|
||||
};
|
||||
const commonFile2: FileOrFolder = {
|
||||
path: "/a/b/commonFile2.ts",
|
||||
content: "let y = 1"
|
||||
};
|
||||
const libFile: FileOrFolder = {
|
||||
path: "/a/lib/lib.d.ts",
|
||||
content: libFileContent
|
||||
};
|
||||
|
||||
it("create inferred project", () => {
|
||||
const appFile: FileOrFolder = {
|
||||
path: "/a/b/c/app.ts",
|
||||
@@ -225,10 +282,7 @@ namespace ts {
|
||||
console.log(f)
|
||||
`
|
||||
};
|
||||
const libFile: FileOrFolder = {
|
||||
path: "/a/lib/lib.d.ts",
|
||||
content: libFileContent
|
||||
};
|
||||
|
||||
const moduleFile: FileOrFolder = {
|
||||
path: "/a/b/c/module.d.ts",
|
||||
content: `export let x: number`
|
||||
@@ -238,13 +292,13 @@ namespace ts {
|
||||
const { configFileName } = projectService.openClientFile(appFile.path);
|
||||
|
||||
assert(!configFileName, `should not find config, got: '${configFileName}`);
|
||||
assert.equal(projectService.inferredProjects.length, 1, "expected one inferred project");
|
||||
assert.equal(projectService.configuredProjects.length, 0, "expected no configured project");
|
||||
checkConfiguredProjectNumber(projectService, 0);
|
||||
checkInferredProjectNumber(projectService, 1);
|
||||
|
||||
const project = projectService.inferredProjects[0];
|
||||
|
||||
checkFileNames("inferred project", project.getFileNames(), [appFile.path, libFile.path, moduleFile.path]);
|
||||
checkMapKeys("watchedDirectories", host.watchedDirectories, ["/a/b/c", "/a/b", "/a"]);
|
||||
checkWatchedDirectories(host, ["/a/b/c", "/a/b", "/a"]);
|
||||
});
|
||||
|
||||
it("create configured project without file list", () => {
|
||||
@@ -258,10 +312,6 @@ namespace ts {
|
||||
]
|
||||
}`
|
||||
};
|
||||
const libFile: FileOrFolder = {
|
||||
path: "/a/lib/lib.d.ts",
|
||||
content: libFileContent
|
||||
};
|
||||
const file1: FileOrFolder = {
|
||||
path: "/a/b/c/f1.ts",
|
||||
content: "let x = 1"
|
||||
@@ -274,21 +324,130 @@ namespace ts {
|
||||
path: "/a/b/e/f3.ts",
|
||||
content: "let z = 1"
|
||||
};
|
||||
|
||||
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [ configFile, libFile, file1, file2, file3 ]);
|
||||
const projectService = new server.ProjectService(host, nullLogger);
|
||||
const { configFileName, configFileErrors } = projectService.openClientFile(file1.path);
|
||||
|
||||
assert(configFileName, "should find config file");
|
||||
assert.isTrue(!configFileErrors, `expect no errors in config file, got ${JSON.stringify(configFileErrors)}`);
|
||||
assert.equal(projectService.inferredProjects.length, 0, "expected no inferred project");
|
||||
assert.equal(projectService.configuredProjects.length, 1, "expected one configured project");
|
||||
checkInferredProjectNumber(projectService, 0);
|
||||
checkConfiguredProjectNumber(projectService, 1);
|
||||
|
||||
const project = projectService.configuredProjects[0];
|
||||
checkFileNames("configuredProjects project, actualFileNames", project.getFileNames(), [file1.path, libFile.path, file2.path]);
|
||||
checkFileNames("configuredProjects project, rootFileNames", project.getRootFiles(), [file1.path, file2.path]);
|
||||
checkConfiguredProjectActualFiles(project, [file1.path, libFile.path, file2.path]);
|
||||
checkConfiguredProjectRootFiles(project, [file1.path, file2.path]);
|
||||
// watching all files except one that was open
|
||||
checkWatchedFiles(host, [configFile.path, file2.path, libFile.path]);
|
||||
checkWatchedDirectories(host, [getDirectoryPath(configFile.path)]);
|
||||
});
|
||||
|
||||
checkMapKeys("watchedFiles", host.watchedFiles, [configFile.path, file2.path, libFile.path]); // watching all files except one that was open
|
||||
checkMapKeys("watchedDirectories", host.watchedDirectories, [getDirectoryPath(configFile.path)]);
|
||||
it("add and then remove a config file in a folder with loose files", () => {
|
||||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"files": ["commonFile1.ts"]
|
||||
}`
|
||||
};
|
||||
const filesWithoutConfig = [ libFile, commonFile1, commonFile2 ];
|
||||
const filesWithConfig = [ libFile, commonFile1, commonFile2, configFile ];
|
||||
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", filesWithoutConfig);
|
||||
const projectService = new server.ProjectService(host, nullLogger);
|
||||
projectService.openClientFile(commonFile1.path);
|
||||
projectService.openClientFile(commonFile2.path);
|
||||
|
||||
checkInferredProjectNumber(projectService, 2);
|
||||
checkWatchedDirectories(host, ["/a/b", "/a"]);
|
||||
|
||||
// Add a tsconfig file
|
||||
host.reloadFS(filesWithConfig);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", configFile.path);
|
||||
|
||||
checkInferredProjectNumber(projectService, 1);
|
||||
checkConfiguredProjectNumber(projectService, 1);
|
||||
// watching all files except one that was open
|
||||
checkWatchedFiles(host, [libFile.path, configFile.path]);
|
||||
|
||||
// remove the tsconfig file
|
||||
host.reloadFS(filesWithoutConfig);
|
||||
host.triggerFileWatcherCallback(configFile.path);
|
||||
checkInferredProjectNumber(projectService, 2);
|
||||
checkConfiguredProjectNumber(projectService, 0);
|
||||
checkWatchedDirectories(host, ["/a/b", "/a"]);
|
||||
});
|
||||
|
||||
it("add new files to a configured project without file list", (done: () => void) => {
|
||||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{}`
|
||||
};
|
||||
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, libFile, configFile]);
|
||||
const projectService = new server.ProjectService(host, nullLogger);
|
||||
projectService.openClientFile(commonFile1.path);
|
||||
checkWatchedDirectories(host, ["/a/b"]);
|
||||
checkConfiguredProjectNumber(projectService, 1);
|
||||
|
||||
const project = projectService.configuredProjects[0];
|
||||
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
||||
|
||||
// add a new ts file
|
||||
host.reloadFS([commonFile1, commonFile2, libFile, configFile]);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", commonFile2.path);
|
||||
// project service waits for 250ms to update the project structure, therefore the assertion needs to wait longer.
|
||||
setTimeout(() => {
|
||||
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||
done();
|
||||
}, 1000);
|
||||
});
|
||||
|
||||
it("should ignore non-existing files specified in the config file", () => {
|
||||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{
|
||||
"compilerOptions": {},
|
||||
"files": [
|
||||
"commonFile1.ts",
|
||||
"commonFile3.ts"
|
||||
]
|
||||
}`
|
||||
};
|
||||
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, configFile]);
|
||||
const projectService = new server.ProjectService(host, nullLogger);
|
||||
projectService.openClientFile(commonFile1.path);
|
||||
projectService.openClientFile(commonFile2.path);
|
||||
|
||||
checkConfiguredProjectNumber(projectService, 1);
|
||||
const project = projectService.configuredProjects[0];
|
||||
checkConfiguredProjectRootFiles(project, [commonFile1.path]);
|
||||
checkInferredProjectNumber(projectService, 1);
|
||||
});
|
||||
|
||||
it("handle recreated files correctly", (done: () => void) => {
|
||||
const configFile: FileOrFolder = {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{}`
|
||||
};
|
||||
const host = new TestServerHost(/*useCaseSensitiveFileNames*/ false, getExecutingFilePathFromLibFile(libFile), "/", [commonFile1, commonFile2, configFile]);
|
||||
const projectService = new server.ProjectService(host, nullLogger);
|
||||
projectService.openClientFile(commonFile1.path);
|
||||
|
||||
checkConfiguredProjectNumber(projectService, 1);
|
||||
const project = projectService.configuredProjects[0];
|
||||
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||
|
||||
// delete commonFile1
|
||||
projectService.closeClientFile(commonFile1.path);
|
||||
host.reloadFS([configFile]);
|
||||
host.triggerDirectoryWatcherCallback("/a/b", commonFile1.path);
|
||||
host.setTimeout(() => {
|
||||
// re-add commonFile1
|
||||
host.reloadFS([commonFile1, configFile]);
|
||||
projectService.openClientFile(commonFile1.path);
|
||||
host.setTimeout(() => {
|
||||
checkConfiguredProjectRootFiles(project, [commonFile1.path, commonFile2.path]);
|
||||
done();
|
||||
}, 500);
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user