test(ivy): move render3 renderer_factory tests to acceptance (#30435)
Moves all manual render3 tests which are located within the `renderer_factory_spec.ts` file to acceptance tests. A few tests that use Ivy-specific logic which is not replicable with `TestBed` remain in the render3 folder (e.g. using `renderTemplate`) Additionally migrated tests that assert the lifecycles of the renderer_factory are set to *ivy only* as the lifecycle seems to be different in Ivy. Tracked with: FW-1320 PR Close #30435
This commit is contained in:
parent
197584d1af
commit
a57f3e7bbf
|
@ -1,6 +1,6 @@
|
||||||
package(default_visibility = ["//visibility:private"])
|
package(default_visibility = ["//visibility:private"])
|
||||||
|
|
||||||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
|
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library", "ts_web_test_suite")
|
||||||
|
|
||||||
ts_library(
|
ts_library(
|
||||||
name = "acceptance_lib",
|
name = "acceptance_lib",
|
||||||
|
@ -9,6 +9,9 @@ ts_library(
|
||||||
["**/*.ts"],
|
["**/*.ts"],
|
||||||
),
|
),
|
||||||
deps = [
|
deps = [
|
||||||
|
"//packages/animations",
|
||||||
|
"//packages/animations/browser",
|
||||||
|
"//packages/animations/browser/testing",
|
||||||
"//packages/common",
|
"//packages/common",
|
||||||
"//packages/compiler",
|
"//packages/compiler",
|
||||||
"//packages/compiler/testing",
|
"//packages/compiler/testing",
|
||||||
|
@ -17,7 +20,9 @@ ts_library(
|
||||||
"//packages/core/testing",
|
"//packages/core/testing",
|
||||||
"//packages/platform-browser",
|
"//packages/platform-browser",
|
||||||
"//packages/platform-browser-dynamic",
|
"//packages/platform-browser-dynamic",
|
||||||
|
"//packages/platform-browser/animations",
|
||||||
"//packages/platform-browser/testing",
|
"//packages/platform-browser/testing",
|
||||||
|
"//packages/platform-server",
|
||||||
"//packages/private/testing",
|
"//packages/private/testing",
|
||||||
"@npm//zone.js",
|
"@npm//zone.js",
|
||||||
],
|
],
|
||||||
|
@ -34,3 +39,10 @@ jasmine_node_test(
|
||||||
"@npm//zone.js",
|
"@npm//zone.js",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ts_web_test_suite(
|
||||||
|
name = "acceptance_web",
|
||||||
|
deps = [
|
||||||
|
":acceptance_lib",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {AnimationEvent} from '@angular/animations';
|
||||||
|
import {ɵAnimationEngine, ɵNoopAnimationStyleNormalizer} from '@angular/animations/browser';
|
||||||
|
import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing';
|
||||||
|
import {DOCUMENT} from '@angular/common';
|
||||||
|
import {Component, DoCheck, NgZone, RendererFactory2, RendererType2} from '@angular/core';
|
||||||
|
import {NoopNgZone} from '@angular/core/src/zone/ng_zone';
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
import {EventManager, ɵDomSharedStylesHost} from '@angular/platform-browser';
|
||||||
|
import {ɵAnimationRendererFactory} from '@angular/platform-browser/animations';
|
||||||
|
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||||
|
import {ServerRendererFactory2} from '@angular/platform-server/src/server_renderer';
|
||||||
|
import {onlyInIvy} from '@angular/private/testing';
|
||||||
|
|
||||||
|
describe('renderer factory lifecycle', () => {
|
||||||
|
let logs: string[] = [];
|
||||||
|
|
||||||
|
@Component({selector: 'some-component', template: `foo`})
|
||||||
|
class SomeComponent implements DoCheck {
|
||||||
|
ngOnInit() { logs.push('some_component create'); }
|
||||||
|
ngDoCheck() { logs.push('some_component update'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'some-component-with-error', template: `With error`})
|
||||||
|
class SomeComponentWhichThrows {
|
||||||
|
ngOnInit() { throw new Error('SomeComponentWhichThrows threw'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'lol', template: `<some-component></some-component>`})
|
||||||
|
class TestComponent implements DoCheck {
|
||||||
|
ngOnInit() { logs.push('test_component create'); }
|
||||||
|
ngDoCheck() { logs.push('test_component update'); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a patched renderer factory that pushes entries to the test log */
|
||||||
|
function createPatchedRendererFactory(document: any) {
|
||||||
|
let rendererFactory = getRendererFactory2(document);
|
||||||
|
const createRender = rendererFactory.createRenderer;
|
||||||
|
|
||||||
|
rendererFactory.createRenderer = (hostElement: any, type: RendererType2 | null) => {
|
||||||
|
logs.push('create');
|
||||||
|
return createRender.apply(rendererFactory, [hostElement, type]);
|
||||||
|
};
|
||||||
|
|
||||||
|
rendererFactory.begin = () => logs.push('begin');
|
||||||
|
rendererFactory.end = () => logs.push('end');
|
||||||
|
|
||||||
|
return rendererFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
logs = [];
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SomeComponent, SomeComponentWhichThrows, TestComponent],
|
||||||
|
providers: [{
|
||||||
|
provide: RendererFactory2,
|
||||||
|
useFactory: (document: any) => createPatchedRendererFactory(document),
|
||||||
|
deps: [DOCUMENT]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('FW-1320: Ivy creates renderer twice.').it('should work with a component', () => {
|
||||||
|
const fixture = TestBed.createComponent(SomeComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(logs).toEqual(
|
||||||
|
['create', 'create', 'begin', 'some_component create', 'some_component update', 'end']);
|
||||||
|
|
||||||
|
logs = [];
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(logs).toEqual(['begin', 'some_component update', 'end']);
|
||||||
|
});
|
||||||
|
|
||||||
|
onlyInIvy('FW-1320: Ivy creates renderer twice.')
|
||||||
|
.it('should work with a component which throws', () => {
|
||||||
|
expect(() => {
|
||||||
|
const fixture = TestBed.createComponent(SomeComponentWhichThrows);
|
||||||
|
fixture.detectChanges();
|
||||||
|
}).toThrow();
|
||||||
|
expect(logs).toEqual(['create', 'create', 'begin', 'end']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('animation renderer factory', () => {
|
||||||
|
let eventLogs: string[] = [];
|
||||||
|
let rendererFactory: RendererFactory2|null = null;
|
||||||
|
|
||||||
|
function getAnimationLog(): MockAnimationPlayer[] {
|
||||||
|
return MockAnimationDriver.log as MockAnimationPlayer[];
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
eventLogs = [];
|
||||||
|
rendererFactory = null;
|
||||||
|
MockAnimationDriver.log = [];
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [SomeComponentWithAnimation, SomeComponent],
|
||||||
|
providers: [{
|
||||||
|
provide: RendererFactory2,
|
||||||
|
useFactory: (d: any) => rendererFactory = getAnimationRendererFactory2(d),
|
||||||
|
deps: [DOCUMENT]
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'some-component',
|
||||||
|
template: `
|
||||||
|
<div [@myAnimation]="exp"
|
||||||
|
(@myAnimation.start)="callback($event)"
|
||||||
|
(@myAnimation.done)="callback($event)">
|
||||||
|
foo
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
animations: [{
|
||||||
|
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: {}
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
class SomeComponentWithAnimation {
|
||||||
|
exp: string|undefined;
|
||||||
|
|
||||||
|
callback(event: AnimationEvent) {
|
||||||
|
eventLogs.push(`${event.fromState ? event.fromState : event.toState} - ${event.phaseName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'some-component', template: 'foo'})
|
||||||
|
class SomeComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should work with components without animations', () => {
|
||||||
|
const fixture = TestBed.createComponent(SomeComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(fixture.nativeElement.innerHTML).toEqual('foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
isBrowser && it('should work with animated components', (done) => {
|
||||||
|
const fixture = TestBed.createComponent(SomeComponentWithAnimation);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(rendererFactory).toBeTruthy();
|
||||||
|
expect(fixture.nativeElement.innerHTML)
|
||||||
|
.toMatch(/<div class="ng-tns-c\d+-0 ng-trigger ng-trigger-myAnimation">\s+foo\s+<\/div>/);
|
||||||
|
|
||||||
|
fixture.componentInstance.exp = 'on';
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const [player] = getAnimationLog();
|
||||||
|
expect(player.keyframes).toEqual([
|
||||||
|
{opacity: '*', offset: 0},
|
||||||
|
{opacity: 1, offset: 1},
|
||||||
|
]);
|
||||||
|
player.finish();
|
||||||
|
|
||||||
|
rendererFactory !.whenRenderingDone !().then(() => {
|
||||||
|
expect(eventLogs).toEqual(['void - start', 'void - done', 'on - start', 'on - done']);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function getRendererFactory2(document: any): RendererFactory2 {
|
||||||
|
const fakeNgZone: NgZone = new NoopNgZone();
|
||||||
|
const eventManager = new EventManager([], fakeNgZone);
|
||||||
|
const rendererFactory = new ServerRendererFactory2(
|
||||||
|
eventManager, fakeNgZone, document, new ɵDomSharedStylesHost(document));
|
||||||
|
const origCreateRenderer = rendererFactory.createRenderer;
|
||||||
|
rendererFactory.createRenderer = function() {
|
||||||
|
const renderer = origCreateRenderer.apply(this, arguments);
|
||||||
|
renderer.destroyNode = () => {};
|
||||||
|
return renderer;
|
||||||
|
};
|
||||||
|
return rendererFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAnimationRendererFactory2(document: any): RendererFactory2 {
|
||||||
|
const fakeNgZone: NgZone = new NoopNgZone();
|
||||||
|
return new ɵAnimationRendererFactory(
|
||||||
|
getRendererFactory2(document),
|
||||||
|
new ɵAnimationEngine(
|
||||||
|
document.body, new MockAnimationDriver(), new ɵNoopAnimationStyleNormalizer()),
|
||||||
|
fakeNgZone);
|
||||||
|
}
|
|
@ -6,16 +6,13 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationEvent} from '@angular/animations';
|
|
||||||
import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing';
|
|
||||||
|
|
||||||
import {RendererType2, ViewEncapsulation} from '../../src/core';
|
import {RendererType2, ViewEncapsulation} from '../../src/core';
|
||||||
import {ɵɵdefineComponent} from '../../src/render3/index';
|
import {ɵɵdefineComponent} from '../../src/render3/index';
|
||||||
import {tick, ɵɵbind, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementProperty, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵlistener, ɵɵtext} from '../../src/render3/instructions/all';
|
import {ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainerRefreshStart, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵembeddedViewEnd, ɵɵembeddedViewStart, ɵɵtext} from '../../src/render3/instructions/all';
|
||||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||||
|
|
||||||
import {getAnimationRendererFactory2, getRendererFactory2} from './imported_renderer2';
|
import {getRendererFactory2} from './imported_renderer2';
|
||||||
import {TemplateFixture, containerEl, document, renderComponent, renderToHtml, toHtml} from './render_util';
|
import {TemplateFixture, document, renderToHtml} from './render_util';
|
||||||
|
|
||||||
describe('renderer factory lifecycle', () => {
|
describe('renderer factory lifecycle', () => {
|
||||||
let logs: string[] = [];
|
let logs: string[] = [];
|
||||||
|
@ -87,21 +84,6 @@ describe('renderer factory lifecycle', () => {
|
||||||
|
|
||||||
beforeEach(() => { logs = []; });
|
beforeEach(() => { logs = []; });
|
||||||
|
|
||||||
it('should work with a component', () => {
|
|
||||||
const component = renderComponent(SomeComponent, {rendererFactory});
|
|
||||||
expect(logs).toEqual(
|
|
||||||
['create', 'create', 'begin', 'component create', 'component update', 'end']);
|
|
||||||
|
|
||||||
logs = [];
|
|
||||||
tick(component);
|
|
||||||
expect(logs).toEqual(['begin', 'component update', 'end']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with a component which throws', () => {
|
|
||||||
expect(() => renderComponent(SomeComponentWhichThrows, {rendererFactory})).toThrow();
|
|
||||||
expect(logs).toEqual(['create', 'create', 'begin', 'end']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work with a template', () => {
|
it('should work with a template', () => {
|
||||||
renderToHtml(Template, {}, 1, 0, null, null, rendererFactory);
|
renderToHtml(Template, {}, 1, 0, null, null, rendererFactory);
|
||||||
expect(logs).toEqual(['create', 'function create', 'function update']);
|
expect(logs).toEqual(['create', 'function create', 'function update']);
|
||||||
|
@ -125,108 +107,6 @@ 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,
|
|
||||||
encapsulation: ViewEncapsulation.None,
|
|
||||||
selectors: [['some-component']],
|
|
||||||
consts: 1,
|
|
||||||
vars: 0,
|
|
||||||
template: function(rf: RenderFlags, ctx: SomeComponent) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵtext(0, 'foo');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
factory: () => new SomeComponent
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class SomeComponentWithAnimation {
|
|
||||||
// TODO(issue/24571): remove '!'.
|
|
||||||
exp !: string;
|
|
||||||
callback(event: AnimationEvent) {
|
|
||||||
eventLogs.push(`${event.fromState ? event.fromState : event.toState} - ${event.phaseName}`);
|
|
||||||
}
|
|
||||||
static ngComponentDef = ɵɵdefineComponent({
|
|
||||||
type: SomeComponentWithAnimation,
|
|
||||||
selectors: [['some-component']],
|
|
||||||
consts: 2,
|
|
||||||
vars: 1,
|
|
||||||
template: function(rf: RenderFlags, ctx: SomeComponentWithAnimation) {
|
|
||||||
if (rf & RenderFlags.Create) {
|
|
||||||
ɵɵelementStart(0, 'div');
|
|
||||||
{
|
|
||||||
ɵɵlistener('@myAnimation.start', ctx.callback.bind(ctx));
|
|
||||||
ɵɵlistener('@myAnimation.done', ctx.callback.bind(ctx));
|
|
||||||
ɵɵtext(1, 'foo');
|
|
||||||
}
|
|
||||||
ɵɵelementEnd();
|
|
||||||
}
|
|
||||||
if (rf & RenderFlags.Update) {
|
|
||||||
ɵɵelementProperty(0, '@myAnimation', ɵɵbind(ctx.exp));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
factory: () => new SomeComponentWithAnimation,
|
|
||||||
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, {rendererFactory: getAnimationRendererFactory2(document)});
|
|
||||||
expect(toHtml(containerEl)).toEqual('foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
isBrowser && it('should work with animated components', (done) => {
|
|
||||||
const rendererFactory = getAnimationRendererFactory2(document);
|
|
||||||
const component = renderComponent(SomeComponentWithAnimation, {rendererFactory});
|
|
||||||
expect(toHtml(containerEl))
|
|
||||||
.toMatch(/<div class="ng-tns-c\d+-0 ng-trigger ng-trigger-myAnimation">foo<\/div>/);
|
|
||||||
|
|
||||||
component.exp = 'on';
|
|
||||||
tick(component);
|
|
||||||
|
|
||||||
const [player] = getLog();
|
|
||||||
expect(player.keyframes).toEqual([
|
|
||||||
{opacity: '*', offset: 0},
|
|
||||||
{opacity: 1, offset: 1},
|
|
||||||
]);
|
|
||||||
player.finish();
|
|
||||||
|
|
||||||
rendererFactory.whenRenderingDone !().then(() => {
|
|
||||||
expect(eventLogs).toEqual(['void - start', 'void - done', 'on - start', 'on - done']);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Renderer2 destruction hooks', () => {
|
describe('Renderer2 destruction hooks', () => {
|
||||||
const rendererFactory = getRendererFactory2(document);
|
const rendererFactory = getRendererFactory2(document);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue