From 000ec6be3c3bc0b44719a4d47467e56b4a771678 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Mon, 8 Feb 2021 11:25:48 -0800 Subject: [PATCH] refactor(language-service): migrate type_definitions_spec to the new testing package (#40966) refactor(language-service): migrate type_definitions_spec to the new testing package PR Close #40966 --- .../ivy/test/type_definitions_spec.ts | 45 +++++++++---------- .../ivy/testing/src/buffer.ts | 4 ++ .../ivy/testing/src/project.ts | 31 ++++++++++++- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/packages/language-service/ivy/test/type_definitions_spec.ts b/packages/language-service/ivy/test/type_definitions_spec.ts index 01ec679cad..a911986082 100644 --- a/packages/language-service/ivy/test/type_definitions_spec.ts +++ b/packages/language-service/ivy/test/type_definitions_spec.ts @@ -6,21 +6,17 @@ * found in the LICENSE file at https://angular.io/license */ -import {absoluteFrom} from '@angular/compiler-cli/src/ngtsc/file_system'; -import {initMockFileSystem, TestFile} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; +import {initMockFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing'; -import {LanguageServiceTestEnvironment} from './env'; -import {humanizeDocumentSpanLike} from './test_utils'; +import {extractCursorInfo, humanizeDocumentSpanLike, LanguageServiceTestEnv, Project} from '../testing'; describe('type definitions', () => { - let env: LanguageServiceTestEnvironment; + let env: LanguageServiceTestEnv; it('returns the pipe class as definition when checkTypeOfPipes is false', () => { initMockFileSystem('Native'); - const testFiles: TestFile[] = [ - { - name: absoluteFrom('/app.ts'), - contents: ` + const files = { + 'app.ts': ` import {Component, NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; @@ -30,17 +26,13 @@ describe('type definitions', () => { @NgModule({declarations: [AppCmp], imports: [CommonModule]}) export class AppModule {} `, - isRoot: true - }, - { - name: absoluteFrom('/app.html'), - contents: `Will be overridden`, - } - ]; + 'app.html': `Will be overridden`, + }; // checkTypeOfPipes is set to false when strict templates is false - env = LanguageServiceTestEnvironment.setup(testFiles, {strictTemplates: false}); + env = LanguageServiceTestEnv.setup(); + const project = env.addProject('test', files, {strictTemplates: false}); const definitions = - getTypeDefinitionsAndAssertBoundSpan({templateOverride: '{{"1/1/2020" | dat¦e}}'}); + getTypeDefinitionsAndAssertBoundSpan(project, {templateOverride: '{{"1/1/2020" | dat¦e}}'}); expect(definitions!.length).toEqual(1); const [def] = definitions; @@ -48,14 +40,17 @@ describe('type definitions', () => { expect(def.contextSpan).toContain('DatePipe'); }); - function getTypeDefinitionsAndAssertBoundSpan({templateOverride}: {templateOverride: string}) { - const {cursor, text} = env.updateFileWithCursor(absoluteFrom('/app.html'), templateOverride); + function getTypeDefinitionsAndAssertBoundSpan( + project: Project, {templateOverride}: {templateOverride: string}) { + const {text} = extractCursorInfo(templateOverride); + const template = project.openFile('app.html'); + template.contents = text; env.expectNoSourceDiagnostics(); - env.expectNoTemplateDiagnostics(absoluteFrom('/app.ts'), 'AppCmp'); - const defs = env.ngLS.getTypeDefinitionAtPosition(absoluteFrom('/app.html'), cursor); + project.expectNoTemplateDiagnostics('app.ts', 'AppCmp'); + + template.moveCursorToText(templateOverride); + const defs = template.getTypeDefinitionAtPosition(); expect(defs).toBeTruthy(); - const overrides = new Map(); - overrides.set(absoluteFrom('/app.html'), text); - return defs!.map(d => humanizeDocumentSpanLike(d, env, overrides)); + return defs!.map(d => humanizeDocumentSpanLike(d, env)); } }); diff --git a/packages/language-service/ivy/testing/src/buffer.ts b/packages/language-service/ivy/testing/src/buffer.ts index 7c0fc9798c..b17c869c8d 100644 --- a/packages/language-service/ivy/testing/src/buffer.ts +++ b/packages/language-service/ivy/testing/src/buffer.ts @@ -84,4 +84,8 @@ export class OpenBuffer { getQuickInfoAtPosition() { return this.ngLS.getQuickInfoAtPosition(this.scriptInfo.fileName, this._cursor); } + + getTypeDefinitionAtPosition() { + return this.ngLS.getTypeDefinitionAtPosition(this.scriptInfo.fileName, this._cursor); + } } diff --git a/packages/language-service/ivy/testing/src/project.ts b/packages/language-service/ivy/testing/src/project.ts index e59805bba2..b79d903916 100644 --- a/packages/language-service/ivy/testing/src/project.ts +++ b/packages/language-service/ivy/testing/src/project.ts @@ -7,8 +7,8 @@ */ import {StrictTemplateOptions} from '@angular/compiler-cli/src/ngtsc/core/api'; -import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system'; -import {OptimizeFor} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; +import {absoluteFrom, AbsoluteFsPath, FileSystem, getFileSystem, getSourceFileOrError} from '@angular/compiler-cli/src/ngtsc/file_system'; +import {OptimizeFor, TemplateTypeChecker} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import * as ts from 'typescript/lib/tsserverlibrary'; import {LanguageService} from '../../language_service'; import {OpenBuffer} from './buffer'; @@ -156,4 +156,31 @@ export class Project { this.ngLS.compilerFactory.registerLastKnownProgram(); } + + expectNoTemplateDiagnostics(projectFileName: string, className: string): void { + const program = this.tsLS.getProgram(); + if (program === undefined) { + throw new Error(`Expected to get a ts.Program`); + } + const fileName = absoluteFrom(`/${this.name}/${projectFileName}`); + const sf = getSourceFileOrError(program, fileName); + const component = getClassOrError(sf, className); + + const diags = this.getTemplateTypeChecker().getDiagnosticsForComponent(component); + this.ngLS.compilerFactory.registerLastKnownProgram(); + expect(diags.map(diag => diag.messageText)).toEqual([]); + } + + getTemplateTypeChecker(): TemplateTypeChecker { + return this.ngLS.compilerFactory.getOrCreate().getTemplateTypeChecker(); + } } + +function getClassOrError(sf: ts.SourceFile, name: string): ts.ClassDeclaration { + for (const stmt of sf.statements) { + if (ts.isClassDeclaration(stmt) && stmt.name !== undefined && stmt.name.text === name) { + return stmt; + } + } + throw new Error(`Class ${name} not found in file: ${sf.fileName}: ${sf.text}`); +} \ No newline at end of file