diff --git a/packages/core/test/animation/animation_query_integration_spec.ts b/packages/core/test/animation/animation_query_integration_spec.ts
index 4044f868a6..981e3b0bee 100644
--- a/packages/core/test/animation/animation_query_integration_spec.ts
+++ b/packages/core/test/animation/animation_query_integration_spec.ts
@@ -2113,6 +2113,125 @@ export function main() {
expect(p4.element.classList.contains('d'));
});
+ it('should collect multiple root levels of :enter and :leave nodes', () => {
+ @Component({
+ selector: 'ani-cmp',
+ animations: [
+ trigger('pageAnimation', [
+ transition(':enter', []),
+ transition('* => *', [
+ query(':leave', [
+ animate('1s', style({ opacity: 0 }))
+ ], { optional: true }),
+ query(':enter', [
+ animate('1s', style({ opacity: 1 }))
+ ], { optional: true })
+ ])
+ ])
+ ],
+ template: `
+
+
+ {{ title }}
+ loading...
+
+
+
+ `
+ })
+ class Cmp {
+ get title() {
+ if (this.page1) {
+ return 'hello from page1';
+ }
+ return 'greetings from page2';
+ }
+
+ page1 = false;
+ page2 = false;
+ loading = false;
+
+ get status() {
+ if (this.loading) return 'loading';
+ if (this.page1) return 'page1';
+ if (this.page2) return 'page2';
+ return '';
+ }
+ }
+
+ TestBed.configureTestingModule({declarations: [Cmp]});
+
+ const engine = TestBed.get(ɵAnimationEngine);
+ const fixture = TestBed.createComponent(Cmp);
+ const cmp = fixture.componentInstance;
+ cmp.loading = true;
+ fixture.detectChanges();
+ engine.flush();
+
+ let players = getLog();
+ resetLog();
+ cancelAllPlayers(players);
+
+ cmp.page1 = true;
+ cmp.loading = false;
+ fixture.detectChanges();
+ engine.flush();
+
+ let p1: MockAnimationPlayer;
+ let p2: MockAnimationPlayer;
+ let p3: MockAnimationPlayer;
+
+ players = getLog();
+ expect(players.length).toEqual(3);
+ [p1, p2, p3] = players;
+
+ expect(p1.element.classList.contains('loading')).toBe(true);
+ expect(p2.element.classList.contains('title')).toBe(true);
+ expect(p3.element.classList.contains('page1')).toBe(true);
+
+ resetLog();
+ cancelAllPlayers(players);
+
+ cmp.page1 = false;
+ cmp.loading = true;
+ fixture.detectChanges();
+
+ players = getLog();
+ cancelAllPlayers(players);
+
+ expect(players.length).toEqual(3);
+ [p1, p2, p3] = players;
+
+ expect(p1.element.classList.contains('title')).toBe(true);
+ expect(p2.element.classList.contains('page1')).toBe(true);
+ expect(p3.element.classList.contains('loading')).toBe(true);
+
+ resetLog();
+ cancelAllPlayers(players);
+
+ cmp.page2 = true;
+ cmp.loading = false;
+ fixture.detectChanges();
+ engine.flush();
+
+ players = getLog();
+ expect(players.length).toEqual(3);
+ [p1, p2, p3] = players;
+
+ expect(p1.element.classList.contains('loading')).toBe(true);
+ expect(p2.element.classList.contains('title')).toBe(true);
+ expect(p3.element.classList.contains('page2')).toBe(true);
+ });
+
it('should emulate leave animation callbacks for all sub elements that have leave triggers within the component',
fakeAsync(() => {
@Component({
diff --git a/packages/core/test/animation/animation_router_integration_spec.ts b/packages/core/test/animation/animation_router_integration_spec.ts
index f540f3c7af..545150fe19 100644
--- a/packages/core/test/animation/animation_router_integration_spec.ts
+++ b/packages/core/test/animation/animation_router_integration_spec.ts
@@ -351,6 +351,92 @@ export function main() {
{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: `
+
+
+
+
Page Ready
+
loading...
+
+
+
+
+ `
+ })
+ class ContainerCmp {
+ loading = false;
+
+ constructor(public router: Router) {}
+
+ 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.get(ɵ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.getRealPlayer().players;
+ expect(innerPlayers.length).toEqual(2);
+
+ const [ip1, ip2] = innerPlayers;
+ expect(ip1.element.innerText).toEqual('page1');
+ expect(ip2.element.innerText).toEqual('page2');
+ }));
});
}