File diff suppressed because it is too large
Load Diff
@ -10,376 +10,377 @@ import {AnimationDriver, ɵAnimationEngine} from '@angular/animations/browser';
|
||||
import {TransitionAnimationPlayer} from '@angular/animations/browser/src/render/transition_animation_engine';
|
||||
import {MockAnimationDriver, MockAnimationPlayer} from '@angular/animations/browser/testing';
|
||||
import {Component, HostBinding} from '@angular/core';
|
||||
import {TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing';
|
||||
import {fakeAsync, flushMicrotasks, TestBed, tick} from '@angular/core/testing';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {ActivatedRoute, Router, RouterOutlet} from '@angular/router';
|
||||
import {RouterTestingModule} from '@angular/router/testing';
|
||||
|
||||
(function() {
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
if (isNode) return;
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
if (isNode) return;
|
||||
|
||||
describe('Animation Router Tests', function() {
|
||||
function getLog(): MockAnimationPlayer[] {
|
||||
return MockAnimationDriver.log as MockAnimationPlayer[];
|
||||
}
|
||||
describe('Animation Router Tests', function() {
|
||||
function getLog(): MockAnimationPlayer[] {
|
||||
return MockAnimationDriver.log as MockAnimationPlayer[];
|
||||
}
|
||||
|
||||
function resetLog() { MockAnimationDriver.log = []; }
|
||||
function resetLog() {
|
||||
MockAnimationDriver.log = [];
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
resetLog();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule, BrowserAnimationsModule],
|
||||
providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}]
|
||||
});
|
||||
beforeEach(() => {
|
||||
resetLog();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [RouterTestingModule, BrowserAnimationsModule],
|
||||
providers: [{provide: AnimationDriver, useClass: MockAnimationDriver}]
|
||||
});
|
||||
});
|
||||
|
||||
it('should query the old and new routes via :leave and :enter', fakeAsync(() => {
|
||||
@Component({
|
||||
animations: [
|
||||
trigger(
|
||||
'routerAnimations',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query(':leave', animateChild()),
|
||||
query(':enter', animateChild()),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
it('should query the old and new routes via :leave and :enter', fakeAsync(() => {
|
||||
@Component({
|
||||
animations: [
|
||||
trigger(
|
||||
'routerAnimations',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query(':leave', animateChild()),
|
||||
query(':enter', animateChild()),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
<div [@routerAnimations]="prepareRouteAnimation(r)">
|
||||
<router-outlet #r="outlet"></router-outlet>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class ContainerCmp {
|
||||
constructor(public router: Router) {}
|
||||
})
|
||||
class ContainerCmp {
|
||||
constructor(public router: Router) {}
|
||||
|
||||
prepareRouteAnimation(r: RouterOutlet) {
|
||||
const animation = r.activatedRouteData['animation'];
|
||||
const value = animation ? animation['value'] : null;
|
||||
return value;
|
||||
}
|
||||
prepareRouteAnimation(r: RouterOutlet) {
|
||||
const animation = r.activatedRouteData['animation'];
|
||||
const value = animation ? animation['value'] : null;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'page1',
|
||||
template: `page1`,
|
||||
animations: [
|
||||
trigger(
|
||||
'page1Animation',
|
||||
[
|
||||
transition(
|
||||
':leave',
|
||||
[
|
||||
style({width: '200px'}),
|
||||
animate(1000, style({width: '0px'})),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Page1Cmp {
|
||||
@HostBinding('@page1Animation') public doAnimate = true;
|
||||
}
|
||||
@Component({
|
||||
selector: 'page1',
|
||||
template: `page1`,
|
||||
animations: [
|
||||
trigger(
|
||||
'page1Animation',
|
||||
[
|
||||
transition(
|
||||
':leave',
|
||||
[
|
||||
style({width: '200px'}),
|
||||
animate(1000, style({width: '0px'})),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Page1Cmp {
|
||||
@HostBinding('@page1Animation') public doAnimate = true;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'page2',
|
||||
template: `page2`,
|
||||
animations: [
|
||||
trigger(
|
||||
'page2Animation',
|
||||
[
|
||||
transition(
|
||||
':enter',
|
||||
[
|
||||
style({opacity: 0}),
|
||||
animate(1000, style({opacity: 1})),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Page2Cmp {
|
||||
@HostBinding('@page2Animation') public doAnimate = true;
|
||||
}
|
||||
@Component({
|
||||
selector: 'page2',
|
||||
template: `page2`,
|
||||
animations: [
|
||||
trigger(
|
||||
'page2Animation',
|
||||
[
|
||||
transition(
|
||||
':enter',
|
||||
[
|
||||
style({opacity: 0}),
|
||||
animate(1000, style({opacity: 1})),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Page2Cmp {
|
||||
@HostBinding('@page2Animation') public doAnimate = true;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
const player = engine.players[0] !;
|
||||
const groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
const players = groupPlayer.players as MockAnimationPlayer[];
|
||||
const player = engine.players[0]!;
|
||||
const groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
const players = groupPlayer.players as MockAnimationPlayer[];
|
||||
|
||||
expect(players.length).toEqual(2);
|
||||
const [p1, p2] = players;
|
||||
expect(players.length).toEqual(2);
|
||||
const [p1, p2] = players;
|
||||
|
||||
expect(p1.duration).toEqual(1000);
|
||||
expect(p1.keyframes).toEqual([
|
||||
{offset: 0, width: '200px'},
|
||||
{offset: 1, width: '0px'},
|
||||
]);
|
||||
expect(p1.duration).toEqual(1000);
|
||||
expect(p1.keyframes).toEqual([
|
||||
{offset: 0, width: '200px'},
|
||||
{offset: 1, width: '0px'},
|
||||
]);
|
||||
|
||||
expect(p2.duration).toEqual(2000);
|
||||
expect(p2.keyframes).toEqual([
|
||||
{offset: 0, opacity: '0'},
|
||||
{offset: .5, opacity: '0'},
|
||||
{offset: 1, opacity: '1'},
|
||||
]);
|
||||
}));
|
||||
expect(p2.duration).toEqual(2000);
|
||||
expect(p2.keyframes).toEqual([
|
||||
{offset: 0, opacity: '0'},
|
||||
{offset: .5, opacity: '0'},
|
||||
{offset: 1, opacity: '1'},
|
||||
]);
|
||||
}));
|
||||
|
||||
it('should allow inner enter animations to be emulated within a routed item', fakeAsync(() => {
|
||||
@Component({
|
||||
animations: [
|
||||
trigger(
|
||||
'routerAnimations',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query(':enter', animateChild()),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
it('should allow inner enter animations to be emulated within a routed item', fakeAsync(() => {
|
||||
@Component({
|
||||
animations: [
|
||||
trigger(
|
||||
'routerAnimations',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query(':enter', animateChild()),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
<div [@routerAnimations]="prepareRouteAnimation(r)">
|
||||
<router-outlet #r="outlet"></router-outlet>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class ContainerCmp {
|
||||
constructor(public router: Router) {}
|
||||
})
|
||||
class ContainerCmp {
|
||||
constructor(public router: Router) {}
|
||||
|
||||
prepareRouteAnimation(r: RouterOutlet) {
|
||||
const animation = r.activatedRouteData['animation'];
|
||||
const value = animation ? animation['value'] : null;
|
||||
return value;
|
||||
}
|
||||
prepareRouteAnimation(r: RouterOutlet) {
|
||||
const animation = r.activatedRouteData['animation'];
|
||||
const value = animation ? animation['value'] : null;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'page1', template: `page1`, animations: []})
|
||||
class Page1Cmp {
|
||||
}
|
||||
@Component({selector: 'page1', template: `page1`, animations: []})
|
||||
class Page1Cmp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'page2',
|
||||
template: `
|
||||
@Component({
|
||||
selector: 'page2',
|
||||
template: `
|
||||
<h1>Page 2</h1>
|
||||
<div *ngIf="exp" class="if-one" @ifAnimation></div>
|
||||
<div *ngIf="exp" class="if-two" @ifAnimation></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'page2Animation',
|
||||
[
|
||||
transition(
|
||||
':enter',
|
||||
[query('.if-one', animateChild()), query('.if-two', animateChild())]),
|
||||
]),
|
||||
trigger(
|
||||
'ifAnimation',
|
||||
[transition(
|
||||
':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])
|
||||
]
|
||||
})
|
||||
class Page2Cmp {
|
||||
@HostBinding('@page2Animation') public doAnimate = true;
|
||||
animations: [
|
||||
trigger(
|
||||
'page2Animation',
|
||||
[
|
||||
transition(
|
||||
':enter',
|
||||
[query('.if-one', animateChild()), query('.if-two', animateChild())]),
|
||||
]),
|
||||
trigger(
|
||||
'ifAnimation',
|
||||
[transition(':enter', [style({opacity: 0}), animate(1000, style({opacity: 1}))])])
|
||||
]
|
||||
})
|
||||
class Page2Cmp {
|
||||
@HostBinding('@page2Animation') public doAnimate = true;
|
||||
|
||||
public exp = true;
|
||||
}
|
||||
public exp = true;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
const player = engine.players[0] !;
|
||||
const groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
const players = groupPlayer.players as MockAnimationPlayer[];
|
||||
const player = engine.players[0]!;
|
||||
const groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
const players = groupPlayer.players as MockAnimationPlayer[];
|
||||
|
||||
expect(players.length).toEqual(2);
|
||||
const [p1, p2] = players;
|
||||
expect(players.length).toEqual(2);
|
||||
const [p1, p2] = players;
|
||||
|
||||
expect(p1.keyframes).toEqual([
|
||||
{offset: 0, opacity: '0'},
|
||||
{offset: 1, opacity: '1'},
|
||||
]);
|
||||
expect(p1.keyframes).toEqual([
|
||||
{offset: 0, opacity: '0'},
|
||||
{offset: 1, opacity: '1'},
|
||||
]);
|
||||
|
||||
expect(p2.keyframes).toEqual([
|
||||
{offset: 0, opacity: '0'},
|
||||
{offset: .5, opacity: '0'},
|
||||
{offset: 1, opacity: '1'},
|
||||
]);
|
||||
}));
|
||||
expect(p2.keyframes).toEqual([
|
||||
{offset: 0, opacity: '0'},
|
||||
{offset: .5, opacity: '0'},
|
||||
{offset: 1, opacity: '1'},
|
||||
]);
|
||||
}));
|
||||
|
||||
it('should allow inner leave animations to be emulated within a routed item', fakeAsync(() => {
|
||||
@Component({
|
||||
animations: [
|
||||
trigger(
|
||||
'routerAnimations',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query(':leave', animateChild()),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
it('should allow inner leave animations to be emulated within a routed item', fakeAsync(() => {
|
||||
@Component({
|
||||
animations: [
|
||||
trigger(
|
||||
'routerAnimations',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query(':leave', animateChild()),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
<div [@routerAnimations]="prepareRouteAnimation(r)">
|
||||
<router-outlet #r="outlet"></router-outlet>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class ContainerCmp {
|
||||
constructor(public router: Router) {}
|
||||
})
|
||||
class ContainerCmp {
|
||||
constructor(public router: Router) {}
|
||||
|
||||
prepareRouteAnimation(r: RouterOutlet) {
|
||||
const animation = r.activatedRouteData['animation'];
|
||||
const value = animation ? animation['value'] : null;
|
||||
return value;
|
||||
}
|
||||
prepareRouteAnimation(r: RouterOutlet) {
|
||||
const animation = r.activatedRouteData['animation'];
|
||||
const value = animation ? animation['value'] : null;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'page1',
|
||||
template: `
|
||||
@Component({
|
||||
selector: 'page1',
|
||||
template: `
|
||||
<h1>Page 1</h1>
|
||||
<div *ngIf="exp" class="if-one" @ifAnimation></div>
|
||||
<div *ngIf="exp" class="if-two" @ifAnimation></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'page1Animation',
|
||||
[
|
||||
transition(
|
||||
':leave',
|
||||
[query('.if-one', animateChild()), query('.if-two', animateChild())]),
|
||||
]),
|
||||
trigger(
|
||||
'ifAnimation',
|
||||
[transition(':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])]),
|
||||
]
|
||||
})
|
||||
class Page1Cmp {
|
||||
@HostBinding('@page1Animation') public doAnimate = true;
|
||||
animations: [
|
||||
trigger(
|
||||
'page1Animation',
|
||||
[
|
||||
transition(
|
||||
':leave',
|
||||
[query('.if-one', animateChild()), query('.if-two', animateChild())]),
|
||||
]),
|
||||
trigger(
|
||||
'ifAnimation',
|
||||
[transition(':leave', [style({opacity: 1}), animate(1000, style({opacity: 0}))])]),
|
||||
]
|
||||
})
|
||||
class Page1Cmp {
|
||||
@HostBinding('@page1Animation') public doAnimate = true;
|
||||
|
||||
public exp = true;
|
||||
}
|
||||
public exp = true;
|
||||
}
|
||||
|
||||
@Component({selector: 'page2', template: `page2`, animations: []})
|
||||
class Page2Cmp {
|
||||
}
|
||||
@Component({selector: 'page2', template: `page2`, animations: []})
|
||||
class Page2Cmp {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
const player = engine.players[0] !;
|
||||
const groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
const players = groupPlayer.players as MockAnimationPlayer[];
|
||||
const player = engine.players[0]!;
|
||||
const groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
const players = groupPlayer.players as MockAnimationPlayer[];
|
||||
|
||||
expect(players.length).toEqual(2);
|
||||
const [p1, p2] = players;
|
||||
expect(players.length).toEqual(2);
|
||||
const [p1, p2] = players;
|
||||
|
||||
expect(p1.keyframes).toEqual([
|
||||
{offset: 0, opacity: '1'},
|
||||
{offset: 1, opacity: '0'},
|
||||
]);
|
||||
expect(p1.keyframes).toEqual([
|
||||
{offset: 0, opacity: '1'},
|
||||
{offset: 1, opacity: '0'},
|
||||
]);
|
||||
|
||||
expect(p2.keyframes).toEqual([
|
||||
{offset: 0, opacity: '1'},
|
||||
{offset: .5, opacity: '1'},
|
||||
{offset: 1, opacity: '0'},
|
||||
]);
|
||||
}));
|
||||
expect(p2.keyframes).toEqual([
|
||||
{offset: 0, opacity: '1'},
|
||||
{offset: .5, opacity: '1'},
|
||||
{offset: 1, opacity: '0'},
|
||||
]);
|
||||
}));
|
||||
|
||||
it('should properly collect :enter / :leave router nodes even when another non-router *template component is within the trigger boundaries',
|
||||
fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
animations: [
|
||||
trigger(
|
||||
'pageAnimation',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query('.router-container :leave', animate('1s', style({opacity: 0}))),
|
||||
query('.router-container :enter', animate('1s', style({opacity: 1}))),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
it('should properly collect :enter / :leave router nodes even when another non-router *template component is within the trigger boundaries',
|
||||
fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
animations: [
|
||||
trigger(
|
||||
'pageAnimation',
|
||||
[
|
||||
transition(
|
||||
'page1 => page2',
|
||||
[
|
||||
query('.router-container :leave', animate('1s', style({opacity: 0}))),
|
||||
query('.router-container :enter', animate('1s', style({opacity: 1}))),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
<div [@pageAnimation]="prepRoute(outlet)">
|
||||
<header>
|
||||
<div class="inner">
|
||||
@ -392,138 +393,144 @@ import {RouterTestingModule} from '@angular/router/testing';
|
||||
</section>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class ContainerCmp {
|
||||
loading = false;
|
||||
})
|
||||
class ContainerCmp {
|
||||
loading = false;
|
||||
|
||||
constructor(public router: Router) {}
|
||||
constructor(public router: Router) {}
|
||||
|
||||
prepRoute(outlet: any) { return outlet.activatedRouteData['animation']; }
|
||||
prepRoute(outlet: any) {
|
||||
return outlet.activatedRouteData['animation'];
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'page1', template: `page1`})
|
||||
class Page1Cmp {
|
||||
}
|
||||
|
||||
@Component({selector: 'page2', template: `page2`})
|
||||
class Page2Cmp {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
cmp.loading = true;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
cmp.loading = false;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
const players = engine.players;
|
||||
expect(players.length).toEqual(1);
|
||||
const [p1] = players;
|
||||
|
||||
const innerPlayers =
|
||||
((p1 as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players;
|
||||
expect(innerPlayers.length).toEqual(2);
|
||||
|
||||
const [ip1, ip2] = innerPlayers as any;
|
||||
expect(ip1.element.innerText).toEqual('page1');
|
||||
expect(ip2.element.innerText).toEqual('page2');
|
||||
}));
|
||||
|
||||
it('should allow a recursive set of :leave animations to occur for nested routes',
|
||||
fakeAsync(() => {
|
||||
@Component({selector: 'ani-cmp', template: '<router-outlet name="recur"></router-outlet>'})
|
||||
class ContainerCmp {
|
||||
constructor(private _router: Router) {}
|
||||
log: string[] = [];
|
||||
|
||||
enter() {
|
||||
this._router.navigateByUrl('/(recur:recur/nested)');
|
||||
}
|
||||
|
||||
@Component({selector: 'page1', template: `page1`})
|
||||
class Page1Cmp {
|
||||
leave() {
|
||||
this._router.navigateByUrl('/');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'page2', template: `page2`})
|
||||
class Page2Cmp {
|
||||
@Component({
|
||||
selector: 'recur-page',
|
||||
template: 'Depth: {{ depth }} \n <router-outlet></router-outlet>',
|
||||
animations: [
|
||||
trigger(
|
||||
'pageAnimations',
|
||||
[
|
||||
transition(':leave', [group([
|
||||
sequence([style({opacity: 1}), animate('1s', style({opacity: 0}))]),
|
||||
query('@*', animateChild(), {optional: true})
|
||||
])]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class RecurPageCmp {
|
||||
@HostBinding('@pageAnimations') public animatePage = true;
|
||||
|
||||
@HostBinding('attr.data-depth') public depth = 0;
|
||||
|
||||
constructor(private container: ContainerCmp, private route: ActivatedRoute) {
|
||||
this.route.data.subscribe(data => {
|
||||
this.container.log.push(`DEPTH ${data.depth}`);
|
||||
this.depth = data.depth;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [Page1Cmp, Page2Cmp, ContainerCmp],
|
||||
imports: [RouterTestingModule.withRoutes([
|
||||
{path: 'page1', component: Page1Cmp, data: makeAnimationData('page1')},
|
||||
{path: 'page2', component: Page2Cmp, data: makeAnimationData('page2')}
|
||||
])]
|
||||
});
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ContainerCmp, RecurPageCmp],
|
||||
imports: [RouterTestingModule.withRoutes([{
|
||||
path: 'recur',
|
||||
component: RecurPageCmp,
|
||||
outlet: 'recur',
|
||||
data: {depth: 0},
|
||||
children: [{path: 'nested', component: RecurPageCmp, data: {depth: 1}}]
|
||||
}])]
|
||||
});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.router.initialNavigation();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.enter();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
cmp.router.navigateByUrl('/page1');
|
||||
tick();
|
||||
cmp.loading = true;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
expect(cmp.log).toEqual([
|
||||
'DEPTH 0',
|
||||
'DEPTH 1',
|
||||
]);
|
||||
|
||||
cmp.router.navigateByUrl('/page2');
|
||||
tick();
|
||||
cmp.loading = false;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.leave();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const players = engine.players;
|
||||
expect(players.length).toEqual(1);
|
||||
const [p1] = players;
|
||||
const players = getLog();
|
||||
expect(players.length).toEqual(2);
|
||||
|
||||
const innerPlayers =
|
||||
((p1 as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players;
|
||||
expect(innerPlayers.length).toEqual(2);
|
||||
|
||||
const [ip1, ip2] = innerPlayers as any;
|
||||
expect(ip1.element.innerText).toEqual('page1');
|
||||
expect(ip2.element.innerText).toEqual('page2');
|
||||
}));
|
||||
|
||||
it('should allow a recursive set of :leave animations to occur for nested routes',
|
||||
fakeAsync(() => {
|
||||
@Component({selector: 'ani-cmp', template: '<router-outlet name="recur"></router-outlet>'})
|
||||
class ContainerCmp {
|
||||
constructor(private _router: Router) {}
|
||||
log: string[] = [];
|
||||
|
||||
enter() { this._router.navigateByUrl('/(recur:recur/nested)'); }
|
||||
|
||||
leave() { this._router.navigateByUrl('/'); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'recur-page',
|
||||
template: 'Depth: {{ depth }} \n <router-outlet></router-outlet>',
|
||||
animations: [
|
||||
trigger(
|
||||
'pageAnimations',
|
||||
[
|
||||
transition(':leave', [group([
|
||||
sequence([style({opacity: 1}), animate('1s', style({opacity: 0}))]),
|
||||
query('@*', animateChild(), {optional: true})
|
||||
])]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class RecurPageCmp {
|
||||
@HostBinding('@pageAnimations') public animatePage = true;
|
||||
|
||||
@HostBinding('attr.data-depth') public depth = 0;
|
||||
|
||||
constructor(private container: ContainerCmp, private route: ActivatedRoute) {
|
||||
this.route.data.subscribe(data => {
|
||||
this.container.log.push(`DEPTH ${data.depth}`);
|
||||
this.depth = data.depth;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ContainerCmp, RecurPageCmp],
|
||||
imports: [RouterTestingModule.withRoutes([{
|
||||
path: 'recur',
|
||||
component: RecurPageCmp,
|
||||
outlet: 'recur',
|
||||
data: {depth: 0},
|
||||
children: [{path: 'nested', component: RecurPageCmp, data: {depth: 1}}]
|
||||
}])]
|
||||
});
|
||||
|
||||
const fixture = TestBed.createComponent(ContainerCmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.enter();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
flushMicrotasks();
|
||||
|
||||
expect(cmp.log).toEqual([
|
||||
'DEPTH 0',
|
||||
'DEPTH 1',
|
||||
]);
|
||||
|
||||
cmp.leave();
|
||||
tick();
|
||||
fixture.detectChanges();
|
||||
|
||||
const players = getLog();
|
||||
expect(players.length).toEqual(2);
|
||||
|
||||
const [p1, p2] = players;
|
||||
expect(p1.element.getAttribute('data-depth')).toEqual('0');
|
||||
expect(p2.element.getAttribute('data-depth')).toEqual('1');
|
||||
}));
|
||||
});
|
||||
const [p1, p2] = players;
|
||||
expect(p1.element.getAttribute('data-depth')).toEqual('0');
|
||||
expect(p2.element.getAttribute('data-depth')).toEqual('1');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
function makeAnimationData(value: string, params: {[key: string]: any} = {}): {[key: string]: any} {
|
||||
|
@ -15,23 +15,22 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut
|
||||
import {TestBed} from '../../testing';
|
||||
|
||||
(function() {
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
// Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793
|
||||
if (isNode) return;
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
// Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793
|
||||
if (isNode) return;
|
||||
|
||||
describe('animation integration tests using css keyframe animations', function() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [{provide: AnimationDriver, useClass: CssKeyframesDriver}],
|
||||
imports: [BrowserAnimationsModule]
|
||||
});
|
||||
describe('animation integration tests using css keyframe animations', function() {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [{provide: AnimationDriver, useClass: CssKeyframesDriver}],
|
||||
imports: [BrowserAnimationsModule]
|
||||
});
|
||||
});
|
||||
|
||||
it('should compute (*) animation styles for a container that is being removed', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should compute (*) animation styles for a container that is being removed', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div @auto *ngIf="exp">
|
||||
<div style="line-height:20px;">1</div>
|
||||
<div style="line-height:20px;">2</div>
|
||||
@ -40,46 +39,46 @@ import {TestBed} from '../../testing';
|
||||
<div style="line-height:20px;">5</div>
|
||||
</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'auto',
|
||||
[
|
||||
state('void', style({height: '0px'})),
|
||||
state('*', style({height: '*'})),
|
||||
transition('* => *', animate(1000)),
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
public exp: boolean = false;
|
||||
}
|
||||
animations: [trigger(
|
||||
'auto',
|
||||
[
|
||||
state('void', style({height: '0px'})),
|
||||
state('*', style({height: '*'})),
|
||||
transition('* => *', animate(1000)),
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
public exp: boolean = false;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let player = getPlayer(engine) as CssKeyframesPlayer;
|
||||
expect(player.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]);
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let player = getPlayer(engine) as CssKeyframesPlayer;
|
||||
expect(player.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]);
|
||||
|
||||
player.finish();
|
||||
if (browserDetection.isOldChrome) return;
|
||||
player.finish();
|
||||
if (browserDetection.isOldChrome) return;
|
||||
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
player = getPlayer(engine) as CssKeyframesPlayer;
|
||||
expect(player.keyframes).toEqual([{height: '100px', offset: 0}, {height: '0px', offset: 1}]);
|
||||
});
|
||||
player = getPlayer(engine) as CssKeyframesPlayer;
|
||||
expect(player.keyframes).toEqual([{height: '100px', offset: 0}, {height: '0px', offset: 1}]);
|
||||
});
|
||||
|
||||
it('should cleanup all existing @keyframe <style> objects after the animation has finished',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should cleanup all existing @keyframe <style> objects after the animation has finished',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div [@myAnimation]="myAnimationExp">
|
||||
<div>1</div>
|
||||
<div>2</div>
|
||||
@ -88,278 +87,278 @@ import {TestBed} from '../../testing';
|
||||
<div>5</div>
|
||||
</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
query(
|
||||
'div',
|
||||
[
|
||||
style({opacity: 0}),
|
||||
animate('1s', style({opacity: 0})),
|
||||
]),
|
||||
]),
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
query(
|
||||
'div',
|
||||
[
|
||||
style({opacity: 0}),
|
||||
animate('1s', style({opacity: 0})),
|
||||
]),
|
||||
]),
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
|
||||
const webPlayer = <AnimationGroupPlayer>getPlayer(engine);
|
||||
const players = webPlayer.players as CssKeyframesPlayer[];
|
||||
expect(players.length).toEqual(5);
|
||||
const webPlayer = <AnimationGroupPlayer>getPlayer(engine);
|
||||
const players = webPlayer.players as CssKeyframesPlayer[];
|
||||
expect(players.length).toEqual(5);
|
||||
|
||||
const head = document.querySelector('head') !;
|
||||
const sheets: any[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const sheet = findStyleObjectWithKeyframes(i);
|
||||
expect(head.contains(sheet)).toBeTruthy();
|
||||
sheets.push(sheet);
|
||||
}
|
||||
const head = document.querySelector('head')!;
|
||||
const sheets: any[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const sheet = findStyleObjectWithKeyframes(i);
|
||||
expect(head.contains(sheet)).toBeTruthy();
|
||||
sheets.push(sheet);
|
||||
}
|
||||
|
||||
cmp.myAnimationExp = 'go-back';
|
||||
fixture.detectChanges();
|
||||
cmp.myAnimationExp = 'go-back';
|
||||
fixture.detectChanges();
|
||||
|
||||
for (let i = 0; i < 5; i++) {
|
||||
expect(head.contains(sheets[i])).toBeFalsy();
|
||||
}
|
||||
});
|
||||
for (let i = 0; i < 5; i++) {
|
||||
expect(head.contains(sheets[i])).toBeFalsy();
|
||||
}
|
||||
});
|
||||
|
||||
it('should properly handle easing values that are apart of the sequence', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should properly handle easing values that are apart of the sequence', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div #elm [@myAnimation]="myAnimationExp"></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => goSteps',
|
||||
[
|
||||
style({opacity: 0}),
|
||||
animate('1s ease-out', style({opacity: 1})),
|
||||
]),
|
||||
transition(
|
||||
'* => goKeyframes',
|
||||
[
|
||||
animate('1s cubic-bezier(0.5, 1, 0.5, 1)', keyframes([
|
||||
style({opacity: 0}),
|
||||
style({opacity: 0.5}),
|
||||
style({opacity: 1}),
|
||||
])),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm') public element: any;
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => goSteps',
|
||||
[
|
||||
style({opacity: 0}),
|
||||
animate('1s ease-out', style({opacity: 1})),
|
||||
]),
|
||||
transition(
|
||||
'* => goKeyframes',
|
||||
[
|
||||
animate('1s cubic-bezier(0.5, 1, 0.5, 1)', keyframes([
|
||||
style({opacity: 0}),
|
||||
style({opacity: 0.5}),
|
||||
style({opacity: 1}),
|
||||
])),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm') public element: any;
|
||||
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.myAnimationExp = 'goSteps';
|
||||
fixture.detectChanges();
|
||||
cmp.myAnimationExp = 'goSteps';
|
||||
fixture.detectChanges();
|
||||
|
||||
let kfElm = findStyleObjectWithKeyframes();
|
||||
const [r1, r2] = kfElm.sheet.cssRules[0].cssRules;
|
||||
assertEasing(r1, 'ease-out');
|
||||
assertEasing(r2, '');
|
||||
let kfElm = findStyleObjectWithKeyframes();
|
||||
const [r1, r2] = kfElm.sheet.cssRules[0].cssRules;
|
||||
assertEasing(r1, 'ease-out');
|
||||
assertEasing(r2, '');
|
||||
|
||||
const element = cmp.element.nativeElement;
|
||||
const element = cmp.element.nativeElement;
|
||||
|
||||
const webPlayer = getPlayer(engine);
|
||||
cmp.myAnimationExp = 'goKeyframes';
|
||||
fixture.detectChanges();
|
||||
const webPlayer = getPlayer(engine);
|
||||
cmp.myAnimationExp = 'goKeyframes';
|
||||
fixture.detectChanges();
|
||||
|
||||
assertEasing(element, 'cubic-bezier(0.5,1,0.5,1)');
|
||||
});
|
||||
assertEasing(element, 'cubic-bezier(0.5,1,0.5,1)');
|
||||
});
|
||||
|
||||
it('should restore existing style values once the animation completes', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should restore existing style values once the animation completes', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div #elm [@myAnimation]="myAnimationExp"></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({width: '200px'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
style({height: '100px', width: '100px'}), group([
|
||||
animate('1s', style({height: '200px'})),
|
||||
animate('1s', style({width: '200px'}))
|
||||
])
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm') public element: any;
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({width: '200px'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
style({height: '100px', width: '100px'}), group([
|
||||
animate('1s', style({height: '200px'})),
|
||||
animate('1s', style({width: '200px'}))
|
||||
])
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm') public element: any;
|
||||
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
fixture.detectChanges();
|
||||
const element = cmp.element.nativeElement;
|
||||
element.style['width'] = '50px';
|
||||
element.style['height'] = '50px';
|
||||
fixture.detectChanges();
|
||||
const element = cmp.element.nativeElement;
|
||||
element.style['width'] = '50px';
|
||||
element.style['height'] = '50px';
|
||||
|
||||
assertStyle(element, 'width', '50px');
|
||||
assertStyle(element, 'height', '50px');
|
||||
assertStyle(element, 'width', '50px');
|
||||
assertStyle(element, 'height', '50px');
|
||||
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
|
||||
const player = getPlayer(engine);
|
||||
const player = getPlayer(engine);
|
||||
|
||||
assertStyle(element, 'width', '100px');
|
||||
assertStyle(element, 'height', '100px');
|
||||
assertStyle(element, 'width', '100px');
|
||||
assertStyle(element, 'height', '100px');
|
||||
|
||||
player.finish();
|
||||
player.finish();
|
||||
|
||||
assertStyle(element, 'width', '200px');
|
||||
assertStyle(element, 'height', '50px');
|
||||
});
|
||||
assertStyle(element, 'width', '200px');
|
||||
assertStyle(element, 'height', '50px');
|
||||
});
|
||||
|
||||
it('should clean up 0 second animation styles (queried styles) that contain camel casing when complete',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should clean up 0 second animation styles (queried styles) that contain camel casing when complete',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div #elm [@myAnimation]="myAnimationExp">
|
||||
<div class="foo"></div>
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({width: '200px'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
query('.foo', [style({maxHeight: '0px'})]),
|
||||
query(
|
||||
'.bar',
|
||||
[
|
||||
style({width: '0px'}),
|
||||
animate('1s', style({width: '100px'})),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm', {static: true}) public element: any;
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({width: '200px'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
query('.foo', [style({maxHeight: '0px'})]),
|
||||
query(
|
||||
'.bar',
|
||||
[
|
||||
style({width: '0px'}),
|
||||
animate('1s', style({width: '100px'})),
|
||||
]),
|
||||
]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm', {static: true}) public element: any;
|
||||
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
const elm = cmp.element.nativeElement;
|
||||
const foo = elm.querySelector('.foo') as HTMLElement;
|
||||
const elm = cmp.element.nativeElement;
|
||||
const foo = elm.querySelector('.foo') as HTMLElement;
|
||||
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(foo.style.getPropertyValue('max-height')).toEqual('0px');
|
||||
expect(foo.style.getPropertyValue('max-height')).toEqual('0px');
|
||||
|
||||
const player = engine.players.pop() !;
|
||||
player.finish();
|
||||
const player = engine.players.pop()!;
|
||||
player.finish();
|
||||
|
||||
expect(foo.style.getPropertyValue('max-height')).toBeFalsy();
|
||||
});
|
||||
expect(foo.style.getPropertyValue('max-height')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div #elm [@myAnimation]="myAnimationExp" style="display:table; position:fixed"></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({display: 'inline-block'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
style({display: 'inline', position: 'absolute', opacity: 0}),
|
||||
animate('1s', style({display: 'inline', opacity: 1, position: 'static'})),
|
||||
animate('1s', style({display: 'flexbox', opacity: 0})),
|
||||
])
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm', {static: true}) public element: any;
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({display: 'inline-block'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
style({display: 'inline', position: 'absolute', opacity: 0}),
|
||||
animate('1s', style({display: 'inline', opacity: 1, position: 'static'})),
|
||||
animate('1s', style({display: 'flexbox', opacity: 0})),
|
||||
])
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm', {static: true}) public element: any;
|
||||
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(AnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
const elm = cmp.element.nativeElement;
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('table');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
const elm = cmp.element.nativeElement;
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('table');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('absolute');
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('absolute');
|
||||
|
||||
const player = engine.players.pop() !;
|
||||
player.finish();
|
||||
player.destroy();
|
||||
const player = engine.players.pop()!;
|
||||
player.finish();
|
||||
player.destroy();
|
||||
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline-block');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
});
|
||||
});
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline-block');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
function getPlayer(engine: AnimationEngine, index = 0) {
|
||||
return (engine.players[index] as any) !.getRealPlayer();
|
||||
return (engine.players[index] as any)!.getRealPlayer();
|
||||
}
|
||||
|
||||
function findStyleObjectWithKeyframes(index?: number): any|null {
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {animate, query, state, style, transition, trigger} from '@angular/animations';
|
||||
import {AnimationDriver, ɵAnimationEngine, ɵWebAnimationsDriver, ɵWebAnimationsPlayer, ɵsupportsWebAnimations} from '@angular/animations/browser';
|
||||
import {AnimationDriver, ɵAnimationEngine, ɵsupportsWebAnimations, ɵWebAnimationsDriver, ɵWebAnimationsPlayer} from '@angular/animations/browser';
|
||||
import {TransitionAnimationPlayer} from '@angular/animations/browser/src/render/transition_animation_engine';
|
||||
import {AnimationGroupPlayer} from '@angular/animations/src/players/animation_group_player';
|
||||
import {Component, ViewChild} from '@angular/core';
|
||||
@ -15,23 +15,22 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {browserDetection} from '@angular/platform-browser/testing/src/browser_util';
|
||||
|
||||
(function() {
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
// Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793
|
||||
if (isNode || !ɵsupportsWebAnimations()) return;
|
||||
// these tests are only mean't to be run within the DOM (for now)
|
||||
// Buggy in Chromium 39, see https://github.com/angular/angular/issues/15793
|
||||
if (isNode || !ɵsupportsWebAnimations()) return;
|
||||
|
||||
describe('animation integration tests using web animations', function() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [{provide: AnimationDriver, useClass: ɵWebAnimationsDriver}],
|
||||
imports: [BrowserAnimationsModule]
|
||||
});
|
||||
describe('animation integration tests using web animations', function() {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [{provide: AnimationDriver, useClass: ɵWebAnimationsDriver}],
|
||||
imports: [BrowserAnimationsModule]
|
||||
});
|
||||
});
|
||||
|
||||
it('should compute (*) animation styles for a container that is being removed', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should compute (*) animation styles for a container that is being removed', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div @auto *ngIf="exp">
|
||||
<div style="line-height:20px;">1</div>
|
||||
<div style="line-height:20px;">2</div>
|
||||
@ -40,152 +39,147 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut
|
||||
<div style="line-height:20px;">5</div>
|
||||
</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'auto',
|
||||
[
|
||||
state('void', style({height: '0px'})), state('*', style({height: '*'})),
|
||||
transition('* => *', animate(1000))
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
public exp: boolean = false;
|
||||
}
|
||||
animations: [trigger(
|
||||
'auto',
|
||||
[
|
||||
state('void', style({height: '0px'})), state('*', style({height: '*'})),
|
||||
transition('* => *', animate(1000))
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
public exp: boolean = false;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let webPlayer =
|
||||
(engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let webPlayer =
|
||||
(engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '0px', offset: 0}, {height: '100px', offset: 1}
|
||||
]);
|
||||
expect(webPlayer.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]);
|
||||
|
||||
webPlayer.finish();
|
||||
webPlayer.finish();
|
||||
|
||||
if (!browserDetection.isOldChrome) {
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
expect(engine.players.length).toEqual(1);
|
||||
webPlayer = (engine.players[0] as TransitionAnimationPlayer)
|
||||
.getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '100px', offset: 0}, {height: '0px', offset: 1}
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('should compute (!) animation styles for a container that is being inserted', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div @auto *ngIf="exp">
|
||||
<div style="line-height:20px;">1</div>
|
||||
<div style="line-height:20px;">2</div>
|
||||
<div style="line-height:20px;">3</div>
|
||||
<div style="line-height:20px;">4</div>
|
||||
<div style="line-height:20px;">5</div>
|
||||
</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'auto',
|
||||
[transition(
|
||||
':enter', [style({height: '!'}), animate(1000, style({height: '120px'}))])])]
|
||||
})
|
||||
class Cmp {
|
||||
public exp: boolean = false;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = true;
|
||||
if (!browserDetection.isOldChrome) {
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let webPlayer =
|
||||
webPlayer =
|
||||
(engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '100px', offset: 0}, {height: '120px', offset: 1}
|
||||
{height: '100px', offset: 0}, {height: '0px', offset: 1}
|
||||
]);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it('should compute pre (!) and post (*) animation styles with different dom states', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should compute (!) animation styles for a container that is being inserted', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div @auto *ngIf="exp">
|
||||
<div style="line-height:20px;">1</div>
|
||||
<div style="line-height:20px;">2</div>
|
||||
<div style="line-height:20px;">3</div>
|
||||
<div style="line-height:20px;">4</div>
|
||||
<div style="line-height:20px;">5</div>
|
||||
</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'auto',
|
||||
[transition(':enter', [style({height: '!'}), animate(1000, style({height: '120px'}))])])]
|
||||
})
|
||||
class Cmp {
|
||||
public exp: boolean = false;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let webPlayer =
|
||||
(engine.players[0] as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '100px', offset: 0}, {height: '120px', offset: 1}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should compute pre (!) and post (*) animation styles with different dom states', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div [@myAnimation]="exp" #parent>
|
||||
<div *ngFor="let item of items" class="child" style="line-height:20px">
|
||||
- {{ item }}
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[transition('* => *', [style({height: '!'}), animate(1000, style({height: '*'}))])])]
|
||||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp !: number;
|
||||
public items = [0, 1, 2, 3, 4];
|
||||
}
|
||||
animations: [trigger(
|
||||
'myAnimation',
|
||||
[transition('* => *', [style({height: '!'}), animate(1000, style({height: '*'}))])])]
|
||||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp!: number;
|
||||
public items = [0, 1, 2, 3, 4];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = 1;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.exp = 1;
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let player = engine.players[0];
|
||||
let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(engine.players.length).toEqual(1);
|
||||
let player = engine.players[0];
|
||||
let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '0px', offset: 0}, {height: '100px', offset: 1}
|
||||
]);
|
||||
expect(webPlayer.keyframes).toEqual([{height: '0px', offset: 0}, {height: '100px', offset: 1}]);
|
||||
|
||||
// we destroy the player because since it has started and is
|
||||
// at 0ms duration a height value of `0px` will be extracted
|
||||
// from the element and passed into the follow-up animation.
|
||||
player.destroy();
|
||||
// we destroy the player because since it has started and is
|
||||
// at 0ms duration a height value of `0px` will be extracted
|
||||
// from the element and passed into the follow-up animation.
|
||||
player.destroy();
|
||||
|
||||
cmp.exp = 2;
|
||||
cmp.items = [0, 1, 2, 6];
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
cmp.exp = 2;
|
||||
cmp.items = [0, 1, 2, 6];
|
||||
fixture.detectChanges();
|
||||
engine.flush();
|
||||
|
||||
expect(engine.players.length).toEqual(1);
|
||||
player = engine.players[0];
|
||||
webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(engine.players.length).toEqual(1);
|
||||
player = engine.players[0];
|
||||
webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '100px', offset: 0}, {height: '80px', offset: 1}
|
||||
]);
|
||||
});
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '100px', offset: 0}, {height: '80px', offset: 1}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should treat * styles as ! when a removal animation is being rendered', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
styles: [`
|
||||
it('should treat * styles as ! when a removal animation is being rendered', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
styles: [`
|
||||
.box {
|
||||
width: 500px;
|
||||
overflow:hidden;
|
||||
@ -195,58 +189,60 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut
|
||||
text-align:center;
|
||||
}
|
||||
`],
|
||||
template: `
|
||||
template: `
|
||||
<button (click)="toggle()">Open / Close</button>
|
||||
<hr />
|
||||
<div *ngIf="exp" @slide class="box">
|
||||
...
|
||||
</div>
|
||||
`,
|
||||
animations: [trigger(
|
||||
'slide',
|
||||
[
|
||||
state('void', style({height: '0px'})),
|
||||
state('*', style({height: '*'})),
|
||||
transition('* => *', animate('500ms')),
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
exp = false;
|
||||
animations: [trigger(
|
||||
'slide',
|
||||
[
|
||||
state('void', style({height: '0px'})),
|
||||
state('*', style({height: '*'})),
|
||||
transition('* => *', animate('500ms')),
|
||||
])]
|
||||
})
|
||||
class Cmp {
|
||||
exp = false;
|
||||
|
||||
toggle() { this.exp = !this.exp; }
|
||||
toggle() {
|
||||
this.exp = !this.exp;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
cmp.exp = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let player = engine.players[0] !;
|
||||
let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '0px', offset: 0},
|
||||
{height: '300px', offset: 1},
|
||||
]);
|
||||
player.finish();
|
||||
let player = engine.players[0]!;
|
||||
let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '0px', offset: 0},
|
||||
{height: '300px', offset: 1},
|
||||
]);
|
||||
player.finish();
|
||||
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
cmp.exp = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
player = engine.players[0] !;
|
||||
webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '300px', offset: 0},
|
||||
{height: '0px', offset: 1},
|
||||
]);
|
||||
});
|
||||
player = engine.players[0]!;
|
||||
webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(webPlayer.keyframes).toEqual([
|
||||
{height: '300px', offset: 0},
|
||||
{height: '0px', offset: 1},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should treat * styles as ! for queried items that are collected in a container that is being removed',
|
||||
() => {
|
||||
@Component({
|
||||
it('should treat * styles as ! for queried items that are collected in a container that is being removed',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
styles: [`
|
||||
.list .outer {
|
||||
@ -287,235 +283,236 @@ import {browserDetection} from '@angular/platform-browser/testing/src/browser_ut
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
items: any[] = [];
|
||||
items: any[] = [];
|
||||
|
||||
get exp() { return this.items.length ? 'full' : 'empty'; }
|
||||
|
||||
empty() { this.items = []; }
|
||||
|
||||
full() { this.items = [0, 1, 2, 3, 4]; }
|
||||
get exp() {
|
||||
return this.items.length ? 'full' : 'empty';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
empty() {
|
||||
this.items = [];
|
||||
}
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
full() {
|
||||
this.items = [0, 1, 2, 3, 4];
|
||||
}
|
||||
}
|
||||
|
||||
cmp.empty();
|
||||
fixture.detectChanges();
|
||||
let player = engine.players[0] !as TransitionAnimationPlayer;
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.empty();
|
||||
fixture.detectChanges();
|
||||
let player = engine.players[0]! as TransitionAnimationPlayer;
|
||||
player.finish();
|
||||
|
||||
cmp.full();
|
||||
fixture.detectChanges();
|
||||
|
||||
player = engine.players[0]! as TransitionAnimationPlayer;
|
||||
let queriedPlayers =
|
||||
((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players;
|
||||
expect(queriedPlayers.length).toEqual(5);
|
||||
|
||||
let i = 0;
|
||||
for (i = 0; i < queriedPlayers.length; i++) {
|
||||
let player = queriedPlayers[i] as ɵWebAnimationsPlayer;
|
||||
expect(player.keyframes).toEqual([
|
||||
{height: '0px', offset: 0},
|
||||
{height: '50px', offset: 1},
|
||||
]);
|
||||
player.finish();
|
||||
}
|
||||
|
||||
cmp.full();
|
||||
fixture.detectChanges();
|
||||
cmp.empty();
|
||||
fixture.detectChanges();
|
||||
|
||||
player = engine.players[0] !as TransitionAnimationPlayer;
|
||||
let queriedPlayers =
|
||||
((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer)
|
||||
.players;
|
||||
expect(queriedPlayers.length).toEqual(5);
|
||||
player = engine.players[0]! as TransitionAnimationPlayer;
|
||||
queriedPlayers =
|
||||
((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer).players;
|
||||
expect(queriedPlayers.length).toEqual(5);
|
||||
|
||||
let i = 0;
|
||||
for (i = 0; i < queriedPlayers.length; i++) {
|
||||
let player = queriedPlayers[i] as ɵWebAnimationsPlayer;
|
||||
expect(player.keyframes).toEqual([
|
||||
{height: '0px', offset: 0},
|
||||
{height: '50px', offset: 1},
|
||||
]);
|
||||
player.finish();
|
||||
}
|
||||
for (i = 0; i < queriedPlayers.length; i++) {
|
||||
let player = queriedPlayers[i] as ɵWebAnimationsPlayer;
|
||||
expect(player.keyframes).toEqual([
|
||||
{height: '50px', offset: 0},
|
||||
{height: '0px', offset: 1},
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
cmp.empty();
|
||||
fixture.detectChanges();
|
||||
|
||||
player = engine.players[0] !as TransitionAnimationPlayer;
|
||||
queriedPlayers =
|
||||
((player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer)
|
||||
.players;
|
||||
expect(queriedPlayers.length).toEqual(5);
|
||||
|
||||
for (i = 0; i < queriedPlayers.length; i++) {
|
||||
let player = queriedPlayers[i] as ɵWebAnimationsPlayer;
|
||||
expect(player.keyframes).toEqual([
|
||||
{height: '50px', offset: 0},
|
||||
{height: '0px', offset: 1},
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('should compute intermediate styles properly when an animation is cancelled', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should compute intermediate styles properly when an animation is cancelled', () => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div [@myAnimation]="exp">...</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => a',
|
||||
[
|
||||
style({width: 0, height: 0}),
|
||||
animate('1s', style({width: '300px', height: '600px'})),
|
||||
]),
|
||||
transition('* => b', [animate('1s', style({opacity: 0}))]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp !: string;
|
||||
}
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => a',
|
||||
[
|
||||
style({width: 0, height: 0}),
|
||||
animate('1s', style({width: '300px', height: '600px'})),
|
||||
]),
|
||||
transition('* => b', [animate('1s', style({opacity: 0}))]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp!: string;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = 'a';
|
||||
fixture.detectChanges();
|
||||
cmp.exp = 'a';
|
||||
fixture.detectChanges();
|
||||
|
||||
let player = engine.players[0] !;
|
||||
let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
webPlayer.setPosition(0.5);
|
||||
let player = engine.players[0]!;
|
||||
let webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
webPlayer.setPosition(0.5);
|
||||
|
||||
cmp.exp = 'b';
|
||||
fixture.detectChanges();
|
||||
cmp.exp = 'b';
|
||||
fixture.detectChanges();
|
||||
|
||||
player = engine.players[0] !;
|
||||
webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(approximate(parseFloat(webPlayer.keyframes[0]['width'] as string), 150))
|
||||
.toBeLessThan(0.05);
|
||||
expect(approximate(parseFloat(webPlayer.keyframes[0]['height'] as string), 300))
|
||||
.toBeLessThan(0.05);
|
||||
});
|
||||
player = engine.players[0]!;
|
||||
webPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as ɵWebAnimationsPlayer;
|
||||
expect(approximate(parseFloat(webPlayer.keyframes[0]['width'] as string), 150))
|
||||
.toBeLessThan(0.05);
|
||||
expect(approximate(parseFloat(webPlayer.keyframes[0]['height'] as string), 300))
|
||||
.toBeLessThan(0.05);
|
||||
});
|
||||
|
||||
it('should compute intermediate styles properly for multiple queried elements when an animation is cancelled',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should compute intermediate styles properly for multiple queried elements when an animation is cancelled',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div [@myAnimation]="exp">
|
||||
<div *ngFor="let item of items" class="target"></div>
|
||||
</div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => full', [query(
|
||||
'.target',
|
||||
[
|
||||
style({width: 0, height: 0}),
|
||||
animate('1s', style({width: '500px', height: '1000px'})),
|
||||
])]),
|
||||
transition(
|
||||
'* => empty', [query('.target', [animate('1s', style({opacity: 0}))])]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp !: string;
|
||||
public items: any[] = [];
|
||||
}
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition(
|
||||
'* => full', [query(
|
||||
'.target',
|
||||
[
|
||||
style({width: 0, height: 0}),
|
||||
animate('1s', style({width: '500px', height: '1000px'})),
|
||||
])]),
|
||||
transition('* => empty', [query('.target', [animate('1s', style({opacity: 0}))])]),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
public exp!: string;
|
||||
public items: any[] = [];
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
cmp.exp = 'full';
|
||||
cmp.items = [0, 1, 2, 3, 4];
|
||||
fixture.detectChanges();
|
||||
cmp.exp = 'full';
|
||||
cmp.items = [0, 1, 2, 3, 4];
|
||||
fixture.detectChanges();
|
||||
|
||||
let player = engine.players[0] !;
|
||||
let groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
let players = groupPlayer.players;
|
||||
expect(players.length).toEqual(5);
|
||||
let player = engine.players[0]!;
|
||||
let groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
let players = groupPlayer.players;
|
||||
expect(players.length).toEqual(5);
|
||||
|
||||
for (let i = 0; i < players.length; i++) {
|
||||
const p = players[i] as ɵWebAnimationsPlayer;
|
||||
p.setPosition(0.5);
|
||||
}
|
||||
for (let i = 0; i < players.length; i++) {
|
||||
const p = players[i] as ɵWebAnimationsPlayer;
|
||||
p.setPosition(0.5);
|
||||
}
|
||||
|
||||
cmp.exp = 'empty';
|
||||
cmp.items = [];
|
||||
fixture.detectChanges();
|
||||
cmp.exp = 'empty';
|
||||
cmp.items = [];
|
||||
fixture.detectChanges();
|
||||
|
||||
player = engine.players[0];
|
||||
groupPlayer =
|
||||
(player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
players = groupPlayer.players;
|
||||
player = engine.players[0];
|
||||
groupPlayer = (player as TransitionAnimationPlayer).getRealPlayer() as AnimationGroupPlayer;
|
||||
players = groupPlayer.players;
|
||||
|
||||
expect(players.length).toEqual(5);
|
||||
for (let i = 0; i < players.length; i++) {
|
||||
const p = players[i] as ɵWebAnimationsPlayer;
|
||||
expect(approximate(parseFloat(p.keyframes[0]['width'] as string), 250))
|
||||
.toBeLessThan(0.05);
|
||||
expect(approximate(parseFloat(p.keyframes[0]['height'] as string), 500))
|
||||
.toBeLessThan(0.05);
|
||||
}
|
||||
});
|
||||
expect(players.length).toEqual(5);
|
||||
for (let i = 0; i < players.length; i++) {
|
||||
const p = players[i] as ɵWebAnimationsPlayer;
|
||||
expect(approximate(parseFloat(p.keyframes[0]['width'] as string), 250)).toBeLessThan(0.05);
|
||||
expect(approximate(parseFloat(p.keyframes[0]['height'] as string), 500))
|
||||
.toBeLessThan(0.05);
|
||||
}
|
||||
});
|
||||
|
||||
it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
it('should apply the `display` and `position` styles as regular inline styles for the duration of the animation',
|
||||
() => {
|
||||
@Component({
|
||||
selector: 'ani-cmp',
|
||||
template: `
|
||||
<div #elm [@myAnimation]="myAnimationExp" style="display:table; position:fixed"></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({display: 'inline-block'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
style({display: 'inline', position: 'absolute', opacity: 0}),
|
||||
animate('1s', style({display: 'inline', opacity: 1, position: 'static'})),
|
||||
animate('1s', style({display: 'flexbox', opacity: 0})),
|
||||
])
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm', {static: true}) public element: any;
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
state('go', style({display: 'inline-block'})),
|
||||
transition(
|
||||
'* => go',
|
||||
[
|
||||
style({display: 'inline', position: 'absolute', opacity: 0}),
|
||||
animate('1s', style({display: 'inline', opacity: 1, position: 'static'})),
|
||||
animate('1s', style({display: 'flexbox', opacity: 0})),
|
||||
])
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('elm', {static: true}) public element: any;
|
||||
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
public myAnimationExp = '';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
const engine = TestBed.inject(ɵAnimationEngine);
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
|
||||
const elm = cmp.element.nativeElement;
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('table');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
const elm = cmp.element.nativeElement;
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('table');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
cmp.myAnimationExp = 'go';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('absolute');
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('absolute');
|
||||
|
||||
const player = engine.players.pop() !;
|
||||
player.finish();
|
||||
player.destroy();
|
||||
const player = engine.players.pop()!;
|
||||
player.finish();
|
||||
player.destroy();
|
||||
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline-block');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
});
|
||||
});
|
||||
expect(elm.style.getPropertyValue('display')).toEqual('inline-block');
|
||||
expect(elm.style.getPropertyValue('position')).toEqual('fixed');
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
function approximate(value: number, target: number) {
|
||||
|
Reference in New Issue
Block a user