| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @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 {ParseError, parseTemplate} from '@angular/compiler'; | 
					
						
							|  |  |  | 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
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 09:50:02 -08:00
										 |  |  | import {getTargetAtPosition, SingleNodeTarget, TargetNodeKind, TwoWayBindingContext} from '../../template_target'; | 
					
						
							| 
									
										
										
										
											2020-11-04 12:28:37 -08:00
										 |  |  | import {isExpressionNode, isTemplateNode} from '../../utils'; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | interface ParseResult { | 
					
						
							|  |  |  |   nodes: t.Node[]; | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |   errors: ParseError[]|null; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   position: number; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function parse(template: string): ParseResult { | 
					
						
							|  |  |  |   const position = template.indexOf('¦'); | 
					
						
							|  |  |  |   if (position < 0) { | 
					
						
							|  |  |  |     throw new Error(`Template "${template}" does not contain the cursor`); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   template = template.replace('¦', ''); | 
					
						
							|  |  |  |   const templateUrl = '/foo'; | 
					
						
							|  |  |  |   return { | 
					
						
							| 
									
										
										
										
											2021-01-21 21:48:42 +01:00
										 |  |  |     ...parseTemplate(template, templateUrl, { | 
					
						
							|  |  |  |       // Set `leadingTriviaChars` and `preserveWhitespaces` such that whitespace is not stripped
 | 
					
						
							|  |  |  |       // and fully accounted for in source spans. Without these flags the source spans can be
 | 
					
						
							|  |  |  |       // inaccurate.
 | 
					
						
							|  |  |  |       // Note: template parse options should be aligned with the `diagNodes` in
 | 
					
						
							| 
									
										
										
										
											2021-01-26 15:52:38 -08:00
										 |  |  |       // `ComponentDecoratorHandler._parseTemplate`.
 | 
					
						
							| 
									
										
										
										
											2021-01-21 21:48:42 +01:00
										 |  |  |       leadingTriviaChars: [], | 
					
						
							|  |  |  |       preserveWhitespaces: true, | 
					
						
							| 
									
										
										
										
											2021-03-02 13:00:45 -08:00
										 |  |  |       alwaysAttemptHtmlToR3AstConversion: true, | 
					
						
							| 
									
										
										
										
											2021-01-21 21:48:42 +01:00
										 |  |  |     }), | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     position, | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 10:28:15 -07:00
										 |  |  | describe('getTargetAtPosition for template AST', () => { | 
					
						
							| 
									
										
										
										
											2021-03-02 13:00:45 -08:00
										 |  |  |   it('should locate incomplete tag', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div¦`); | 
					
						
							|  |  |  |     expect(errors?.length).toBe(1); | 
					
						
							|  |  |  |     expect(errors![0].msg).toContain('Opening tag "div" not terminated.'); | 
					
						
							|  |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							|  |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   it('should locate element in opening tag', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<di¦v></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate element in closing tag', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div></di¦v>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate element when cursor is at the beginning', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<¦div></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate element when cursor is at the end', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div¦></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate attribute key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div cla¦ss="foo"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate attribute value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div class="fo¦o"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     // TODO: Note that we do not have the ability to detect the RHS (yet)
 | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate bound attribute key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<test-cmp [fo¦o]="bar"></test-cmp>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate bound attribute value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<test-cmp [foo]="b¦ar"></test-cmp>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 22:11:57 -07:00
										 |  |  |   it('should not locate bound attribute if cursor is between key and value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<test-cmp [foo]¦="bar"></test-cmp>`); | 
					
						
							|  |  |  |     expect(errors).toBeNull(); | 
					
						
							| 
									
										
										
										
											2020-10-13 10:28:15 -07:00
										 |  |  |     const nodeInfo = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     expect(nodeInfo).toBeNull(); | 
					
						
							| 
									
										
										
										
											2020-09-24 22:11:57 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   it('should locate bound event key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<test-cmp (fo¦o)="bar()"></test-cmp>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundEvent); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate bound event value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<test-cmp (foo)="b¦ar()"></test-cmp>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.MethodCall); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate element children', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div><sp¦an></span></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |     expect((node as t.Element).name).toBe('span'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate element reference', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div #my¦div></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Reference); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template text attribute', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template ng¦If></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template bound attribute key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template [ng¦If]="foo"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template bound attribute value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template [ngIf]="f¦oo"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template bound attribute key in two-way binding', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template [(f¦oo)]="bar"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-17 09:50:02 -08:00
										 |  |  |     const {context, parent} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     expect(parent).toBeInstanceOf(t.Template); | 
					
						
							|  |  |  |     const {nodes: [boundAttribute, boundEvent]} = context as TwoWayBindingContext; | 
					
						
							|  |  |  |     expect(boundAttribute.name).toBe('foo'); | 
					
						
							|  |  |  |     expect(boundEvent.name).toBe('fooChange'); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template bound attribute value in two-way binding', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template [(foo)]="b¦ar"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							| 
									
										
										
										
											2020-12-17 09:50:02 -08:00
										 |  |  |     // It doesn't actually matter if the template target returns the read or the write.
 | 
					
						
							|  |  |  |     // When the template target returns a property read, we only use the LHS downstream because the
 | 
					
						
							|  |  |  |     // RHS would have its own node in the AST that would have been returned instead. The LHS of the
 | 
					
						
							|  |  |  |     // `e.PropertyWrite` is the same as the `e.PropertyRead`.
 | 
					
						
							|  |  |  |     expect((node instanceof e.PropertyRead) || (node instanceof e.PropertyWrite)).toBeTrue(); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead | e.PropertyWrite).name).toBe('bar'); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template bound event key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template (cl¦ick)="foo()"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundEvent); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template bound event value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template (click)="f¦oo()"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(node).toBeInstanceOf(e.MethodCall); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template attribute key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template i¦d="foo"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template attribute value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template id="f¦oo"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     // TODO: Note that we do not have the ability to detect the RHS (yet)
 | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template reference key via the # notation', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template #f¦oo></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Reference); | 
					
						
							|  |  |  |     expect((node as t.Reference).name).toBe('foo'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template reference key via the ref- notation', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template ref-fo¦o></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Reference); | 
					
						
							|  |  |  |     expect((node as t.Reference).name).toBe('foo'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template reference value via the # notation', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template #foo="export¦As"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Reference); | 
					
						
							|  |  |  |     expect((node as t.Reference).value).toBe('exportAs'); | 
					
						
							|  |  |  |     // TODO: Note that we do not have the ability to distinguish LHS and RHS
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template reference value via the ref- notation', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template ref-foo="export¦As"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Reference); | 
					
						
							|  |  |  |     expect((node as t.Reference).value).toBe('exportAs'); | 
					
						
							|  |  |  |     // TODO: Note that we do not have the ability to distinguish LHS and RHS
 | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template variable key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template let-f¦oo="bar"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Variable); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template variable value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template let-foo="b¦ar"></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Variable); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template children', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-template><d¦iv></div></ng-template>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate ng-content', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<ng-co¦ntent></ng-content>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Content); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate ng-content attribute key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse('<ng-content cla¦ss="red"></ng-content>'); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate ng-content attribute value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse('<ng-content class="r¦ed"></ng-content>'); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     // TODO: Note that we do not have the ability to detect the RHS (yet)
 | 
					
						
							|  |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should not locate implicit receiver', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div [foo]="¦bar"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate bound attribute key in two-way binding', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<cmp [(f¦oo)]="bar"></cmp>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-17 09:50:02 -08:00
										 |  |  |     const {context, parent} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     expect(parent).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |     const {nodes: [boundAttribute, boundEvent]} = context as TwoWayBindingContext; | 
					
						
							|  |  |  |     expect(boundAttribute.name).toBe('foo'); | 
					
						
							|  |  |  |     expect(boundEvent.name).toBe('fooChange'); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-17 09:50:02 -08:00
										 |  |  |   it('should locate node when in value span of two-way binding', () => { | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     const {errors, nodes, position} = parse(`<cmp [(foo)]="b¦ar"></cmp>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-17 09:50:02 -08:00
										 |  |  |     const {context, parent} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     // It doesn't actually matter if the template target returns the read or the write.
 | 
					
						
							|  |  |  |     // When the template target returns a property read, we only use the LHS downstream because the
 | 
					
						
							|  |  |  |     // RHS would have its own node in the AST that would have been returned instead. The LHS of the
 | 
					
						
							|  |  |  |     // `e.PropertyWrite` is the same as the `e.PropertyRead`.
 | 
					
						
							|  |  |  |     expect((parent instanceof t.BoundAttribute) || (parent instanceof t.BoundEvent)).toBe(true); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							| 
									
										
										
										
											2020-12-17 09:50:02 -08:00
										 |  |  |     expect((node instanceof e.PropertyRead) || (node instanceof e.PropertyWrite)).toBeTrue(); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead | e.PropertyWrite).name).toBe('bar'); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-10-01 17:09:38 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |   it('should locate switch value in ICUs', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<span i18n>{sw¦itch, plural, other {text}}"></span>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-10-01 17:09:38 +02:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead).name).toBe('switch'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate switch value in nested ICUs', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse( | 
					
						
							|  |  |  |         `<span i18n>{expr, plural, other { {ne¦sted, plural, =1 { {{nestedInterpolation}} }} }}"></span>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-10-01 17:09:38 +02:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead).name).toBe('nested'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate interpolation expressions in ICUs', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = | 
					
						
							|  |  |  |         parse(`<span i18n>{expr, plural, other { {{ i¦nterpolation }} }}"></span>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-10-01 17:09:38 +02:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead).name).toBe('interpolation'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate interpolation expressions in nested ICUs', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse( | 
					
						
							|  |  |  |         `<span i18n>{expr, plural, other { {nested, plural, =1 { {{n¦estedInterpolation}} }} }}"></span>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-10-01 17:09:38 +02:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead).name).toBe('nestedInterpolation'); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 10:28:15 -07:00
										 |  |  | describe('getTargetAtPosition for expression AST', () => { | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   it('should not locate implicit receiver', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ ¦title }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead).name).toBe('title'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate property read', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ ti¦tle }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead).name).toBe('title'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate safe property read', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ foo?¦.bar }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.SafePropertyRead); | 
					
						
							|  |  |  |     expect((node as e.SafePropertyRead).name).toBe('bar'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate keyed read', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ foo['bar']¦ }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.KeyedRead); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate property write', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div (foo)="b¦ar=$event"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyWrite); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate keyed write', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div (foo)="bar['baz']¦=$event"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.KeyedWrite); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate binary', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ 1 +¦ 2 }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.Binary); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate binding pipe with an identifier', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ title | p¦ }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.BindingPipe); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-24 12:50:00 -05:00
										 |  |  |   it('should locate binding pipe without identifier', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ title | ¦ }}`); | 
					
						
							|  |  |  |     expect(errors?.length).toBe(1); | 
					
						
							|  |  |  |     expect(errors![0].toString()) | 
					
						
							|  |  |  |         .toContain( | 
					
						
							|  |  |  |             'Unexpected end of input, expected identifier or keyword at the end of the expression'); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-10-24 12:50:00 -05:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							| 
									
										
										
										
											2021-01-07 14:16:05 -08:00
										 |  |  |     expect(node).toBeInstanceOf(e.BindingPipe); | 
					
						
							| 
									
										
										
										
											2020-10-24 12:50:00 -05:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 10:28:15 -07:00
										 |  |  |   it('should locate binding pipe without identifier', | 
					
						
							|  |  |  |      () => { | 
					
						
							|  |  |  |          // TODO: We are not able to locate pipe if identifier is missing because the
 | 
					
						
							|  |  |  |          // parser throws an error. This case is important for autocomplete.
 | 
					
						
							|  |  |  |          // const {errors, nodes, position} = parse(`{{ title | ¦ }}`);
 | 
					
						
							|  |  |  |          // expect(errors).toBe(null);
 | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |          // const {context} = findNodeAtPosition(nodes, position)!;
 | 
					
						
							| 
									
										
										
										
											2020-10-13 10:28:15 -07:00
										 |  |  |          // expect(isExpressionNode(node!)).toBe(true);
 | 
					
						
							|  |  |  |          // expect(node).toBeInstanceOf(e.BindingPipe);
 | 
					
						
							|  |  |  |      }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   it('should locate method call', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ title.toString(¦) }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.MethodCall); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate safe method call', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ title?.toString(¦) }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.SafeMethodCall); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate literal primitive in interpolation', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ title.indexOf('t¦') }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.LiteralPrimitive); | 
					
						
							|  |  |  |     expect((node as e.LiteralPrimitive).value).toBe('t'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate literal primitive in binding', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div [id]="'t¦'"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.LiteralPrimitive); | 
					
						
							|  |  |  |     expect((node as e.LiteralPrimitive).value).toBe('t'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate empty expression', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div [id]="¦"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.EmptyExpr); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate literal array', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ [1, 2,¦ 3] }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.LiteralArray); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate literal map', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ { hello:¦ "world" } }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.LiteralMap); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate conditional', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`{{ cond ?¦ true : false }}`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.Conditional); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('findNodeAtPosition for microsyntax expression', () => { | 
					
						
							|  |  |  |   it('should locate template key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ng¦If="foo"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundAttribute); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngIf="f¦oo"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 09:12:26 -08:00
										 |  |  |   it('should locate property read next to variable in structural directive syntax', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngIf="fo¦o as bar"></div>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-12-22 09:12:26 -08:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   it('should locate text attribute', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ng¦For="let item of items"></div>`); | 
					
						
							|  |  |  |     // ngFor is a text attribute because the desugared form is
 | 
					
						
							|  |  |  |     // <ng-template ngFor let-item [ngForOf]="items">
 | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-09-09 11:22:50 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBeTrue(); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.TextAttribute); | 
					
						
							|  |  |  |     expect((node as t.TextAttribute).name).toBe('ngFor'); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-13 10:28:15 -07:00
										 |  |  |   it('should not locate let keyword', () => { | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     const {errors, nodes, position} = parse(`<div *ngFor="l¦et item of items"></div>`); | 
					
						
							| 
									
										
										
										
											2020-10-13 10:28:15 -07:00
										 |  |  |     expect(errors).toBeNull(); | 
					
						
							|  |  |  |     const target = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     expect(target).toBeNull(); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate let variable', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngFor="let i¦tem of items"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Variable); | 
					
						
							|  |  |  |     expect((node as t.Variable).name).toBe('item'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate bound attribute key', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngFor="let item o¦f items"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundAttribute); | 
					
						
							|  |  |  |     expect((node as t.BoundAttribute).name).toBe('ngForOf'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 09:31:17 -08:00
										 |  |  |   it('should locate bound attribute key when cursor is at the start', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngFor="let item ¦of items"></div>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const node = (context as SingleNodeTarget).node; | 
					
						
							| 
									
										
										
										
											2020-11-18 14:07:30 -08:00
										 |  |  |     expect(isTemplateNode(node)).toBe(true); | 
					
						
							| 
									
										
										
										
											2020-12-09 09:39:33 -08:00
										 |  |  |     expect(node).toBeInstanceOf(t.BoundAttribute); | 
					
						
							|  |  |  |     expect((node as t.BoundAttribute).name).toBe('ngForOf'); | 
					
						
							| 
									
										
										
										
											2020-12-09 09:31:17 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-23 08:36:38 -07:00
										 |  |  |   it('should locate bound attribute key for trackBy', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = | 
					
						
							|  |  |  |         parse(`<div *ngFor="let item of items; trac¦kBy: trackByFn"></div>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-09-23 08:36:38 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundAttribute); | 
					
						
							|  |  |  |     expect((node as t.BoundAttribute).name).toBe('ngForTrackBy'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-30 08:59:47 -07:00
										 |  |  |   it('should locate first bound attribute when there are two', () => { | 
					
						
							|  |  |  |     // It used to be the case that all microsyntax bindings share the same
 | 
					
						
							|  |  |  |     // source span, so the second bound attribute would overwrite the first.
 | 
					
						
							|  |  |  |     // This has been fixed in pr/39036, this case is added to prevent regression.
 | 
					
						
							|  |  |  |     const {errors, nodes, position} = | 
					
						
							|  |  |  |         parse(`<div *ngFor="let item o¦f items; trackBy: trackByFn"></div>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-09-30 08:59:47 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.BoundAttribute); | 
					
						
							|  |  |  |     expect((node as t.BoundAttribute).name).toBe('ngForOf'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   it('should locate bound attribute value', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngFor="let item of it¦ems"></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |     expect((node as e.PropertyRead).name).toBe('items'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate template children', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<di¦v *ngIf></div>`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context, template} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Element); | 
					
						
							|  |  |  |     expect((node as t.Element).name).toBe('div'); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     expect(template).toBeInstanceOf(t.Template); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate property read of variable declared within template', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`
 | 
					
						
							|  |  |  |       <div *ngFor="let item of items; let i=index"> | 
					
						
							|  |  |  |         {{ i¦ }} | 
					
						
							|  |  |  |       </div>`);
 | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isExpressionNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(e.PropertyRead); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate LHS of variable declaration', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngFor="let item of items; let i¦=index">`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Variable); | 
					
						
							|  |  |  |     // TODO: Currently there is no way to distinguish LHS from RHS
 | 
					
						
							|  |  |  |     expect((node as t.Variable).name).toBe('i'); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate RHS of variable declaration', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div *ngFor="let item of items; let i=in¦dex">`); | 
					
						
							| 
									
										
										
										
											2020-08-26 17:10:04 +01:00
										 |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     const {node} = context as SingleNodeTarget; | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  |     expect(isTemplateNode(node!)).toBe(true); | 
					
						
							|  |  |  |     expect(node).toBeInstanceOf(t.Variable); | 
					
						
							|  |  |  |     // TODO: Currently there is no way to distinguish LHS from RHS
 | 
					
						
							|  |  |  |     expect((node as t.Variable).value).toBe('index'); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-11-18 14:07:30 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   it('should locate an element in its tag context', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div¦ attr></div>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     expect(context.kind).toBe(TargetNodeKind.ElementInTagContext); | 
					
						
							|  |  |  |     expect((context as SingleNodeTarget).node).toBeInstanceOf(t.Element); | 
					
						
							| 
									
										
										
										
											2020-11-18 14:07:30 -08:00
										 |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   it('should locate an element in its body context', () => { | 
					
						
							|  |  |  |     const {errors, nodes, position} = parse(`<div ¦ attr></div>`); | 
					
						
							|  |  |  |     expect(errors).toBe(null); | 
					
						
							| 
									
										
										
										
											2020-12-16 14:39:49 -08:00
										 |  |  |     const {context} = getTargetAtPosition(nodes, position)!; | 
					
						
							|  |  |  |     expect(context.kind).toBe(TargetNodeKind.ElementInBodyContext); | 
					
						
							|  |  |  |     expect((context as SingleNodeTarget).node).toBeInstanceOf(t.Element); | 
					
						
							| 
									
										
										
										
											2020-11-18 14:07:30 -08:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2020-08-12 16:13:02 -07:00
										 |  |  | }); |