diff --git a/packages/core/test/render3/BUILD.bazel b/packages/core/test/render3/BUILD.bazel index a27837958e..ccbb26d6f5 100644 --- a/packages/core/test/render3/BUILD.bazel +++ b/packages/core/test/render3/BUILD.bazel @@ -15,6 +15,10 @@ ts_library( "//packages:types", "//packages/core", "//packages/platform-browser", + "//packages/platform-browser/animations", + "//packages/animations", + "//packages/animations/browser", + "//packages/animations/browser/testing", ], ) diff --git a/packages/core/test/render3/component_spec.ts b/packages/core/test/render3/component_spec.ts index e23285c60f..0089db882d 100644 --- a/packages/core/test/render3/component_spec.ts +++ b/packages/core/test/render3/component_spec.ts @@ -170,6 +170,6 @@ describe('encapsulation', () => { renderComponent(WrapperComponentWith, getRendererFactory2(document)); expect(containerEl.outerHTML) .toEqual( - '
bar
'); + '
bar
'); }); }); diff --git a/packages/core/test/render3/imported_renderer2.ts b/packages/core/test/render3/imported_renderer2.ts index b2858cceb2..09742807bc 100644 --- a/packages/core/test/render3/imported_renderer2.ts +++ b/packages/core/test/render3/imported_renderer2.ts @@ -6,8 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ +import {ɵAnimationEngine, ɵNoopAnimationStyleNormalizer} from '@angular/animations/browser'; +import {MockAnimationDriver} from '@angular/animations/browser/testing'; import {EventEmitter, NgZone, RendererFactory2} from '@angular/core'; import {EventManager, ɵDomEventsPlugin, ɵDomRendererFactory2, ɵDomSharedStylesHost} from '@angular/platform-browser'; +import {ɵAnimationRendererFactory} from '@angular/platform-browser/animations'; + // Adapted renderer: it creates a Renderer2 instance and adapts it to Renderer3 // TODO: remove once this code is in angular/angular @@ -52,3 +56,11 @@ export function getRendererFactory2(document: any): RendererFactory2 { new EventManager([new SimpleDomEventsPlugin(document, fakeNgZone)], fakeNgZone); return new ɵDomRendererFactory2(eventManager, new ɵDomSharedStylesHost(document)); } + +export function getAnimationRendererFactory2(document: any): RendererFactory2 { + const fakeNgZone: NgZone = new NoopNgZone(); + return new ɵAnimationRendererFactory( + getRendererFactory2(document), + new ɵAnimationEngine(new MockAnimationDriver(), new ɵNoopAnimationStyleNormalizer()), + fakeNgZone); +} diff --git a/packages/core/test/render3/load_domino.ts b/packages/core/test/render3/load_domino.ts index 5857630e34..e07ef10a9c 100644 --- a/packages/core/test/render3/load_domino.ts +++ b/packages/core/test/render3/load_domino.ts @@ -6,8 +6,12 @@ * found in the LICENSE file at https://angular.io/license */ +// Needed to run animation tests +require('zone.js/dist/zone-node.js'); + if (typeof window == 'undefined') { - const createWindow = require('domino').createWindow; + const domino = require('domino'); + const createWindow = domino.createWindow; const window = createWindow('', 'http://localhost'); (global as any).document = window.document; @@ -16,4 +20,8 @@ if (typeof window == 'undefined') { // It fails with Domino with TypeError: Cannot assign to read only property // 'stopImmediatePropagation' of object '#' (global as any).Event = null; + + // For animation tests, see + // https://github.com/angular/angular/blob/master/packages/animations/browser/src/render/shared.ts#L140 + (global as any).Element = domino.impl.Element; } diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 88c9c0942c..573eee1735 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -33,8 +33,12 @@ requestAnimationFrame.flush = function() { export function resetDOM() { requestAnimationFrame.queue = []; + if (containerEl) { + document.body.removeChild(containerEl); + } containerEl = document.createElement('div'); containerEl.setAttribute('host', ''); + document.body.appendChild(containerEl); host = null; // TODO: assert that the global state is clean (e.g. ngData, previousOrParentNode, etc) } diff --git a/packages/core/test/render3/renderer_factory_spec.ts b/packages/core/test/render3/renderer_factory_spec.ts index 0efe207c61..3c21761a42 100644 --- a/packages/core/test/render3/renderer_factory_spec.ts +++ b/packages/core/test/render3/renderer_factory_spec.ts @@ -6,13 +6,15 @@ * found in the LICENSE file at https://angular.io/license */ -import {RendererType2} from '@angular/core'; +import {AnimationEvent} from '@angular/animations'; +import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing'; -import {D, E, e} from '../../src/render3'; -import {T, defineComponent, detectChanges} from '../../src/render3/index'; +import {RendererType2, ViewEncapsulation} from '../../src/core'; +import {D, E, L, T, b, defineComponent, detectChanges, e, p} from '../../src/render3'; +import {createRendererType2} from '../../src/view'; -import {getRendererFactory2} from './imported_renderer2'; -import {document, renderComponent, renderToHtml, resetDOM} from './render_util'; +import {getAnimationRendererFactory2, getRendererFactory2} from './imported_renderer2'; +import {containerEl, document, renderComponent, renderToHtml} from './render_util'; describe('renderer factory lifecycle', () => { let logs: string[] = []; @@ -104,3 +106,99 @@ describe('renderer factory lifecycle', () => { }); }); + +describe('animation renderer factory', () => { + let eventLogs: string[] = []; + function getLog(): MockAnimationPlayer[] { + return MockAnimationDriver.log as MockAnimationPlayer[]; + } + + function resetLog() { MockAnimationDriver.log = []; } + + beforeEach(() => { + eventLogs = []; + resetLog(); + }); + + class SomeComponent { + static ngComponentDef = defineComponent({ + type: SomeComponent, + tag: 'some-component', + template: function(ctx: SomeComponent, cm: boolean) { + if (cm) { + T(0, 'foo'); + } + }, + factory: () => new SomeComponent + }); + } + + class SomeComponentWithAnimation { + exp: string; + callback(event: AnimationEvent) { + eventLogs.push(`${event.fromState ? event.fromState : event.toState} - ${event.phaseName}`); + } + static ngComponentDef = defineComponent({ + type: SomeComponentWithAnimation, + tag: 'some-component', + template: function(ctx: SomeComponentWithAnimation, cm: boolean) { + if (cm) { + E(0, 'div'); + { + L('@myAnimation.start', ctx.callback.bind(ctx)); + L('@myAnimation.done', ctx.callback.bind(ctx)); + T(1, 'foo'); + } + e(); + } + p(0, '@myAnimation', b(ctx.exp)); + }, + factory: () => new SomeComponentWithAnimation, + rendererType: createRendererType2({ + encapsulation: ViewEncapsulation.None, + styles: [], + data: { + animation: [{ + type: 7, + name: 'myAnimation', + definitions: [{ + type: 1, + expr: '* => on', + animation: + [{type: 4, styles: {type: 6, styles: {opacity: 1}, offset: null}, timings: 10}], + options: null + }], + options: {} + }] + } + }), + }); + } + + it('should work with components without animations', () => { + renderComponent(SomeComponent, getAnimationRendererFactory2(document)); + expect(containerEl.innerHTML).toEqual('foo'); + }); + + it('should work with animated components', (done) => { + const factory = getAnimationRendererFactory2(document); + const component = renderComponent(SomeComponentWithAnimation, factory); + expect(containerEl.innerHTML) + .toEqual('
foo
'); + + component.exp = 'on'; + detectChanges(component); + + const [player] = getLog(); + expect(player.keyframes).toEqual([ + {opacity: '*', offset: 0}, + {opacity: 1, offset: 1}, + ]); + player.finish(); + + factory.whenRenderingDone !().then(() => { + expect(eventLogs).toEqual(['void - start', 'void - done', 'on - start', 'on - done']); + done(); + }); + }); +});