Skip to content

Commit

Permalink
fix(ivy): Prevent errors when querying for elements outside Angular c…
Browse files Browse the repository at this point in the history
…ontext

DebugElement.query also searches elements that may have been created
outside of Angular (ex: with `document.appendChild`). The current
behavior attempts to get the LContext of these nodes but throws an error
because the LContext does not exist.
  • Loading branch information
atscott committed Aug 30, 2019
1 parent 67d80f9 commit 67e0d89
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 19 deletions.
45 changes: 27 additions & 18 deletions packages/core/src/debug/debug_node.ts
Expand Up @@ -16,7 +16,7 @@ import {TStylingContext} from '../render3/styling_next/interfaces';
import {stylingMapToStringMap} from '../render3/styling_next/map_based_bindings';
import {NodeStylingDebug} from '../render3/styling_next/styling_debug';
import {isStylingContext} from '../render3/styling_next/util';
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext, loadLContextFromNode} from '../render3/util/discovery_utils';
import {getComponent, getContext, getInjectionTokens, getInjector, getListeners, getLocalRefs, isBrowserEvents, loadLContext} from '../render3/util/discovery_utils';
import {INTERPOLATION_DELIMITER, isPropMetadataString, renderStringify} from '../render3/util/misc_utils';
import {findComponentView} from '../render3/util/view_traversal_utils';
import {getComponentViewByIndex, getNativeByTNodeOrNull} from '../render3/util/view_utils';
Expand Down Expand Up @@ -272,7 +272,11 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
* - attribute bindings (e.g. `[attr.role]="menu"`)
*/
get properties(): {[key: string]: any;} {
const context = loadLContext(this.nativeNode) !;
const context = loadLContext(this.nativeNode, false);
if (context == null) {
return {};
}

const lView = context.lView;
const tData = lView[TVIEW].data;
const tNode = tData[context.nodeIndex] as TNode;
Expand All @@ -297,7 +301,11 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
return attributes;
}

const context = loadLContext(element);
const context = loadLContext(element, false);
if (context == null) {
return {};
}

const lView = context.lView;
const tNodeAttrs = (lView[TVIEW].data[context.nodeIndex] as TNode).attrs;
const lowercaseTNodeAttrs: string[] = [];
Expand Down Expand Up @@ -413,22 +421,23 @@ class DebugElement__POST_R3__ extends DebugNode__POST_R3__ implements DebugEleme
}

function _getStylingDebugInfo(element: any, isClassBased: boolean) {
if (element) {
const context = loadLContextFromNode(element);
const lView = context.lView;
const tData = lView[TVIEW].data;
const tNode = tData[context.nodeIndex] as TNode;
if (isClassBased) {
return isStylingContext(tNode.classes) ?
new NodeStylingDebug(tNode.classes as TStylingContext, lView, true).values :
stylingMapToStringMap(tNode.classes);
} else {
return isStylingContext(tNode.styles) ?
new NodeStylingDebug(tNode.styles as TStylingContext, lView, false).values :
stylingMapToStringMap(tNode.styles);
}
const context = loadLContext(element, false);
if (!context) {
return {};
}

const lView = context.lView;
const tData = lView[TVIEW].data;
const tNode = tData[context.nodeIndex] as TNode;
if (isClassBased) {
return isStylingContext(tNode.classes) ?
new NodeStylingDebug(tNode.classes as TStylingContext, lView, true).values :
stylingMapToStringMap(tNode.classes);
} else {
return isStylingContext(tNode.styles) ?
new NodeStylingDebug(tNode.styles as TStylingContext, lView, false).values :
stylingMapToStringMap(tNode.styles);
}
return {};
}

/**
Expand Down
39 changes: 38 additions & 1 deletion packages/core/test/debug/debug_node_spec.ts
Expand Up @@ -8,7 +8,7 @@


import {CommonModule, NgIfContext, ɵgetDOM as getDOM} from '@angular/common';
import {Component, DebugNode, Directive, ElementRef, EmbeddedViewRef, EventEmitter, HostBinding, Injectable, Input, NO_ERRORS_SCHEMA, OnInit, Output, Renderer2, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
import {Component, DebugElement, DebugNode, Directive, ElementRef, EmbeddedViewRef, EventEmitter, HostBinding, Injectable, Input, NO_ERRORS_SCHEMA, OnInit, Output, Renderer2, TemplateRef, ViewChild, ViewContainerRef} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {hasClass} from '@angular/platform-browser/testing/src/browser_util';
Expand Down Expand Up @@ -580,6 +580,43 @@ class TestCmptWithPropBindings {
expect(fixture.debugElement.query(By.css('.myclass'))).toBeTruthy();
});

describe('DebugElement.query doesn\'t fail on elements outside Angular context', () => {
@Component({template: '<div></div>'})
class NativeEl {
constructor(private elementRef: ElementRef) {}

ngAfterViewInit() {
this.elementRef.nativeElement.children[0].appendChild(document.createElement('p'));
}
}

let el: DebugElement;
beforeEach(() => {
const fixture =
TestBed.configureTestingModule({declarations: [NativeEl]}).createComponent(NativeEl);
fixture.detectChanges();
el = fixture.debugElement;
});

it('when searching for elements by name',
() => { expect(() => el.query(e => e.name === 'any search text')).not.toThrow(); });

it('when searching for elements by their attributes', () => {
expect(() => el.query(e => e.attributes !['name'] === 'any attribute')).not.toThrow();
});

it('when searching for elements by their classes',
() => { expect(() => el.query(e => e.classes['any class'] === true)).not.toThrow(); });

it('when searching for elements by their styles', () => {
expect(() => el.query(e => e.styles['any style'] === 'any value')).not.toThrow();
});

it('when searching for elements by their properties', () => {
expect(() => el.query(e => e.properties['any prop'] === 'any value')).not.toThrow();
});
});

it('DebugElement.queryAll should pick up both elements inserted via the view and through Renderer2',
() => {
@Directive({
Expand Down

0 comments on commit 67e0d89

Please sign in to comment.