Keen Yee Liau 58ea040570 test(language-service): add new mock host for testing ivy (#36879)
This commit adds a new mock host for testing the ivy language service.

Unlike the existing mock_host which mocks the LanguageServiceHost, the
Ivy mock host mocks just the filesystem interface, aka ts.ServerHost.

This is because Ivy language service requires an actual Project to
perform operations like adding synthetic typecheck files to the project,
and by extension, to the ts.Program. These requirements make the existing
mock host unsuitable to be reused.

This new testing structure also improves test performance, because the
old mock host copies (it actually creates symlinks, but still that's
relatively expensive due to the sheer number of files involved) all
@angular/* packages along with the typescript package to a temporary
node_modules directory. This is done every time setup() is called.
Instead, this new mock host just loads them from a pre-determined path
in Bazel runfiles.

PR Close #36879
2020-05-04 12:47:15 -07:00

78 lines
2.6 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {join} from 'path';
import * as ts from 'typescript/lib/tsserverlibrary';
const logger: ts.server.Logger = {
close(): void{},
hasLevel(level: ts.server.LogLevel): boolean {
return false;
},
loggingEnabled(): boolean {
return false;
},
perftrc(s: string): void{},
info(s: string): void{},
startGroup(): void{},
endGroup(): void{},
msg(s: string, type?: ts.server.Msg): void{},
getLogFileName(): string |
undefined {
return;
},
};
export const TEST_SRCDIR = process.env.TEST_SRCDIR!;
export const PROJECT_DIR =
join(TEST_SRCDIR, 'angular', 'packages', 'language-service', 'test', 'project');
export const TSCONFIG = join(PROJECT_DIR, 'tsconfig.json');
export const APP_COMPONENT = join(PROJECT_DIR, 'app', 'app.component.ts');
export const APP_MAIN = join(PROJECT_DIR, 'app', 'main.ts');
export const PARSING_CASES = join(PROJECT_DIR, 'app', 'parsing-cases.ts');
export const host: ts.server.ServerHost = {
...ts.sys,
readFile(absPath: string, encoding?: string): string |
undefined {
// TODO: Need to remove all annotations in templates like we do in
// MockTypescriptHost
return ts.sys.readFile(absPath, encoding);
},
// TODO: Need to cast as never because this is not a proper ServerHost interface.
// ts.sys lacks methods like watchFile() and watchDirectory(), but these are not
// needed for now.
} as never;
/**
* Create a ConfiguredProject and an actual program for the test project located
* in packages/language-service/test/project. Project creation exercises the
* actual code path, but a mock host is used for the filesystem to intercept
* and modify test files.
*/
export function setup() {
const projectService = new ts.server.ProjectService({
host,
logger,
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: true,
useInferredProjectPerProjectRoot: true,
typingsInstaller: ts.server.nullTypingsInstaller,
});
// Opening APP_COMPONENT forces a new ConfiguredProject to be created based
// on the tsconfig.json in the test project.
projectService.openClientFile(APP_COMPONENT);
const project = projectService.findProject(TSCONFIG);
if (!project) {
throw new Error(`Failed to create project for ${TSCONFIG}`);
}
// The following operation forces a ts.Program to be created.
const tsLS = project.getLanguageService();
return {projectService, project, tsLS};
}