/** * @license * Copyright Google LLC 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 * as ng from '@angular/compiler'; import * as ts from 'typescript'; import {getClassDeclFromDecoratorProp, getDirectiveClassLike} from '../src/ts_utils'; import {getPathToNodeAtPosition} from '../src/utils'; import {MockTypescriptHost} from './test_utils'; describe('getDirectiveClassLike', () => { it('should return a directive class', () => { const sourceFile = ts.createSourceFile( 'foo.ts', ` @NgModule({ declarations: [], }) class AppModule {} `, ts.ScriptTarget.ES2015); const result = sourceFile.forEachChild(c => { const directive = getDirectiveClassLike(c); if (directive) { return directive; } }); expect(result).toBeTruthy(); const {decoratorId, classId} = result!; expect(decoratorId.kind).toBe(ts.SyntaxKind.Identifier); expect(decoratorId.text).toBe('NgModule'); expect(classId.text).toBe('AppModule'); }); }); describe('getPathToNodeAtPosition', () => { const html = '
'; const nodes: ng.Node[] = []; beforeAll(() => { const parser = new ng.HtmlParser(); const {rootNodes, errors} = parser.parse(html, 'url'); expect(errors.length).toBe(0); nodes.push(...rootNodes); }); it('should capture element', () => { // Try to get a path to an element // <|div c> // ^ cursor is here const position = html.indexOf('div'); const path = getPathToNodeAtPosition(nodes, position); // There should be just 1 node in the path, the Element node expect(path.empty).toBe(false); expect(path.head instanceof ng.Element).toBe(true); expect(path.head).toBe(path.tail); }); it('should capture attribute', () => { // Try to get a path to an attribute // // ^ cusor is here, before the attribute const position = html.indexOf('c'); const path = getPathToNodeAtPosition(nodes, position); expect(path.empty).toBe(false); expect(path.head instanceof ng.Element).toBe(true); expect(path.tail instanceof ng.Attribute).toBe(true); }); it('should capture attribute before cursor', () => { // Try to get a path to an attribute // // ^ cursor is here, after the attribute const position = html.indexOf('c') + 1; const path = getPathToNodeAtPosition(nodes, position); expect(path.empty).toBe(false); expect(path.head instanceof ng.Element).toBe(true); expect(path.tail instanceof ng.Attribute).toBe(true); }); }); describe('getClassDeclFromTemplateNode', () => { it('should find class declaration in syntax-only mode', () => { const sourceFile = ts.createSourceFile( 'foo.ts', ` @Component({ template: '' }) class MyComponent {}`, ts.ScriptTarget.ES2015, true /* setParentNodes */); function visit(node: ts.Node): ts.ClassDeclaration|undefined { if (ts.isPropertyAssignment(node)) { return getClassDeclFromDecoratorProp(node); } return node.forEachChild(visit); } const classDecl = sourceFile.forEachChild(visit); expect(classDecl).toBeTruthy(); expect(classDecl!.kind).toBe(ts.SyntaxKind.ClassDeclaration); expect((classDecl as ts.ClassDeclaration).name!.text).toBe('MyComponent'); }); it('should return class declaration for AppComponent', () => { const host = new MockTypescriptHost(['/app/app.component.ts']); const tsLS = ts.createLanguageService(host); const sourceFile = tsLS.getProgram()!.getSourceFile('/app/app.component.ts'); expect(sourceFile).toBeTruthy(); const classDecl = sourceFile!.forEachChild(function visit(node): ts.Node|undefined { if (ts.isPropertyAssignment(node)) { return getClassDeclFromDecoratorProp(node); } return node.forEachChild(visit); }); expect(classDecl).toBeTruthy(); expect(ts.isClassDeclaration(classDecl!)).toBe(true); expect((classDecl as ts.ClassDeclaration).name!.text).toBe('AppComponent'); }); });