fix(core): view engine - fix perf regressions (#14345)
- Make sure `NodeDef`s don’t fall into dictionary mode. - Use strategy pattern to add debug information / checks, instead of constantly checking for `isDevMode`. - introduce a very light weight `RendererV2` interface to not have duplicate code paths for direct and non direct rendering The strategy pattern is implemented via the new `Services` object. Part of #14013 PR Close #14345
This commit is contained in:

committed by
Miško Hevery

parent
f6b5965a63
commit
24af51a623
@ -7,40 +7,22 @@
|
||||
*/
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||
import {DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||
import {createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
if (isBrowser()) {
|
||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
||||
}
|
||||
defineTests({directDom: false, viewFlags: 0});
|
||||
}
|
||||
|
||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
describe(`View Anchor, directDom: ${config.directDom}`, () => {
|
||||
setupAndCheckRenderer(config);
|
||||
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe(`View Anchor`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||
return viewDef(ViewFlags.None, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
viewDef: ViewDefinition, ctx?: any): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef, ctx);
|
||||
const view = createRootView(viewDef, ctx);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -69,14 +51,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(getDOM().childNodes(rootNodes[0]).length).toBe(1);
|
||||
});
|
||||
|
||||
if (!config.directDom) {
|
||||
it('should add debug information to the renderer', () => {
|
||||
const someContext = new Object();
|
||||
const {view, rootNodes} = createAndGetRootNodes(
|
||||
compViewDef([anchorDef(NodeFlags.None, null, null, 0)]), someContext);
|
||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
||||
});
|
||||
}
|
||||
it('should add debug information to the renderer', () => {
|
||||
const someContext = new Object();
|
||||
const {view, rootNodes} = createAndGetRootNodes(
|
||||
compViewDef([anchorDef(NodeFlags.None, null, null, 0)]), someContext);
|
||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -7,40 +7,22 @@
|
||||
*/
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||
import {BindingType, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, anchorDef, asProviderData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, directiveDef, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, anchorDef, asProviderData, directiveDef, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootData, isBrowser, removeNodes, setupAndCheckRenderer} from './helper';
|
||||
import {createRootView, isBrowser, removeNodes} from './helper';
|
||||
|
||||
export function main() {
|
||||
if (isBrowser()) {
|
||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
||||
}
|
||||
defineTests({directDom: false, viewFlags: 0});
|
||||
}
|
||||
|
||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
describe(`Component Views, directDom: ${config.directDom}`, () => {
|
||||
setupAndCheckRenderer(config);
|
||||
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe(`Component Views`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
flags?: ViewFlags): ViewDefinition {
|
||||
return viewDef(config.viewFlags | flags, nodes, update, handleEvent, renderComponentType);
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef);
|
||||
const view = createRootView(viewDef);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -79,39 +61,41 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
});
|
||||
|
||||
it('should select root elements based on a selector', () => {
|
||||
rootData.selectorOrNode = 'root';
|
||||
const view = createRootView(rootData, compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||
]));
|
||||
const view = createRootView(
|
||||
compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||
]),
|
||||
{}, [], 'root');
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
expect(rootNodes).toEqual([rootNode]);
|
||||
});
|
||||
|
||||
it('should select root elements based on a node', () => {
|
||||
rootData.selectorOrNode = rootNode;
|
||||
const view = createRootView(rootData, compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||
]));
|
||||
const view = createRootView(
|
||||
compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||
]),
|
||||
{}, [], rootNode);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
expect(rootNodes).toEqual([rootNode]);
|
||||
});
|
||||
|
||||
it('should set attributes on the root node', () => {
|
||||
rootData.selectorOrNode = rootNode;
|
||||
const view =
|
||||
createRootView(rootData, compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||
]));
|
||||
const view = createRootView(
|
||||
compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||
]),
|
||||
{}, [], rootNode);
|
||||
expect(rootNode.getAttribute('a')).toBe('b');
|
||||
});
|
||||
|
||||
it('should clear the content of the root node', () => {
|
||||
rootData.selectorOrNode = rootNode;
|
||||
rootNode.appendChild(document.createElement('div'));
|
||||
const view =
|
||||
createRootView(rootData, compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||
]));
|
||||
const view = createRootView(
|
||||
compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
|
||||
]),
|
||||
{}, [], rootNode);
|
||||
expect(rootNode.childNodes.length).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -124,10 +108,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
a: any;
|
||||
}
|
||||
|
||||
const update = jasmine.createSpy('updater').and.callFake((view: ViewData) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInline(value);
|
||||
});
|
||||
const update =
|
||||
jasmine.createSpy('updater').and.callFake((check: NodeCheckFn, view: ViewData) => {
|
||||
check(view, 0, ArgumentType.Inline, value);
|
||||
});
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(
|
||||
compViewDef([
|
||||
@ -141,19 +125,19 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
value = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
expect(update).toHaveBeenCalledWith(compView);
|
||||
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||
|
||||
update.calls.reset();
|
||||
checkNoChangesView(view);
|
||||
Services.checkNoChangesView(view);
|
||||
|
||||
expect(update).toHaveBeenCalledWith(compView);
|
||||
expect(update.calls.mostRecent().args[1]).toBe(compView);
|
||||
|
||||
value = 'v2';
|
||||
expect(() => checkNoChangesView(view))
|
||||
expect(() => Services.checkNoChangesView(view))
|
||||
.toThrowError(
|
||||
`Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
||||
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
||||
});
|
||||
|
||||
it('should support detaching and attaching component views for dirty checking', () => {
|
||||
@ -176,15 +160,15 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
update.calls.reset();
|
||||
|
||||
compView.state &= ~ViewState.ChecksEnabled;
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
|
||||
compView.state |= ViewState.ChecksEnabled;
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -212,40 +196,37 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
],
|
||||
update, null, ViewFlags.OnPush)),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 1);
|
||||
checkNodeInline(compInputValue);
|
||||
}));
|
||||
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
|
||||
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
// auto detach
|
||||
update.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
|
||||
// auto attach on input changes
|
||||
update.calls.reset();
|
||||
compInputValue = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).toHaveBeenCalled();
|
||||
|
||||
// auto detach
|
||||
update.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
|
||||
// auto attach on events
|
||||
addListenerSpy.calls.mostRecent().args[1]('SomeEvent');
|
||||
update.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).toHaveBeenCalled();
|
||||
|
||||
// auto detach
|
||||
update.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
@ -263,22 +244,19 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
NodeFlags.None, null, 0, AComp, [], null, null,
|
||||
() => compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 0, 'span'),
|
||||
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
|
||||
],
|
||||
update)),
|
||||
]));
|
||||
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
update.and.callFake((view: ViewData) => {
|
||||
setCurrentNode(view, 0);
|
||||
throw new Error('Test');
|
||||
});
|
||||
expect(() => checkAndUpdateView(view)).toThrow();
|
||||
update.and.callFake((check: NodeCheckFn, view: ViewData) => { throw new Error('Test'); });
|
||||
expect(() => Services.checkAndUpdateView(view)).toThrowError('Test');
|
||||
expect(update).toHaveBeenCalled();
|
||||
|
||||
update.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -304,7 +282,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
])),
|
||||
]));
|
||||
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
|
||||
expect(log).toEqual(['ngOnDestroy']);
|
||||
});
|
||||
@ -314,12 +292,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 0, 'div'),
|
||||
],
|
||||
(view) => { setCurrentNode(view, 0); }));
|
||||
(view) => {}));
|
||||
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
|
||||
expect(() => checkAndUpdateView(view))
|
||||
.toThrowError('View has been used after destroy for CheckAndUpdate');
|
||||
expect(() => Services.checkAndUpdateView(view))
|
||||
.toThrowError('ViewDestroyedError: Attempt to use a destroyed view: detectChanges');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -8,40 +8,23 @@
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {BindingType, DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, BindingType, DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, removeNodes, setupAndCheckRenderer} from './helper';
|
||||
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser, removeNodes} from './helper';
|
||||
|
||||
export function main() {
|
||||
if (isBrowser()) {
|
||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
||||
}
|
||||
defineTests({directDom: false, viewFlags: 0});
|
||||
}
|
||||
|
||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
describe(`View Elements, directDom: ${config.directDom}`, () => {
|
||||
setupAndCheckRenderer(config);
|
||||
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe(`View Elements`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef, context);
|
||||
const view = createRootView(viewDef, context);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -81,19 +64,17 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(getDOM().getAttribute(rootNodes[0], 'title')).toBe('a');
|
||||
});
|
||||
|
||||
if (!config.directDom) {
|
||||
it('should add debug information to the renderer', () => {
|
||||
const someContext = new Object();
|
||||
const {view, rootNodes} = createAndGetRootNodes(
|
||||
compViewDef([elementDef(NodeFlags.None, null, null, 0, 'div')]), someContext);
|
||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
||||
});
|
||||
}
|
||||
it('should add debug information to the renderer', () => {
|
||||
const someContext = new Object();
|
||||
const {view, rootNodes} = createAndGetRootNodes(
|
||||
compViewDef([elementDef(NodeFlags.None, null, null, 0, 'div')]), someContext);
|
||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asElementData(view, 0).renderElement);
|
||||
});
|
||||
});
|
||||
|
||||
describe('change properties', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
@ -104,27 +85,24 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
[BindingType.ElementProperty, 'value', SecurityContext.NONE]
|
||||
]),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, ['v1', 'v2']);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
|
||||
}));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const el = rootNodes[0];
|
||||
expect(getDOM().getProperty(el, 'title')).toBe('v1');
|
||||
expect(getDOM().getProperty(el, 'value')).toBe('v2');
|
||||
|
||||
if (!config.directDom) {
|
||||
expect(getDOM().getAttribute(el, 'ng-reflect-title')).toBe('v1');
|
||||
}
|
||||
expect(getDOM().getAttribute(el, 'ng-reflect-title')).toBe('v1');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('change attributes', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(
|
||||
@ -134,12 +112,11 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
[BindingType.ElementAttribute, 'a2', SecurityContext.NONE]
|
||||
]),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, ['v1', 'v2']);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['v1', 'v2']);
|
||||
}));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const el = rootNodes[0];
|
||||
expect(getDOM().getAttribute(el, 'a1')).toBe('v1');
|
||||
@ -149,20 +126,19 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
});
|
||||
|
||||
describe('change classes', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(
|
||||
NodeFlags.None, null, null, 0, 'div', null,
|
||||
[[BindingType.ElementClass, 'c1'], [BindingType.ElementClass, 'c2']]),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [true, true]);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [true, true]);
|
||||
}));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const el = rootNodes[0];
|
||||
expect(getDOM().hasClass(el, 'c1')).toBeTruthy();
|
||||
@ -172,8 +148,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
});
|
||||
|
||||
describe('change styles', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(
|
||||
@ -183,12 +159,11 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
[BindingType.ElementStyle, 'color', null]
|
||||
]),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [10, 'red']);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [10, 'red']);
|
||||
}));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const el = rootNodes[0];
|
||||
expect(getDOM().getStyle(el, 'width')).toBe('10px');
|
||||
@ -198,8 +173,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
});
|
||||
|
||||
describe('general binding behavior', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
@ -210,25 +185,24 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
[BindingType.ElementProperty, 'someProp', SecurityContext.NONE],
|
||||
]),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
const setterSpy = jasmine.createSpy('set');
|
||||
Object.defineProperty(rootNodes[0], 'someProp', {set: setterSpy});
|
||||
|
||||
bindingValue = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
setterSpy.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).not.toHaveBeenCalled();
|
||||
|
||||
setterSpy.calls.reset();
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
});
|
||||
});
|
||||
@ -265,7 +239,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(handleEventArgs[2]).toBe('click');
|
||||
expect(handleEventArgs[3]).toBeTruthy();
|
||||
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
@ -291,7 +265,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(handleEventArgs[2]).toBe('windowClick');
|
||||
expect(handleEventArgs[3]).toBeTruthy();
|
||||
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
@ -317,7 +291,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(handleEventArgs[2]).toBe('documentClick');
|
||||
expect(handleEventArgs[3]).toBeTruthy();
|
||||
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
|
||||
expect(removeListenerSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -7,44 +7,27 @@
|
||||
*/
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
|
||||
import {BindingType, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||
import {createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
if (isBrowser()) {
|
||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
||||
}
|
||||
defineTests({directDom: false, viewFlags: 0});
|
||||
}
|
||||
|
||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
describe(`Embedded Views, directDom: ${config.directDom}`, () => {
|
||||
setupAndCheckRenderer(config);
|
||||
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe(`Embedded Views`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update);
|
||||
return viewDef(ViewFlags.None, nodes, update);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef, context);
|
||||
const view = createRootView(viewDef, context);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -62,7 +45,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
]),
|
||||
parentContext);
|
||||
|
||||
const childView = createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
||||
const childView =
|
||||
Services.createEmbeddedView(parentView, parentView.def.nodes[1], childContext);
|
||||
expect(childView.component).toBe(parentContext);
|
||||
expect(childView.context).toBe(childContext);
|
||||
});
|
||||
@ -79,8 +63,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
]));
|
||||
const viewContainerData = asElementData(parentView, 1);
|
||||
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
|
||||
attachEmbeddedView(viewContainerData, 0, childView0);
|
||||
attachEmbeddedView(viewContainerData, 1, childView1);
|
||||
@ -109,8 +93,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
]));
|
||||
const viewContainerData = asElementData(parentView, 1);
|
||||
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView1 = Services.createEmbeddedView(parentView, parentView.def.nodes[2]);
|
||||
|
||||
attachEmbeddedView(viewContainerData, 0, childView0);
|
||||
attachEmbeddedView(viewContainerData, 1, childView1);
|
||||
@ -133,7 +117,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
elementDef(NodeFlags.None, null, null, 0, 'span', {'name': 'after'})
|
||||
]));
|
||||
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[0]);
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[0]);
|
||||
attachEmbeddedView(asElementData(parentView, 0), 0, childView0);
|
||||
|
||||
const rootNodes = rootRenderNodes(parentView);
|
||||
@ -144,10 +128,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
it('should dirty check embedded views', () => {
|
||||
let childValue = 'v1';
|
||||
const update = jasmine.createSpy('updater').and.callFake((view: ViewData) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInline(childValue);
|
||||
});
|
||||
const update =
|
||||
jasmine.createSpy('updater').and.callFake((check: NodeCheckFn, view: ViewData) => {
|
||||
check(view, 0, ArgumentType.Inline, childValue);
|
||||
});
|
||||
|
||||
const {view: parentView, rootNodes} = createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||
@ -160,24 +144,24 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
update))
|
||||
]));
|
||||
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
|
||||
const rootEl = rootNodes[0];
|
||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
||||
|
||||
checkAndUpdateView(parentView);
|
||||
Services.checkAndUpdateView(parentView);
|
||||
|
||||
expect(update).toHaveBeenCalledWith(childView0);
|
||||
expect(update.calls.mostRecent().args[1]).toBe(childView0);
|
||||
|
||||
update.calls.reset();
|
||||
checkNoChangesView(parentView);
|
||||
Services.checkNoChangesView(parentView);
|
||||
|
||||
expect(update).toHaveBeenCalledWith(childView0);
|
||||
expect(update.calls.mostRecent().args[1]).toBe(childView0);
|
||||
|
||||
childValue = 'v2';
|
||||
expect(() => checkNoChangesView(parentView))
|
||||
expect(() => Services.checkNoChangesView(parentView))
|
||||
.toThrowError(
|
||||
`Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
||||
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'v1'. Current value: 'v2'.`);
|
||||
});
|
||||
|
||||
it('should destroy embedded views', () => {
|
||||
@ -195,10 +179,10 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
]))
|
||||
]));
|
||||
|
||||
const childView0 = createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
const childView0 = Services.createEmbeddedView(parentView, parentView.def.nodes[1]);
|
||||
|
||||
attachEmbeddedView(asElementData(parentView, 1), 0, childView0);
|
||||
destroyView(parentView);
|
||||
Services.destroyView(parentView);
|
||||
|
||||
expect(log).toEqual(['ngOnDestroy']);
|
||||
});
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Injector, RootRenderer, Sanitizer} from '@angular/core';
|
||||
import {RootData, checkNodeDynamic, checkNodeInline} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, NodeCheckFn, RootData, Services, ViewData, ViewDefinition, initServicesIfNeeded} from '@angular/core/src/view/index';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
@ -15,53 +15,25 @@ export function isBrowser() {
|
||||
return getDOM().supportsDOMEvents();
|
||||
}
|
||||
|
||||
export function setupAndCheckRenderer(config: {directDom: boolean}) {
|
||||
let rootRenderer: any;
|
||||
if (config.directDom) {
|
||||
beforeEach(() => {
|
||||
rootRenderer = <any>{
|
||||
renderComponent: jasmine.createSpy('renderComponent')
|
||||
.and.throwError('Renderer should not have been called!')
|
||||
};
|
||||
TestBed.configureTestingModule(
|
||||
{providers: [{provide: RootRenderer, useValue: rootRenderer}]});
|
||||
});
|
||||
afterEach(() => { expect(rootRenderer.renderComponent).not.toHaveBeenCalled(); });
|
||||
} else {
|
||||
beforeEach(() => {
|
||||
rootRenderer = TestBed.get(RootRenderer);
|
||||
spyOn(rootRenderer, 'renderComponent').and.callThrough();
|
||||
});
|
||||
afterEach(() => { expect(rootRenderer.renderComponent).toHaveBeenCalled(); });
|
||||
export const ARG_TYPE_VALUES = [ArgumentType.Inline, ArgumentType.Dynamic];
|
||||
|
||||
export function checkNodeInlineOrDynamic(
|
||||
check: NodeCheckFn, view: ViewData, nodeIndex: number, argType: ArgumentType,
|
||||
values: any[]): any {
|
||||
switch (argType) {
|
||||
case ArgumentType.Inline:
|
||||
return (<any>check)(view, nodeIndex, argType, ...values);
|
||||
case ArgumentType.Dynamic:
|
||||
return check(view, nodeIndex, argType, values);
|
||||
}
|
||||
}
|
||||
|
||||
export enum InlineDynamic {
|
||||
Inline,
|
||||
Dynamic
|
||||
}
|
||||
|
||||
export const INLINE_DYNAMIC_VALUES = [InlineDynamic.Inline, InlineDynamic.Dynamic];
|
||||
|
||||
export function checkNodeInlineOrDynamic(inlineDynamic: InlineDynamic, values: any[]): any {
|
||||
switch (inlineDynamic) {
|
||||
case InlineDynamic.Inline:
|
||||
return (<any>checkNodeInline)(...values);
|
||||
case InlineDynamic.Dynamic:
|
||||
return checkNodeDynamic(values);
|
||||
}
|
||||
}
|
||||
|
||||
export function createRootData(projectableNodes?: any[][], rootSelectorOrNode?: any): RootData {
|
||||
const injector = TestBed.get(Injector);
|
||||
const renderer = injector.get(RootRenderer);
|
||||
const sanitizer = injector.get(Sanitizer);
|
||||
projectableNodes = projectableNodes || [];
|
||||
return <RootData>{
|
||||
injector,
|
||||
projectableNodes,
|
||||
selectorOrNode: rootSelectorOrNode, sanitizer, renderer
|
||||
};
|
||||
export function createRootView(
|
||||
def: ViewDefinition, context?: any, projectableNodes?: any[][],
|
||||
rootSelectorOrNode?: any): ViewData {
|
||||
initServicesIfNeeded();
|
||||
return Services.createRootView(
|
||||
TestBed.get(Injector), projectableNodes || [], rootSelectorOrNode, def, context);
|
||||
}
|
||||
|
||||
export let removeNodes: Node[];
|
||||
|
@ -7,39 +7,22 @@
|
||||
*/
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||
import {DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||
import {createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
if (isBrowser()) {
|
||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
||||
}
|
||||
defineTests({directDom: false, viewFlags: 0});
|
||||
}
|
||||
|
||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
describe(`View NgContent, directDom: ${config.directDom}`, () => {
|
||||
setupAndCheckRenderer(config);
|
||||
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe(`View NgContent`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update);
|
||||
return viewDef(ViewFlags.None, nodes, update);
|
||||
}
|
||||
|
||||
function hostElDef(contentNodes: NodeDef[], viewNodes: NodeDef[]): NodeDef[] {
|
||||
@ -56,7 +39,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
function createAndGetRootNodes(
|
||||
viewDef: ViewDefinition, ctx?: any): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef, ctx || {});
|
||||
const view = createRootView(viewDef, ctx || {});
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -125,7 +108,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
])));
|
||||
|
||||
const componentView = asProviderData(view, 1).componentView;
|
||||
const view0 = createEmbeddedView(componentView, componentView.def.nodes[1]);
|
||||
const view0 = Services.createEmbeddedView(componentView, componentView.def.nodes[1]);
|
||||
|
||||
attachEmbeddedView(asElementData(componentView, 1), 0, view0);
|
||||
expect(getDOM().childNodes(getDOM().firstChild(rootNodes[0])).length).toBe(2);
|
||||
@ -138,14 +121,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
if (isBrowser()) {
|
||||
it('should use root projectable nodes', () => {
|
||||
rootData.projectableNodes =
|
||||
[[document.createTextNode('a')], [document.createTextNode('b')]];
|
||||
const projectableNodes = [[document.createTextNode('a')], [document.createTextNode('b')]];
|
||||
const view = createRootView(
|
||||
compViewDef(hostElDef([], [ngContentDef(null, 0), ngContentDef(null, 1)])), {},
|
||||
projectableNodes);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(
|
||||
compViewDef(hostElDef([], [ngContentDef(null, 0), ngContentDef(null, 1)])));
|
||||
|
||||
expect(getDOM().childNodes(rootNodes[0])[0]).toBe(rootData.projectableNodes[0][0]);
|
||||
expect(getDOM().childNodes(rootNodes[0])[1]).toBe(rootData.projectableNodes[1][0]);
|
||||
expect(getDOM().childNodes(rootNodes[0])[0]).toBe(projectableNodes[0][0]);
|
||||
expect(getDOM().childNodes(rootNodes[0])[1]).toBe(projectableNodes[1][0]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -8,43 +8,27 @@
|
||||
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, destroyView, directiveDef, elementDef, providerDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {ArgumentType, BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {TestBed, inject, withModule} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
if (isBrowser()) {
|
||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
||||
}
|
||||
defineTests({directDom: false, viewFlags: 0});
|
||||
}
|
||||
|
||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
describe(`View Providers, directDom: ${config.directDom}`, () => {
|
||||
setupAndCheckRenderer(config);
|
||||
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe(`View Providers`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(
|
||||
viewFlags, nodes, update, handleEvent, 'someCompId', ViewEncapsulation.None, []);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update);
|
||||
return viewDef(ViewFlags.None, nodes, update);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef);
|
||||
const view = createRootView(viewDef, {});
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -127,10 +111,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [])
|
||||
]));
|
||||
createRootView(
|
||||
compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'div'),
|
||||
directiveDef(
|
||||
NodeFlags.None, null, 0, SomeService, [], null, null,
|
||||
() => compViewDef([textDef(null, ['a'])]))
|
||||
]),
|
||||
TestBed.get(Injector), [], getDOM().createElement('div'));
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
@ -138,8 +126,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(err.message).toBe('Test');
|
||||
const debugCtx = getDebugContext(err);
|
||||
expect(debugCtx.view).toBeTruthy();
|
||||
// errors should point to the already existing element
|
||||
expect(debugCtx.nodeIndex).toBe(0);
|
||||
expect(debugCtx.nodeIndex).toBe(1);
|
||||
});
|
||||
|
||||
describe('deps', () => {
|
||||
@ -229,17 +216,15 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(instance.dep).toBe('someParentValue');
|
||||
});
|
||||
|
||||
it('should ask the root injector', () => {
|
||||
const getSpy = spyOn(rootData.injector, 'get');
|
||||
getSpy.and.returnValue('rootValue');
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, ['rootDep'])
|
||||
]));
|
||||
it('should ask the root injector',
|
||||
withModule({providers: [{provide: 'rootDep', useValue: 'rootValue'}]}, () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, ['rootDep'])
|
||||
]));
|
||||
|
||||
expect(instance.dep).toBe('rootValue');
|
||||
expect(getSpy).toHaveBeenCalledWith('rootDep', Injector.THROW_IF_NOT_FOUND);
|
||||
});
|
||||
expect(instance.dep).toBe('rootValue');
|
||||
}));
|
||||
|
||||
describe('builtin tokens', () => {
|
||||
it('should inject ViewContainerRef', () => {
|
||||
@ -302,24 +287,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(instance.dep._view).toBe(compView);
|
||||
});
|
||||
|
||||
if (config.directDom) {
|
||||
it('should not inject Renderer when using directDom', () => {
|
||||
expect(() => createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
||||
])))
|
||||
.toThrowError('No provider for Renderer!');
|
||||
});
|
||||
} else {
|
||||
it('should inject Renderer when not using directDom', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
||||
]));
|
||||
it('should inject RendererV1', () => {
|
||||
createAndGetRootNodes(compViewDef([
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [Renderer])
|
||||
]));
|
||||
|
||||
expect(instance.dep.createElement).toBeTruthy();
|
||||
});
|
||||
}
|
||||
expect(instance.dep.createElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@ -327,8 +302,8 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
describe('data binding', () => {
|
||||
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let instance: SomeService;
|
||||
|
||||
class SomeService {
|
||||
@ -342,23 +317,20 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a'], b: [1, 'b']})
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 1);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, ['v1', 'v2']);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, ['v1', 'v2']);
|
||||
}));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
expect(instance.a).toBe('v1');
|
||||
expect(instance.b).toBe('v2');
|
||||
|
||||
if (!config.directDom) {
|
||||
const el = rootNodes[0];
|
||||
expect(getDOM().getAttribute(el, 'ng-reflect-a')).toBe('v1');
|
||||
}
|
||||
const el = rootNodes[0];
|
||||
expect(getDOM().getAttribute(el, 'ng-reflect-a')).toBe('v1');
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
let setterSpy = jasmine.createSpy('set');
|
||||
|
||||
@ -371,22 +343,21 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(NodeFlags.None, null, 0, SomeService, [], {a: [0, 'a']})
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 1);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
bindingValue = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
setterSpy.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).not.toHaveBeenCalled();
|
||||
|
||||
setterSpy.calls.reset();
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
});
|
||||
@ -422,7 +393,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
emitter.emit('someEventInstance');
|
||||
expect(handleEvent).toHaveBeenCalledWith(view, 0, 'someEventName', 'someEventInstance');
|
||||
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
expect(unsubscribeSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -485,14 +456,12 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
directiveDef(allFlags, null, 0, SomeService, [], {a: [0, 'a']})
|
||||
],
|
||||
(updater) => {
|
||||
setCurrentNode(view, 1);
|
||||
checkNodeInline('someValue');
|
||||
setCurrentNode(view, 3);
|
||||
checkNodeInline('someValue');
|
||||
(check, view) => {
|
||||
check(view, 1, ArgumentType.Inline, 'someValue');
|
||||
check(view, 3, ArgumentType.Inline, 'someValue');
|
||||
}));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
// Note: After... hooks are called bottom up.
|
||||
expect(log).toEqual([
|
||||
@ -513,7 +482,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
]);
|
||||
|
||||
log = [];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
// Note: After... hooks are called bottom up.
|
||||
expect(log).toEqual([
|
||||
@ -522,7 +491,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
]);
|
||||
|
||||
log = [];
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
|
||||
// Note: ngOnDestroy ist called bottom up.
|
||||
expect(log).toEqual(['1_ngOnDestroy', '0_ngOnDestroy']);
|
||||
@ -545,17 +514,14 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
directiveDef(
|
||||
NodeFlags.OnChanges, null, 0, SomeService, [], {a: [0, 'nonMinifiedA']})
|
||||
],
|
||||
(updater) => {
|
||||
setCurrentNode(view, 1);
|
||||
checkNodeInline(currValue);
|
||||
}));
|
||||
(check, view) => { check(view, 1, ArgumentType.Inline, currValue); }));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(changesLog).toEqual([new SimpleChange(undefined, 'v1', true)]);
|
||||
|
||||
currValue = 'v2';
|
||||
changesLog = [];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(changesLog).toEqual([new SimpleChange('v1', 'v2', false)]);
|
||||
});
|
||||
|
||||
@ -571,7 +537,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
@ -582,7 +548,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(debugCtx.nodeIndex).toBe(1);
|
||||
});
|
||||
|
||||
it('should add a DebugContext to errors in destroyView', () => {
|
||||
it('should add a DebugContext to errors inServices.destroyView', () => {
|
||||
class SomeService implements OnDestroy {
|
||||
ngOnDestroy() { throw new Error('Test'); }
|
||||
}
|
||||
@ -594,7 +560,7 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
destroyView(view);
|
||||
Services.destroyView(view);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
@ -7,29 +7,21 @@
|
||||
*/
|
||||
|
||||
import {Injector, PipeTransform, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue} from '@angular/core';
|
||||
import {NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, directiveDef, elementDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asProviderData, asPureExpressionData, directiveDef, elementDef, pureArrayDef, pureObjectDef, purePipeDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
|
||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData} from './helper';
|
||||
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView} from './helper';
|
||||
|
||||
export function main() {
|
||||
describe(`View Pure Expressions`, () => {
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(ViewFlags.None, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef);
|
||||
const view = createRootView(viewDef);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -40,8 +32,8 @@ export function main() {
|
||||
|
||||
describe('pure arrays', () => {
|
||||
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support ${ArgumentType[inlineDynamic]} bindings`, () => {
|
||||
let values: any[];
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
@ -49,55 +41,52 @@ export function main() {
|
||||
elementDef(NodeFlags.None, null, null, 2, 'span'), pureArrayDef(2),
|
||||
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 1);
|
||||
const pureValue = checkNodeInlineOrDynamic(inlineDynamic, values);
|
||||
setCurrentNode(view, 2);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [pureValue]);
|
||||
(check, view) => {
|
||||
const pureValue = checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, values);
|
||||
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [pureValue]);
|
||||
}));
|
||||
const service = asProviderData(view, 2).instance;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const arr0 = service.data;
|
||||
expect(arr0).toEqual([1, 2]);
|
||||
|
||||
// instance should not change
|
||||
// if the values don't change
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(service.data).toBe(arr0);
|
||||
|
||||
values = [3, 2];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const arr1 = service.data;
|
||||
expect(arr1).not.toBe(arr0);
|
||||
expect(arr1).toEqual([3, 2]);
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
pureArrayDef(1),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 1);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
const exprData = asPureExpressionData(view, 1);
|
||||
|
||||
bindingValue = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const v1Arr = exprData.value;
|
||||
expect(v1Arr).toEqual(['v1']);
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).toBe(v1Arr);
|
||||
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).not.toBe(v1Arr);
|
||||
expect(exprData.value).toEqual(['v1']);
|
||||
});
|
||||
@ -106,8 +95,8 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('pure objects', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support ${ArgumentType[inlineDynamic]} bindings`, () => {
|
||||
let values: any[];
|
||||
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
@ -115,55 +104,52 @@ export function main() {
|
||||
elementDef(NodeFlags.None, null, null, 2, 'span'), pureObjectDef(['a', 'b']),
|
||||
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 1);
|
||||
const pureValue = checkNodeInlineOrDynamic(inlineDynamic, values);
|
||||
setCurrentNode(view, 2);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [pureValue]);
|
||||
(check, view) => {
|
||||
const pureValue = checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, values);
|
||||
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [pureValue]);
|
||||
}));
|
||||
const service = asProviderData(view, 2).instance;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const obj0 = service.data;
|
||||
expect(obj0).toEqual({a: 1, b: 2});
|
||||
|
||||
// instance should not change
|
||||
// if the values don't change
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(service.data).toBe(obj0);
|
||||
|
||||
values = [3, 2];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const obj1 = service.data;
|
||||
expect(obj1).not.toBe(obj0);
|
||||
expect(obj1).toEqual({a: 3, b: 2});
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
elementDef(NodeFlags.None, null, null, 1, 'span'),
|
||||
pureObjectDef(['a']),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 1);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 1, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
const exprData = asPureExpressionData(view, 1);
|
||||
|
||||
bindingValue = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const v1Obj = exprData.value;
|
||||
expect(v1Obj).toEqual({'a': 'v1'});
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).toBe(v1Obj);
|
||||
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(exprData.value).not.toBe(v1Obj);
|
||||
expect(exprData.value).toEqual({'a': 'v1'});
|
||||
});
|
||||
@ -171,8 +157,8 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('pure pipes', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support ${InlineDynamic[inlineDynamic]} bindings`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should support ${ArgumentType[inlineDynamic]} bindings`, () => {
|
||||
class SomePipe implements PipeTransform {
|
||||
transform(v1: any, v2: any) { return [v1 + 10, v2 + 20]; }
|
||||
}
|
||||
@ -185,32 +171,30 @@ export function main() {
|
||||
directiveDef(NodeFlags.None, null, 0, SomePipe, []), purePipeDef(SomePipe, 2),
|
||||
directiveDef(NodeFlags.None, null, 0, Service, [], {data: [0, 'data']})
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 2);
|
||||
const pureValue = checkNodeInlineOrDynamic(inlineDynamic, values);
|
||||
setCurrentNode(view, 3);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [pureValue]);
|
||||
(check, view) => {
|
||||
const pureValue = checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, values);
|
||||
checkNodeInlineOrDynamic(check, view, 3, inlineDynamic, [pureValue]);
|
||||
}));
|
||||
const service = asProviderData(view, 3).instance;
|
||||
|
||||
values = [1, 2];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const obj0 = service.data;
|
||||
expect(obj0).toEqual([11, 22]);
|
||||
|
||||
// instance should not change
|
||||
// if the values don't change
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(service.data).toBe(obj0);
|
||||
|
||||
values = [3, 2];
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const obj1 = service.data;
|
||||
expect(obj1).not.toBe(obj0);
|
||||
expect(obj1).toEqual([13, 22]);
|
||||
});
|
||||
|
||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
let transformSpy = jasmine.createSpy('transform');
|
||||
|
||||
@ -224,21 +208,20 @@ export function main() {
|
||||
directiveDef(NodeFlags.None, null, 0, SomePipe, []),
|
||||
purePipeDef(SomePipe, 1),
|
||||
],
|
||||
(view) => {
|
||||
setCurrentNode(view, 2);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 2, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
bindingValue = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(transformSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
transformSpy.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(transformSpy).not.toHaveBeenCalled();
|
||||
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(transformSpy).toHaveBeenCalledWith('v1');
|
||||
});
|
||||
});
|
||||
|
@ -8,26 +8,18 @@
|
||||
|
||||
import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createEmbeddedView, createRootView, destroyView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootData} from './helper';
|
||||
import {createRootView} from './helper';
|
||||
|
||||
export function main() {
|
||||
describe(`Query Views`, () => {
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(ViewFlags.None, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
|
||||
@ -36,7 +28,7 @@ export function main() {
|
||||
|
||||
function createAndGetRootNodes(
|
||||
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef, context);
|
||||
const view = createRootView(viewDef, context);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -79,7 +71,7 @@ export function main() {
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a).toBeUndefined();
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const as = qs.a.toArray();
|
||||
expect(as.length).toBe(2);
|
||||
@ -97,7 +89,7 @@ export function main() {
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = asProviderData(view, 3).instance;
|
||||
expect(qs.a.length).toBe(0);
|
||||
@ -114,7 +106,7 @@ export function main() {
|
||||
])),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const comp: QueryService = asProviderData(view, 1).instance;
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
@ -131,7 +123,7 @@ export function main() {
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
const comp: QueryService = asProviderData(view, 1).instance;
|
||||
expect(comp.a.length).toBe(0);
|
||||
});
|
||||
@ -153,9 +145,9 @@ export function main() {
|
||||
...contentQueryProviders(),
|
||||
]));
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
// queries on parent elements of anchors
|
||||
const qs1: QueryService = asProviderData(view, 1).instance;
|
||||
@ -185,11 +177,11 @@ export function main() {
|
||||
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0),
|
||||
]));
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||
// attach at a different place than the one where the template was defined
|
||||
attachEmbeddedView(asElementData(view, 7), 0, childView);
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
// query on the declaration place
|
||||
const qs1: QueryService = asProviderData(view, 1).instance;
|
||||
@ -215,19 +207,19 @@ export function main() {
|
||||
])),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.length).toBe(0);
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
expect(qs.a.length).toBe(1);
|
||||
|
||||
detachEmbeddedView(asElementData(view, 3), 0);
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
expect(qs.a.length).toBe(0);
|
||||
});
|
||||
@ -247,20 +239,20 @@ export function main() {
|
||||
])),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const comp: QueryService = asProviderData(view, 1).instance;
|
||||
expect(comp.a.length).toBe(0);
|
||||
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
const childView = createEmbeddedView(compView, compView.def.nodes[0]);
|
||||
const childView = Services.createEmbeddedView(compView, compView.def.nodes[0]);
|
||||
attachEmbeddedView(asElementData(compView, 0), 0, childView);
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
expect(comp.a.length).toBe(1);
|
||||
|
||||
detachEmbeddedView(asElementData(compView, 0), 0);
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
expect(comp.a.length).toBe(0);
|
||||
});
|
||||
@ -280,7 +272,7 @@ export function main() {
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a instanceof QueryList).toBeTruthy();
|
||||
@ -303,7 +295,7 @@ export function main() {
|
||||
aServiceProvider(),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a).toBe(asProviderData(view, 3).instance);
|
||||
@ -322,7 +314,7 @@ export function main() {
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.nativeElement).toBe(asElementData(view, 0).renderElement);
|
||||
@ -341,7 +333,7 @@ export function main() {
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.createEmbeddedView).toBeTruthy();
|
||||
@ -358,7 +350,7 @@ export function main() {
|
||||
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const qs: QueryService = asProviderData(view, 1).instance;
|
||||
expect(qs.a.createEmbeddedView).toBeTruthy();
|
||||
@ -380,22 +372,22 @@ export function main() {
|
||||
])),
|
||||
]));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
checkNoChangesView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
Services.checkNoChangesView(view);
|
||||
|
||||
const childView = createEmbeddedView(view, view.def.nodes[3]);
|
||||
const childView = Services.createEmbeddedView(view, view.def.nodes[3]);
|
||||
attachEmbeddedView(asElementData(view, 3), 0, childView);
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
checkNoChangesView(view);
|
||||
Services.checkNoChangesView(view);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
expect(err).toBeTruthy();
|
||||
expect(err.message)
|
||||
.toBe(
|
||||
`Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`);
|
||||
`ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'Query query1 not dirty'. Current value: 'Query query1 dirty'.`);
|
||||
const debugCtx = getDebugContext(err);
|
||||
expect(debugCtx.view).toBe(view);
|
||||
expect(debugCtx.nodeIndex).toBe(2);
|
||||
@ -416,7 +408,7 @@ export function main() {
|
||||
|
||||
let err: any;
|
||||
try {
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
@ -7,31 +7,23 @@
|
||||
*/
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, getDebugNode} from '@angular/core';
|
||||
import {DebugContext, NodeDef, NodeFlags, QueryValueType, Refs, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, directiveDef, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {DebugContext, NodeDef, NodeFlags, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, directiveDef, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||
import {createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
describe('View References', () => {
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe('View Services', () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(ViewFlags.None, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
viewDef: ViewDefinition, context: any = null): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef, context);
|
||||
const view = createRootView(viewDef, context);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -58,7 +50,7 @@ export function main() {
|
||||
const view = createViewWithData();
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
const debugCtx = Refs.createDebugContext(compView, 0);
|
||||
const debugCtx = Services.createDebugContext(compView, 0);
|
||||
|
||||
expect(debugCtx.componentRenderElement).toBe(asElementData(view, 0).renderElement);
|
||||
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
||||
@ -75,7 +67,7 @@ export function main() {
|
||||
const view = createViewWithData();
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
const debugCtx = Refs.createDebugContext(compView, 2);
|
||||
const debugCtx = Services.createDebugContext(compView, 2);
|
||||
|
||||
expect(debugCtx.componentRenderElement).toBe(asElementData(view, 0).renderElement);
|
||||
expect(debugCtx.renderNode).toBe(asTextData(compView, 2).renderText);
|
||||
@ -89,7 +81,7 @@ export function main() {
|
||||
const view = createViewWithData();
|
||||
const compView = asProviderData(view, 1).componentView;
|
||||
|
||||
const debugCtx = Refs.createDebugContext(compView, 1);
|
||||
const debugCtx = Services.createDebugContext(compView, 1);
|
||||
|
||||
expect(debugCtx.renderNode).toBe(asElementData(compView, 0).renderElement);
|
||||
});
|
@ -7,40 +7,23 @@
|
||||
*/
|
||||
|
||||
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
|
||||
import {DebugContext, NodeDef, NodeFlags, RootData, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asTextData, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, createRootView, elementDef, rootRenderNodes, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {ArgumentType, DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asTextData, elementDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {inject} from '@angular/core/testing';
|
||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||
|
||||
import {INLINE_DYNAMIC_VALUES, InlineDynamic, checkNodeInlineOrDynamic, createRootData, isBrowser, setupAndCheckRenderer} from './helper';
|
||||
import {ARG_TYPE_VALUES, checkNodeInlineOrDynamic, createRootView, isBrowser} from './helper';
|
||||
|
||||
export function main() {
|
||||
if (isBrowser()) {
|
||||
defineTests({directDom: true, viewFlags: ViewFlags.DirectDom});
|
||||
}
|
||||
defineTests({directDom: false, viewFlags: 0});
|
||||
}
|
||||
|
||||
function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
describe(`View Text, directDom: ${config.directDom}`, () => {
|
||||
setupAndCheckRenderer(config);
|
||||
|
||||
let rootData: RootData;
|
||||
let renderComponentType: RenderComponentType;
|
||||
|
||||
beforeEach(() => {
|
||||
rootData = createRootData();
|
||||
renderComponentType =
|
||||
new RenderComponentType('1', 'someUrl', 0, ViewEncapsulation.None, [], {});
|
||||
});
|
||||
|
||||
describe(`View Text`, () => {
|
||||
function compViewDef(
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
|
||||
return viewDef(config.viewFlags, nodes, update, handleEvent, renderComponentType);
|
||||
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
|
||||
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
|
||||
return viewDef(viewFlags, nodes, update, handleEvent);
|
||||
}
|
||||
|
||||
function createAndGetRootNodes(
|
||||
viewDef: ViewDefinition, context?: any): {rootNodes: any[], view: ViewData} {
|
||||
const view = createRootView(rootData, viewDef, context);
|
||||
const view = createRootView(viewDef, context);
|
||||
const rootNodes = rootRenderNodes(view);
|
||||
return {rootNodes, view};
|
||||
}
|
||||
@ -69,36 +52,33 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
expect(getDOM().getText(textNode)).toBe('a');
|
||||
});
|
||||
|
||||
if (!config.directDom) {
|
||||
it('should add debug information to the renderer', () => {
|
||||
const someContext = new Object();
|
||||
const {view, rootNodes} =
|
||||
createAndGetRootNodes(compViewDef([textDef(null, ['a'])]), someContext);
|
||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asTextData(view, 0).renderText);
|
||||
});
|
||||
}
|
||||
it('should add debug information to the renderer', () => {
|
||||
const someContext = new Object();
|
||||
const {view, rootNodes} =
|
||||
createAndGetRootNodes(compViewDef([textDef(null, ['a'])]), someContext);
|
||||
expect(getDebugNode(rootNodes[0]).nativeNode).toBe(asTextData(view, 0).renderText);
|
||||
});
|
||||
});
|
||||
|
||||
describe('change text', () => {
|
||||
INLINE_DYNAMIC_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
ARG_TYPE_VALUES.forEach((inlineDynamic) => {
|
||||
it(`should update ${ArgumentType[inlineDynamic]}`, () => {
|
||||
const {view, rootNodes} = createAndGetRootNodes(compViewDef(
|
||||
[
|
||||
textDef(null, ['0', '1', '2']),
|
||||
],
|
||||
(view: ViewData) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, ['a', 'b']);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, ['a', 'b']);
|
||||
}));
|
||||
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
|
||||
const node = rootNodes[0];
|
||||
expect(getDOM().getText(rootNodes[0])).toBe('0a1b2');
|
||||
});
|
||||
|
||||
if (isBrowser()) {
|
||||
it(`should unwrap values with ${InlineDynamic[inlineDynamic]}`, () => {
|
||||
it(`should unwrap values with ${ArgumentType[inlineDynamic]}`, () => {
|
||||
let bindingValue: any;
|
||||
const setterSpy = jasmine.createSpy('set');
|
||||
|
||||
@ -112,24 +92,23 @@ function defineTests(config: {directDom: boolean, viewFlags: number}) {
|
||||
[
|
||||
textDef(null, ['', '']),
|
||||
],
|
||||
(view: ViewData) => {
|
||||
setCurrentNode(view, 0);
|
||||
checkNodeInlineOrDynamic(inlineDynamic, [bindingValue]);
|
||||
(check, view) => {
|
||||
checkNodeInlineOrDynamic(check, view, 0, inlineDynamic, [bindingValue]);
|
||||
}));
|
||||
|
||||
Object.defineProperty(rootNodes[0], 'nodeValue', {set: setterSpy});
|
||||
|
||||
bindingValue = 'v1';
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
|
||||
setterSpy.calls.reset();
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).not.toHaveBeenCalled();
|
||||
|
||||
setterSpy.calls.reset();
|
||||
bindingValue = WrappedValue.wrap('v1');
|
||||
checkAndUpdateView(view);
|
||||
Services.checkAndUpdateView(view);
|
||||
expect(setterSpy).toHaveBeenCalledWith('v1');
|
||||
});
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NodeFlags, QueryValueType, ViewData, ViewDefinition, ViewFlags, anchorDef, checkAndUpdateView, checkNoChangesView, checkNodeDynamic, checkNodeInline, directiveDef, elementDef, setCurrentNode, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
import {NodeFlags, QueryValueType, ViewData, ViewDefinition, ViewFlags, anchorDef, directiveDef, elementDef, textDef, viewDef} from '@angular/core/src/view/index';
|
||||
|
||||
export function main() {
|
||||
describe('viewDef', () => {
|
||||
|
Reference in New Issue
Block a user