refactor(language-service): Add type to `TemplateTarget` that can indicate multi-node targets (#40185)

The current template target implementation only allows a way to
represent the template position as targeting a single node in the
template AST. However, there is at least one case (banana-in-a-box)
where a given template position refers to two template targets.

This commit expands the contexts that the `TemplateTarget` can return to
include support for the banana-in-a-box syntax, which has two logically
targetted AST nodes given a position within the `keySpan` of the
binding.

PR Close #40185
This commit is contained in:
Andrew Scott 2020-12-16 14:39:49 -08:00 committed by atscott
parent 46ea684351
commit a893187d51
5 changed files with 204 additions and 175 deletions

View File

@ -12,7 +12,7 @@ import {isExternalResource} from '@angular/compiler-cli/src/ngtsc/metadata';
import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ShimLocation, Symbol, SymbolKind, TemplateSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api'; import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ShimLocation, Symbol, SymbolKind, TemplateSymbol} from '@angular/compiler-cli/src/ngtsc/typecheck/api';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {getTargetAtPosition} from './template_target'; import {getTargetAtPosition, TargetNodeKind} from './template_target';
import {findTightestNode, getParentClassDeclaration} from './ts_utils'; import {findTightestNode, getParentClassDeclaration} from './ts_utils';
import {flatMap, getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, getTextSpanOfNode, isDollarEvent, isTypeScriptFile, TemplateInfo, toTextSpan} from './utils'; import {flatMap, getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, getTextSpanOfNode, isDollarEvent, isTypeScriptFile, TemplateInfo, toTextSpan} from './utils';
@ -226,14 +226,16 @@ export class DefinitionBuilder {
if (target === null) { if (target === null) {
return undefined; return undefined;
} }
const {nodeInContext, parent} = target; const {context, parent} = target;
const symbol = const node =
this.compiler.getTemplateTypeChecker().getSymbolOfNode(nodeInContext.node, component); context.kind === TargetNodeKind.TwoWayBindingContext ? context.nodes[0] : context.node;
const symbol = this.compiler.getTemplateTypeChecker().getSymbolOfNode(node, component);
if (symbol === null) { if (symbol === null) {
return undefined; return undefined;
} }
return {node: nodeInContext.node, parent, symbol}; return {node, parent, symbol};
} }
} }

View File

@ -19,7 +19,7 @@ import {CompletionBuilder, CompletionNodeContext} from './completions';
import {DefinitionBuilder} from './definitions'; import {DefinitionBuilder} from './definitions';
import {QuickInfoBuilder} from './quick_info'; import {QuickInfoBuilder} from './quick_info';
import {ReferenceBuilder} from './references'; import {ReferenceBuilder} from './references';
import {getTargetAtPosition, TargetNode, TargetNodeKind} from './template_target'; import {getTargetAtPosition, TargetContext, TargetNodeKind} from './template_target';
import {getTemplateInfoAtPosition, isTypeScriptFile} from './utils'; import {getTemplateInfoAtPosition, isTypeScriptFile} from './utils';
export class LanguageService { export class LanguageService {
@ -104,10 +104,11 @@ export class LanguageService {
if (positionDetails === null) { if (positionDetails === null) {
return undefined; return undefined;
} }
const results =
new QuickInfoBuilder( const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
this.tsLS, compiler, templateInfo.component, positionDetails.nodeInContext.node) positionDetails.context.nodes[0] :
.get(); positionDetails.context.node;
const results = new QuickInfoBuilder(this.tsLS, compiler, templateInfo.component, node).get();
this.compilerFactory.registerLastKnownProgram(); this.compilerFactory.registerLastKnownProgram();
return results; return results;
} }
@ -131,9 +132,13 @@ export class LanguageService {
if (positionDetails === null) { if (positionDetails === null) {
return null; return null;
} }
const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
positionDetails.context.nodes[0] :
positionDetails.context.node;
return new CompletionBuilder( return new CompletionBuilder(
this.tsLS, compiler, templateInfo.component, positionDetails.nodeInContext.node, this.tsLS, compiler, templateInfo.component, node,
nodeContextFromTarget(positionDetails.nodeInContext), positionDetails.parent, nodeContextFromTarget(positionDetails.context), positionDetails.parent,
positionDetails.template); positionDetails.template);
} }
@ -260,7 +265,7 @@ function getOrCreateTypeCheckScriptInfo(
return scriptInfo; return scriptInfo;
} }
function nodeContextFromTarget(target: TargetNode): CompletionNodeContext { function nodeContextFromTarget(target: TargetContext): CompletionNodeContext {
switch (target.kind) { switch (target.kind) {
case TargetNodeKind.ElementInTagContext: case TargetNodeKind.ElementInTagContext:
return CompletionNodeContext.ElementTag; return CompletionNodeContext.ElementTag;

View File

@ -12,7 +12,7 @@ import {DirectiveSymbol, SymbolKind, TemplateTypeChecker, TypeCheckingProgramStr
import {ExpressionIdentifier, hasExpressionIdentifier} from '@angular/compiler-cli/src/ngtsc/typecheck/src/comments'; import {ExpressionIdentifier, hasExpressionIdentifier} from '@angular/compiler-cli/src/ngtsc/typecheck/src/comments';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {getTargetAtPosition} from './template_target'; import {getTargetAtPosition, TargetNodeKind} from './template_target';
import {findTightestNode} from './ts_utils'; import {findTightestNode} from './ts_utils';
import {getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, isWithin, TemplateInfo, toTextSpan} from './utils'; import {getDirectiveMatchesForAttribute, getDirectiveMatchesForElementTag, getTemplateInfoAtPosition, isWithin, TemplateInfo, toTextSpan} from './utils';
@ -39,7 +39,9 @@ export class ReferenceBuilder {
return undefined; return undefined;
} }
const node = positionDetails.nodeInContext.node; const node = positionDetails.context.kind === TargetNodeKind.TwoWayBindingContext ?
positionDetails.context.nodes[0] :
positionDetails.context.node;
// Get the information about the TCB at the template position. // Get the information about the TCB at the template position.
const symbol = this.ttc.getSymbolOfNode(node, component); const symbol = this.ttc.getSymbolOfNode(node, component);
@ -74,13 +76,12 @@ export class ReferenceBuilder {
case SymbolKind.Variable: { case SymbolKind.Variable: {
const {positionInShimFile: initializerPosition, shimPath} = symbol.initializerLocation; const {positionInShimFile: initializerPosition, shimPath} = symbol.initializerLocation;
const localVarPosition = symbol.localVarLocation.positionInShimFile; const localVarPosition = symbol.localVarLocation.positionInShimFile;
const templateNode = positionDetails.nodeInContext.node;
if ((templateNode instanceof TmplAstVariable)) { if ((node instanceof TmplAstVariable)) {
if (templateNode.valueSpan !== undefined && isWithin(position, templateNode.valueSpan)) { if (node.valueSpan !== undefined && isWithin(position, node.valueSpan)) {
// In the valueSpan of the variable, we want to get the reference of the initializer. // In the valueSpan of the variable, we want to get the reference of the initializer.
return this.getReferencesAtTypescriptPosition(shimPath, initializerPosition); return this.getReferencesAtTypescriptPosition(shimPath, initializerPosition);
} else if (isWithin(position, templateNode.keySpan)) { } else if (isWithin(position, node.keySpan)) {
// In the keySpan of the variable, we want to get the reference of the local variable. // In the keySpan of the variable, we want to get the reference of the local variable.
return this.getReferencesAtTypescriptPosition(shimPath, localVarPosition); return this.getReferencesAtTypescriptPosition(shimPath, localVarPosition);
} else { } else {

View File

@ -10,7 +10,7 @@ import {ParseSpan, TmplAstBoundEvent} from '@angular/compiler';
import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST
import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST
import {isTemplateNode, isTemplateNodeWithKeyAndValue, isWithin, isWithinKeyValue} from './utils'; import {isTemplateNodeWithKeyAndValue, isWithin, isWithinKeyValue} from './utils';
/** /**
* Contextual information for a target position within the template. * Contextual information for a target position within the template.
@ -22,9 +22,9 @@ export interface TemplateTarget {
position: number; position: number;
/** /**
* The template node (or AST expression) closest to the search position. * The template (or AST expression) node or nodes closest to the search position.
*/ */
nodeInContext: TargetNode; context: TargetContext;
/** /**
* The `t.Template` which contains the found node or expression (or `null` if in the root * The `t.Template` which contains the found node or expression (or `null` if in the root
@ -39,15 +39,26 @@ export interface TemplateTarget {
} }
/** /**
* A node targeted at a given position in the template, including potential contextual information * A node or nodes targeted at a given position in the template, including potential contextual
* about the specific aspect of the node being referenced. * information about the specific aspect of the node being referenced.
* *
* Some nodes have multiple interior contexts. For example, `t.Element` nodes have both a tag name * Some nodes have multiple interior contexts. For example, `t.Element` nodes have both a tag name
* as well as a body, and a given position definitively points to one or the other. `TargetNode` * as well as a body, and a given position definitively points to one or the other. `TargetNode`
* captures the node itself, as well as this additional contextual disambiguation. * captures the node itself, as well as this additional contextual disambiguation.
*/ */
export type TargetNode = RawExpression|RawTemplateNode|ElementInBodyContext|ElementInTagContext| export type TargetContext = SingleNodeTarget|MultiNodeTarget;
AttributeInKeyContext|AttributeInValueContext;
/** Contexts which logically target only a single node in the template AST. */
export type SingleNodeTarget = RawExpression|RawTemplateNode|ElementInBodyContext|
ElementInTagContext|AttributeInKeyContext|AttributeInValueContext;
/**
* Contexts which logically target multiple nodes in the template AST, which cannot be
* disambiguated given a single position because they are all equally relavent. For example, in the
* banana-in-a-box syntax `[(ngModel)]="formValues.person"`, the position in the template for the
* key `ngModel` refers to both the bound event `ngModelChange` and the input `ngModel`.
*/
export type MultiNodeTarget = TwoWayBindingContext;
/** /**
* Differentiates the various kinds of `TargetNode`s. * Differentiates the various kinds of `TargetNode`s.
@ -59,6 +70,7 @@ export enum TargetNodeKind {
ElementInBodyContext, ElementInBodyContext,
AttributeInKeyContext, AttributeInKeyContext,
AttributeInValueContext, AttributeInValueContext,
TwoWayBindingContext,
} }
/** /**
@ -105,6 +117,15 @@ export interface AttributeInValueContext {
node: t.TextAttribute|t.BoundAttribute|t.BoundEvent; node: t.TextAttribute|t.BoundAttribute|t.BoundEvent;
} }
/**
* A `t.BoundAttribute` and `t.BoundEvent` pair that are targeted, where the given position is
* within the key span of both.
*/
export interface TwoWayBindingContext {
kind: TargetNodeKind.TwoWayBindingContext;
nodes: [t.BoundAttribute, t.BoundEvent];
}
/** /**
* This special marker is added to the path when the cursor is within the sourceSpan but not the key * This special marker is added to the path when the cursor is within the sourceSpan but not the key
* or value span of a node with key/value spans. * or value span of a node with key/value spans.
@ -141,7 +162,7 @@ export function getTargetAtPosition(template: t.Node[], position: number): Templ
} }
// Given the candidate node, determine the full targeted context. // Given the candidate node, determine the full targeted context.
let nodeInContext: TargetNode; let nodeInContext: TargetContext;
if (candidate instanceof e.AST) { if (candidate instanceof e.AST) {
nodeInContext = { nodeInContext = {
kind: TargetNodeKind.RawExpression, kind: TargetNodeKind.RawExpression,
@ -188,7 +209,7 @@ export function getTargetAtPosition(template: t.Node[], position: number): Templ
}; };
} }
return {position, nodeInContext, template: context, parent}; return {position, context: nodeInContext, template: context, parent};
} }
/** /**

View File

@ -10,7 +10,7 @@ import {ParseError, parseTemplate} from '@angular/compiler';
import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST import * as e from '@angular/compiler/src/expression_parser/ast'; // e for expression AST
import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST import * as t from '@angular/compiler/src/render3/r3_ast'; // t for template AST
import {getTargetAtPosition, TargetNodeKind} from '../../template_target'; import {getTargetAtPosition, SingleNodeTarget, TargetNodeKind} from '../../template_target';
import {isExpressionNode, isTemplateNode} from '../../utils'; import {isExpressionNode, isTemplateNode} from '../../utils';
interface ParseResult { interface ParseResult {
@ -36,8 +36,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate element in opening tag', () => { it('should locate element in opening tag', () => {
const {errors, nodes, position} = parse(`<di¦v></div>`); const {errors, nodes, position} = parse(`<di¦v></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Element); expect(node).toBeInstanceOf(t.Element);
}); });
@ -45,8 +45,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate element in closing tag', () => { it('should locate element in closing tag', () => {
const {errors, nodes, position} = parse(`<div></di¦v>`); const {errors, nodes, position} = parse(`<div></di¦v>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Element); expect(node).toBeInstanceOf(t.Element);
}); });
@ -54,8 +54,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate element when cursor is at the beginning', () => { it('should locate element when cursor is at the beginning', () => {
const {errors, nodes, position} = parse(`<¦div></div>`); const {errors, nodes, position} = parse(`<¦div></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Element); expect(node).toBeInstanceOf(t.Element);
}); });
@ -63,8 +63,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate element when cursor is at the end', () => { it('should locate element when cursor is at the end', () => {
const {errors, nodes, position} = parse(`<div¦></div>`); const {errors, nodes, position} = parse(`<div¦></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Element); expect(node).toBeInstanceOf(t.Element);
}); });
@ -72,8 +72,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate attribute key', () => { it('should locate attribute key', () => {
const {errors, nodes, position} = parse(`<div cla¦ss="foo"></div>`); const {errors, nodes, position} = parse(`<div cla¦ss="foo"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
}); });
@ -81,8 +81,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate attribute value', () => { it('should locate attribute value', () => {
const {errors, nodes, position} = parse(`<div class="fo¦o"></div>`); const {errors, nodes, position} = parse(`<div class="fo¦o"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
// TODO: Note that we do not have the ability to detect the RHS (yet) // TODO: Note that we do not have the ability to detect the RHS (yet)
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
@ -91,8 +91,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate bound attribute key', () => { it('should locate bound attribute key', () => {
const {errors, nodes, position} = parse(`<test-cmp [fo¦o]="bar"></test-cmp>`); const {errors, nodes, position} = parse(`<test-cmp [fo¦o]="bar"></test-cmp>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
}); });
@ -100,8 +100,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate bound attribute value', () => { it('should locate bound attribute value', () => {
const {errors, nodes, position} = parse(`<test-cmp [foo]="b¦ar"></test-cmp>`); const {errors, nodes, position} = parse(`<test-cmp [foo]="b¦ar"></test-cmp>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
}); });
@ -116,8 +116,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate bound event key', () => { it('should locate bound event key', () => {
const {errors, nodes, position} = parse(`<test-cmp (fo¦o)="bar()"></test-cmp>`); const {errors, nodes, position} = parse(`<test-cmp (fo¦o)="bar()"></test-cmp>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundEvent); expect(node).toBeInstanceOf(t.BoundEvent);
}); });
@ -125,8 +125,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate bound event value', () => { it('should locate bound event value', () => {
const {errors, nodes, position} = parse(`<test-cmp (foo)="b¦ar()"></test-cmp>`); const {errors, nodes, position} = parse(`<test-cmp (foo)="b¦ar()"></test-cmp>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.MethodCall); expect(node).toBeInstanceOf(e.MethodCall);
}); });
@ -134,8 +134,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate element children', () => { it('should locate element children', () => {
const {errors, nodes, position} = parse(`<div><sp¦an></span></div>`); const {errors, nodes, position} = parse(`<div><sp¦an></span></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Element); expect(node).toBeInstanceOf(t.Element);
expect((node as t.Element).name).toBe('span'); expect((node as t.Element).name).toBe('span');
@ -144,8 +144,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate element reference', () => { it('should locate element reference', () => {
const {errors, nodes, position} = parse(`<div #my¦div></div>`); const {errors, nodes, position} = parse(`<div #my¦div></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Reference); expect(node).toBeInstanceOf(t.Reference);
}); });
@ -153,8 +153,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template text attribute', () => { it('should locate template text attribute', () => {
const {errors, nodes, position} = parse(`<ng-template ng¦If></ng-template>`); const {errors, nodes, position} = parse(`<ng-template ng¦If></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
}); });
@ -162,8 +162,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template bound attribute key', () => { it('should locate template bound attribute key', () => {
const {errors, nodes, position} = parse(`<ng-template [ng¦If]="foo"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template [ng¦If]="foo"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
}); });
@ -171,8 +171,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template bound attribute value', () => { it('should locate template bound attribute value', () => {
const {errors, nodes, position} = parse(`<ng-template [ngIf]="f¦oo"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template [ngIf]="f¦oo"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
}); });
@ -180,8 +180,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template bound attribute key in two-way binding', () => { it('should locate template bound attribute key in two-way binding', () => {
const {errors, nodes, position} = parse(`<ng-template [(f¦oo)]="bar"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template [(f¦oo)]="bar"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
expect((node as t.BoundAttribute).name).toBe('foo'); expect((node as t.BoundAttribute).name).toBe('foo');
@ -190,8 +190,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template bound attribute value in two-way binding', () => { it('should locate template bound attribute value in two-way binding', () => {
const {errors, nodes, position} = parse(`<ng-template [(foo)]="b¦ar"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template [(foo)]="b¦ar"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('bar'); expect((node as e.PropertyRead).name).toBe('bar');
@ -200,8 +200,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template bound event key', () => { it('should locate template bound event key', () => {
const {errors, nodes, position} = parse(`<ng-template (cl¦ick)="foo()"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template (cl¦ick)="foo()"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundEvent); expect(node).toBeInstanceOf(t.BoundEvent);
}); });
@ -209,16 +209,16 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template bound event value', () => { it('should locate template bound event value', () => {
const {errors, nodes, position} = parse(`<ng-template (click)="f¦oo()"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template (click)="f¦oo()"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(node).toBeInstanceOf(e.MethodCall); expect(node).toBeInstanceOf(e.MethodCall);
}); });
it('should locate template attribute key', () => { it('should locate template attribute key', () => {
const {errors, nodes, position} = parse(`<ng-template i¦d="foo"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template i¦d="foo"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
}); });
@ -226,8 +226,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template attribute value', () => { it('should locate template attribute value', () => {
const {errors, nodes, position} = parse(`<ng-template id="f¦oo"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template id="f¦oo"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
// TODO: Note that we do not have the ability to detect the RHS (yet) // TODO: Note that we do not have the ability to detect the RHS (yet)
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
@ -236,8 +236,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template reference key via the # notation', () => { it('should locate template reference key via the # notation', () => {
const {errors, nodes, position} = parse(`<ng-template #f¦oo></ng-template>`); const {errors, nodes, position} = parse(`<ng-template #f¦oo></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Reference); expect(node).toBeInstanceOf(t.Reference);
expect((node as t.Reference).name).toBe('foo'); expect((node as t.Reference).name).toBe('foo');
@ -246,8 +246,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template reference key via the ref- notation', () => { it('should locate template reference key via the ref- notation', () => {
const {errors, nodes, position} = parse(`<ng-template ref-fo¦o></ng-template>`); const {errors, nodes, position} = parse(`<ng-template ref-fo¦o></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Reference); expect(node).toBeInstanceOf(t.Reference);
expect((node as t.Reference).name).toBe('foo'); expect((node as t.Reference).name).toBe('foo');
@ -256,8 +256,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template reference value via the # notation', () => { it('should locate template reference value via the # notation', () => {
const {errors, nodes, position} = parse(`<ng-template #foo="export¦As"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template #foo="export¦As"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Reference); expect(node).toBeInstanceOf(t.Reference);
expect((node as t.Reference).value).toBe('exportAs'); expect((node as t.Reference).value).toBe('exportAs');
@ -267,8 +267,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template reference value via the ref- notation', () => { it('should locate template reference value via the ref- notation', () => {
const {errors, nodes, position} = parse(`<ng-template ref-foo="export¦As"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template ref-foo="export¦As"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Reference); expect(node).toBeInstanceOf(t.Reference);
expect((node as t.Reference).value).toBe('exportAs'); expect((node as t.Reference).value).toBe('exportAs');
@ -278,8 +278,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template variable key', () => { it('should locate template variable key', () => {
const {errors, nodes, position} = parse(`<ng-template let-f¦oo="bar"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template let-f¦oo="bar"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Variable); expect(node).toBeInstanceOf(t.Variable);
}); });
@ -287,8 +287,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template variable value', () => { it('should locate template variable value', () => {
const {errors, nodes, position} = parse(`<ng-template let-foo="b¦ar"></ng-template>`); const {errors, nodes, position} = parse(`<ng-template let-foo="b¦ar"></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Variable); expect(node).toBeInstanceOf(t.Variable);
}); });
@ -296,8 +296,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate template children', () => { it('should locate template children', () => {
const {errors, nodes, position} = parse(`<ng-template><d¦iv></div></ng-template>`); const {errors, nodes, position} = parse(`<ng-template><d¦iv></div></ng-template>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Element); expect(node).toBeInstanceOf(t.Element);
}); });
@ -305,8 +305,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate ng-content', () => { it('should locate ng-content', () => {
const {errors, nodes, position} = parse(`<ng-co¦ntent></ng-content>`); const {errors, nodes, position} = parse(`<ng-co¦ntent></ng-content>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Content); expect(node).toBeInstanceOf(t.Content);
}); });
@ -314,8 +314,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate ng-content attribute key', () => { it('should locate ng-content attribute key', () => {
const {errors, nodes, position} = parse('<ng-content cla¦ss="red"></ng-content>'); const {errors, nodes, position} = parse('<ng-content cla¦ss="red"></ng-content>');
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
}); });
@ -323,8 +323,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate ng-content attribute value', () => { it('should locate ng-content attribute value', () => {
const {errors, nodes, position} = parse('<ng-content class="r¦ed"></ng-content>'); const {errors, nodes, position} = parse('<ng-content class="r¦ed"></ng-content>');
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
// TODO: Note that we do not have the ability to detect the RHS (yet) // TODO: Note that we do not have the ability to detect the RHS (yet)
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
@ -333,8 +333,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should not locate implicit receiver', () => { it('should not locate implicit receiver', () => {
const {errors, nodes, position} = parse(`<div [foo]="¦bar"></div>`); const {errors, nodes, position} = parse(`<div [foo]="¦bar"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
}); });
@ -342,8 +342,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate bound attribute key in two-way binding', () => { it('should locate bound attribute key in two-way binding', () => {
const {errors, nodes, position} = parse(`<cmp [(f¦oo)]="bar"></cmp>`); const {errors, nodes, position} = parse(`<cmp [(f¦oo)]="bar"></cmp>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
expect((node as t.BoundAttribute).name).toBe('foo'); expect((node as t.BoundAttribute).name).toBe('foo');
@ -352,8 +352,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate bound attribute value in two-way binding', () => { it('should locate bound attribute value in two-way binding', () => {
const {errors, nodes, position} = parse(`<cmp [(foo)]="b¦ar"></cmp>`); const {errors, nodes, position} = parse(`<cmp [(foo)]="b¦ar"></cmp>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('bar'); expect((node as e.PropertyRead).name).toBe('bar');
@ -362,8 +362,8 @@ describe('getTargetAtPosition for template AST', () => {
it('should locate switch value in ICUs', () => { it('should locate switch value in ICUs', () => {
const {errors, nodes, position} = parse(`<span i18n>{sw¦itch, plural, other {text}}"></span>`); const {errors, nodes, position} = parse(`<span i18n>{sw¦itch, plural, other {text}}"></span>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('switch'); expect((node as e.PropertyRead).name).toBe('switch');
@ -373,8 +373,8 @@ describe('getTargetAtPosition for template AST', () => {
const {errors, nodes, position} = parse( const {errors, nodes, position} = parse(
`<span i18n>{expr, plural, other { {ne¦sted, plural, =1 { {{nestedInterpolation}} }} }}"></span>`); `<span i18n>{expr, plural, other { {ne¦sted, plural, =1 { {{nestedInterpolation}} }} }}"></span>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('nested'); expect((node as e.PropertyRead).name).toBe('nested');
@ -384,8 +384,8 @@ describe('getTargetAtPosition for template AST', () => {
const {errors, nodes, position} = const {errors, nodes, position} =
parse(`<span i18n>{expr, plural, other { {{ i¦nterpolation }} }}"></span>`); parse(`<span i18n>{expr, plural, other { {{ i¦nterpolation }} }}"></span>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('interpolation'); expect((node as e.PropertyRead).name).toBe('interpolation');
@ -395,8 +395,8 @@ describe('getTargetAtPosition for template AST', () => {
const {errors, nodes, position} = parse( const {errors, nodes, position} = parse(
`<span i18n>{expr, plural, other { {nested, plural, =1 { {{n¦estedInterpolation}} }} }}"></span>`); `<span i18n>{expr, plural, other { {nested, plural, =1 { {{n¦estedInterpolation}} }} }}"></span>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('nestedInterpolation'); expect((node as e.PropertyRead).name).toBe('nestedInterpolation');
@ -407,8 +407,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should not locate implicit receiver', () => { it('should not locate implicit receiver', () => {
const {errors, nodes, position} = parse(`{{ ¦title }}`); const {errors, nodes, position} = parse(`{{ ¦title }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('title'); expect((node as e.PropertyRead).name).toBe('title');
@ -417,8 +417,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate property read', () => { it('should locate property read', () => {
const {errors, nodes, position} = parse(`{{ ti¦tle }}`); const {errors, nodes, position} = parse(`{{ ti¦tle }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('title'); expect((node as e.PropertyRead).name).toBe('title');
@ -427,8 +427,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate safe property read', () => { it('should locate safe property read', () => {
const {errors, nodes, position} = parse(`{{ foo?¦.bar }}`); const {errors, nodes, position} = parse(`{{ foo?¦.bar }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.SafePropertyRead); expect(node).toBeInstanceOf(e.SafePropertyRead);
expect((node as e.SafePropertyRead).name).toBe('bar'); expect((node as e.SafePropertyRead).name).toBe('bar');
@ -437,8 +437,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate keyed read', () => { it('should locate keyed read', () => {
const {errors, nodes, position} = parse(`{{ foo['bar']¦ }}`); const {errors, nodes, position} = parse(`{{ foo['bar']¦ }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.KeyedRead); expect(node).toBeInstanceOf(e.KeyedRead);
}); });
@ -446,8 +446,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate property write', () => { it('should locate property write', () => {
const {errors, nodes, position} = parse(`<div (foo)="b¦ar=$event"></div>`); const {errors, nodes, position} = parse(`<div (foo)="b¦ar=$event"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyWrite); expect(node).toBeInstanceOf(e.PropertyWrite);
}); });
@ -455,8 +455,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate keyed write', () => { it('should locate keyed write', () => {
const {errors, nodes, position} = parse(`<div (foo)="bar['baz']¦=$event"></div>`); const {errors, nodes, position} = parse(`<div (foo)="bar['baz']¦=$event"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.KeyedWrite); expect(node).toBeInstanceOf(e.KeyedWrite);
}); });
@ -464,8 +464,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate binary', () => { it('should locate binary', () => {
const {errors, nodes, position} = parse(`{{ 1 +¦ 2 }}`); const {errors, nodes, position} = parse(`{{ 1 +¦ 2 }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.Binary); expect(node).toBeInstanceOf(e.Binary);
}); });
@ -473,8 +473,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate binding pipe with an identifier', () => { it('should locate binding pipe with an identifier', () => {
const {errors, nodes, position} = parse(`{{ title | p¦ }}`); const {errors, nodes, position} = parse(`{{ title | p¦ }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.BindingPipe); expect(node).toBeInstanceOf(e.BindingPipe);
}); });
@ -485,8 +485,8 @@ describe('getTargetAtPosition for expression AST', () => {
expect(errors![0].toString()) expect(errors![0].toString())
.toContain( .toContain(
'Unexpected end of input, expected identifier or keyword at the end of the expression'); 'Unexpected end of input, expected identifier or keyword at the end of the expression');
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
// TODO: We want this to be a BindingPipe. // TODO: We want this to be a BindingPipe.
expect(node).toBeInstanceOf(e.Interpolation); expect(node).toBeInstanceOf(e.Interpolation);
@ -498,7 +498,7 @@ describe('getTargetAtPosition for expression AST', () => {
// parser throws an error. This case is important for autocomplete. // parser throws an error. This case is important for autocomplete.
// const {errors, nodes, position} = parse(`{{ title | ¦ }}`); // const {errors, nodes, position} = parse(`{{ title | ¦ }}`);
// expect(errors).toBe(null); // expect(errors).toBe(null);
// const {nodeInContext} = findNodeAtPosition(nodes, position)!; // const {context} = findNodeAtPosition(nodes, position)!;
// expect(isExpressionNode(node!)).toBe(true); // expect(isExpressionNode(node!)).toBe(true);
// expect(node).toBeInstanceOf(e.BindingPipe); // expect(node).toBeInstanceOf(e.BindingPipe);
}); });
@ -507,8 +507,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate method call', () => { it('should locate method call', () => {
const {errors, nodes, position} = parse(`{{ title.toString(¦) }}`); const {errors, nodes, position} = parse(`{{ title.toString(¦) }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.MethodCall); expect(node).toBeInstanceOf(e.MethodCall);
}); });
@ -516,8 +516,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate safe method call', () => { it('should locate safe method call', () => {
const {errors, nodes, position} = parse(`{{ title?.toString(¦) }}`); const {errors, nodes, position} = parse(`{{ title?.toString(¦) }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.SafeMethodCall); expect(node).toBeInstanceOf(e.SafeMethodCall);
}); });
@ -525,8 +525,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate literal primitive in interpolation', () => { it('should locate literal primitive in interpolation', () => {
const {errors, nodes, position} = parse(`{{ title.indexOf('t¦') }}`); const {errors, nodes, position} = parse(`{{ title.indexOf('t¦') }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.LiteralPrimitive); expect(node).toBeInstanceOf(e.LiteralPrimitive);
expect((node as e.LiteralPrimitive).value).toBe('t'); expect((node as e.LiteralPrimitive).value).toBe('t');
@ -535,8 +535,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate literal primitive in binding', () => { it('should locate literal primitive in binding', () => {
const {errors, nodes, position} = parse(`<div [id]="'t¦'"></div>`); const {errors, nodes, position} = parse(`<div [id]="'t¦'"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.LiteralPrimitive); expect(node).toBeInstanceOf(e.LiteralPrimitive);
expect((node as e.LiteralPrimitive).value).toBe('t'); expect((node as e.LiteralPrimitive).value).toBe('t');
@ -545,8 +545,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate empty expression', () => { it('should locate empty expression', () => {
const {errors, nodes, position} = parse(`<div [id]="¦"></div>`); const {errors, nodes, position} = parse(`<div [id]="¦"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.EmptyExpr); expect(node).toBeInstanceOf(e.EmptyExpr);
}); });
@ -554,8 +554,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate literal array', () => { it('should locate literal array', () => {
const {errors, nodes, position} = parse(`{{ [1, 2,¦ 3] }}`); const {errors, nodes, position} = parse(`{{ [1, 2,¦ 3] }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.LiteralArray); expect(node).toBeInstanceOf(e.LiteralArray);
}); });
@ -563,8 +563,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate literal map', () => { it('should locate literal map', () => {
const {errors, nodes, position} = parse(`{{ { hello:¦ "world" } }}`); const {errors, nodes, position} = parse(`{{ { hello:¦ "world" } }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.LiteralMap); expect(node).toBeInstanceOf(e.LiteralMap);
}); });
@ -572,8 +572,8 @@ describe('getTargetAtPosition for expression AST', () => {
it('should locate conditional', () => { it('should locate conditional', () => {
const {errors, nodes, position} = parse(`{{ cond ?¦ true : false }}`); const {errors, nodes, position} = parse(`{{ cond ?¦ true : false }}`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.Conditional); expect(node).toBeInstanceOf(e.Conditional);
}); });
@ -583,8 +583,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate template key', () => { it('should locate template key', () => {
const {errors, nodes, position} = parse(`<div *ng¦If="foo"></div>`); const {errors, nodes, position} = parse(`<div *ng¦If="foo"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
}); });
@ -592,8 +592,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate template value', () => { it('should locate template value', () => {
const {errors, nodes, position} = parse(`<div *ngIf="f¦oo"></div>`); const {errors, nodes, position} = parse(`<div *ngIf="f¦oo"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
}); });
@ -601,8 +601,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate property read next to variable in structural directive syntax', () => { it('should locate property read next to variable in structural directive syntax', () => {
const {errors, nodes, position} = parse(`<div *ngIf="fo¦o as bar"></div>`); const {errors, nodes, position} = parse(`<div *ngIf="fo¦o as bar"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
}); });
@ -612,8 +612,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
// ngFor is a text attribute because the desugared form is // ngFor is a text attribute because the desugared form is
// <ng-template ngFor let-item [ngForOf]="items"> // <ng-template ngFor let-item [ngForOf]="items">
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBeTrue(); expect(isTemplateNode(node!)).toBeTrue();
expect(node).toBeInstanceOf(t.TextAttribute); expect(node).toBeInstanceOf(t.TextAttribute);
expect((node as t.TextAttribute).name).toBe('ngFor'); expect((node as t.TextAttribute).name).toBe('ngFor');
@ -629,8 +629,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate let variable', () => { it('should locate let variable', () => {
const {errors, nodes, position} = parse(`<div *ngFor="let i¦tem of items"></div>`); const {errors, nodes, position} = parse(`<div *ngFor="let i¦tem of items"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Variable); expect(node).toBeInstanceOf(t.Variable);
expect((node as t.Variable).name).toBe('item'); expect((node as t.Variable).name).toBe('item');
@ -639,8 +639,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate bound attribute key', () => { it('should locate bound attribute key', () => {
const {errors, nodes, position} = parse(`<div *ngFor="let item o¦f items"></div>`); const {errors, nodes, position} = parse(`<div *ngFor="let item o¦f items"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
expect((node as t.BoundAttribute).name).toBe('ngForOf'); expect((node as t.BoundAttribute).name).toBe('ngForOf');
@ -649,8 +649,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate bound attribute key when cursor is at the start', () => { it('should locate bound attribute key when cursor is at the start', () => {
const {errors, nodes, position} = parse(`<div *ngFor="let item ¦of items"></div>`); const {errors, nodes, position} = parse(`<div *ngFor="let item ¦of items"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const node = nodeInContext.node; const node = (context as SingleNodeTarget).node;
expect(isTemplateNode(node)).toBe(true); expect(isTemplateNode(node)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
expect((node as t.BoundAttribute).name).toBe('ngForOf'); expect((node as t.BoundAttribute).name).toBe('ngForOf');
@ -660,8 +660,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
const {errors, nodes, position} = const {errors, nodes, position} =
parse(`<div *ngFor="let item of items; trac¦kBy: trackByFn"></div>`); parse(`<div *ngFor="let item of items; trac¦kBy: trackByFn"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
expect((node as t.BoundAttribute).name).toBe('ngForTrackBy'); expect((node as t.BoundAttribute).name).toBe('ngForTrackBy');
@ -674,8 +674,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
const {errors, nodes, position} = const {errors, nodes, position} =
parse(`<div *ngFor="let item o¦f items; trackBy: trackByFn"></div>`); parse(`<div *ngFor="let item o¦f items; trackBy: trackByFn"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.BoundAttribute); expect(node).toBeInstanceOf(t.BoundAttribute);
expect((node as t.BoundAttribute).name).toBe('ngForOf'); expect((node as t.BoundAttribute).name).toBe('ngForOf');
@ -684,8 +684,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate bound attribute value', () => { it('should locate bound attribute value', () => {
const {errors, nodes, position} = parse(`<div *ngFor="let item of it¦ems"></div>`); const {errors, nodes, position} = parse(`<div *ngFor="let item of it¦ems"></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
expect((node as e.PropertyRead).name).toBe('items'); expect((node as e.PropertyRead).name).toBe('items');
@ -694,12 +694,12 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate template children', () => { it('should locate template children', () => {
const {errors, nodes, position} = parse(`<di¦v *ngIf></div>`); const {errors, nodes, position} = parse(`<di¦v *ngIf></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext, template: context} = getTargetAtPosition(nodes, position)!; const {context, template} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Element); expect(node).toBeInstanceOf(t.Element);
expect((node as t.Element).name).toBe('div'); expect((node as t.Element).name).toBe('div');
expect(context).toBeInstanceOf(t.Template); expect(template).toBeInstanceOf(t.Template);
}); });
it('should locate property read of variable declared within template', () => { it('should locate property read of variable declared within template', () => {
@ -708,8 +708,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
{{ i¦ }} {{ i¦ }}
</div>`); </div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
expect(node).toBeInstanceOf(e.PropertyRead); expect(node).toBeInstanceOf(e.PropertyRead);
}); });
@ -717,8 +717,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate LHS of variable declaration', () => { it('should locate LHS of variable declaration', () => {
const {errors, nodes, position} = parse(`<div *ngFor="let item of items; let i¦=index">`); const {errors, nodes, position} = parse(`<div *ngFor="let item of items; let i¦=index">`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Variable); expect(node).toBeInstanceOf(t.Variable);
// TODO: Currently there is no way to distinguish LHS from RHS // TODO: Currently there is no way to distinguish LHS from RHS
@ -728,8 +728,8 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate RHS of variable declaration', () => { it('should locate RHS of variable declaration', () => {
const {errors, nodes, position} = parse(`<div *ngFor="let item of items; let i=in¦dex">`); const {errors, nodes, position} = parse(`<div *ngFor="let item of items; let i=in¦dex">`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
const {node} = nodeInContext; const {node} = context as SingleNodeTarget;
expect(isTemplateNode(node!)).toBe(true); expect(isTemplateNode(node!)).toBe(true);
expect(node).toBeInstanceOf(t.Variable); expect(node).toBeInstanceOf(t.Variable);
// TODO: Currently there is no way to distinguish LHS from RHS // TODO: Currently there is no way to distinguish LHS from RHS
@ -739,16 +739,16 @@ describe('findNodeAtPosition for microsyntax expression', () => {
it('should locate an element in its tag context', () => { it('should locate an element in its tag context', () => {
const {errors, nodes, position} = parse(`<div¦ attr></div>`); const {errors, nodes, position} = parse(`<div¦ attr></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
expect(nodeInContext.kind).toBe(TargetNodeKind.ElementInTagContext); expect(context.kind).toBe(TargetNodeKind.ElementInTagContext);
expect(nodeInContext.node).toBeInstanceOf(t.Element); expect((context as SingleNodeTarget).node).toBeInstanceOf(t.Element);
}); });
it('should locate an element in its body context', () => { it('should locate an element in its body context', () => {
const {errors, nodes, position} = parse(`<div ¦ attr></div>`); const {errors, nodes, position} = parse(`<div ¦ attr></div>`);
expect(errors).toBe(null); expect(errors).toBe(null);
const {nodeInContext} = getTargetAtPosition(nodes, position)!; const {context} = getTargetAtPosition(nodes, position)!;
expect(nodeInContext.kind).toBe(TargetNodeKind.ElementInBodyContext); expect(context.kind).toBe(TargetNodeKind.ElementInBodyContext);
expect(nodeInContext.node).toBeInstanceOf(t.Element); expect((context as SingleNodeTarget).node).toBeInstanceOf(t.Element);
}); });
}); });