feat(ivy): support WrappedValue in pipes (FW-726) (#27409)

PR Close #27409
This commit is contained in:
Marc Laval 2018-12-03 11:40:07 +01:00 committed by Igor Minar
parent e31992c112
commit bd864fb274
3 changed files with 536 additions and 514 deletions

View File

@ -6,13 +6,16 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {WrappedValue} from '../change_detection/change_detection_util';
import {PipeTransform} from '../change_detection/pipe_transform'; import {PipeTransform} from '../change_detection/pipe_transform';
import {load, store} from './instructions'; import {load, store} from './instructions';
import {PipeDef, PipeDefList} from './interfaces/definition'; import {PipeDef, PipeDefList} from './interfaces/definition';
import {HEADER_OFFSET, TVIEW} from './interfaces/view'; import {HEADER_OFFSET, TVIEW} from './interfaces/view';
import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunctionV} from './pure_function'; import {pureFunction1, pureFunction2, pureFunction3, pureFunction4, pureFunctionV} from './pure_function';
import {getLView} from './state'; import {getBindingRoot, getLView} from './state';
import {NO_CHANGE} from './tokens';
/** /**
* Create a pipe. * Create a pipe.
@ -74,8 +77,9 @@ function getPipeDef(name: string, registry: PipeDefList | null): PipeDef<any> {
*/ */
export function pipeBind1(index: number, slotOffset: number, v1: any): any { export function pipeBind1(index: number, slotOffset: number, v1: any): any {
const pipeInstance = load<PipeTransform>(index); const pipeInstance = load<PipeTransform>(index);
return isPure(index) ? pureFunction1(slotOffset, pipeInstance.transform, v1, pipeInstance) : return unwrapValue(
pipeInstance.transform(v1); isPure(index) ? pureFunction1(slotOffset, pipeInstance.transform, v1, pipeInstance) :
pipeInstance.transform(v1));
} }
/** /**
@ -91,8 +95,9 @@ export function pipeBind1(index: number, slotOffset: number, v1: any): any {
*/ */
export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any): any { export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any): any {
const pipeInstance = load<PipeTransform>(index); const pipeInstance = load<PipeTransform>(index);
return isPure(index) ? pureFunction2(slotOffset, pipeInstance.transform, v1, v2, pipeInstance) : return unwrapValue(
pipeInstance.transform(v1, v2); isPure(index) ? pureFunction2(slotOffset, pipeInstance.transform, v1, v2, pipeInstance) :
pipeInstance.transform(v1, v2));
} }
/** /**
@ -109,9 +114,9 @@ export function pipeBind2(index: number, slotOffset: number, v1: any, v2: any):
*/ */
export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v3: any): any { export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v3: any): any {
const pipeInstance = load<PipeTransform>(index); const pipeInstance = load<PipeTransform>(index);
return isPure(index) ? return unwrapValue(
pureFunction3(slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) : isPure(index) ? pureFunction3(slotOffset, pipeInstance.transform, v1, v2, v3, pipeInstance) :
pipeInstance.transform(v1, v2, v3); pipeInstance.transform(v1, v2, v3));
} }
/** /**
@ -130,9 +135,10 @@ export function pipeBind3(index: number, slotOffset: number, v1: any, v2: any, v
export function pipeBind4( export function pipeBind4(
index: number, slotOffset: number, v1: any, v2: any, v3: any, v4: any): any { index: number, slotOffset: number, v1: any, v2: any, v3: any, v4: any): any {
const pipeInstance = load<PipeTransform>(index); const pipeInstance = load<PipeTransform>(index);
return isPure(index) ? return unwrapValue(
isPure(index) ?
pureFunction4(slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) : pureFunction4(slotOffset, pipeInstance.transform, v1, v2, v3, v4, pipeInstance) :
pipeInstance.transform(v1, v2, v3, v4); pipeInstance.transform(v1, v2, v3, v4));
} }
/** /**
@ -147,10 +153,26 @@ export function pipeBind4(
*/ */
export function pipeBindV(index: number, slotOffset: number, values: any[]): any { export function pipeBindV(index: number, slotOffset: number, values: any[]): any {
const pipeInstance = load<PipeTransform>(index); const pipeInstance = load<PipeTransform>(index);
return isPure(index) ? pureFunctionV(slotOffset, pipeInstance.transform, values, pipeInstance) : return unwrapValue(
pipeInstance.transform.apply(pipeInstance, values); isPure(index) ? pureFunctionV(slotOffset, pipeInstance.transform, values, pipeInstance) :
pipeInstance.transform.apply(pipeInstance, values));
} }
function isPure(index: number): boolean { function isPure(index: number): boolean {
return (<PipeDef<any>>getLView()[TVIEW].data[index + HEADER_OFFSET]).pure; return (<PipeDef<any>>getLView()[TVIEW].data[index + HEADER_OFFSET]).pure;
} }
/**
* Unwrap the output of a pipe transformation.
* In order to trick change detection into considering that the new value is always different from
* the old one, the old value is overwritten by NO_CHANGE.
*
* @param newValue the pipe transformation output.
*/
function unwrapValue(newValue: any): any {
if (WrappedValue.isWrapped(newValue)) {
newValue = WrappedValue.unwrap(newValue);
getLView()[getBindingRoot()] = NO_CHANGE;
}
return newValue;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Directive as _Directive, InjectionToken, OnChanges, OnDestroy, Pipe as _Pipe, PipeTransform, createInjector, defineInjectable, defineInjector, ɵNgModuleDef as NgModuleDef, ɵdefineComponent as defineComponent, ɵdirectiveInject as directiveInject} from '@angular/core'; import {Directive as _Directive, InjectionToken, OnChanges, OnDestroy, Pipe as _Pipe, PipeTransform, WrappedValue, createInjector, defineInjectable, defineInjector, ɵNgModuleDef as NgModuleDef, ɵdefineComponent as defineComponent, ɵdirectiveInject as directiveInject} from '@angular/core';
import {expect} from '@angular/platform-browser/testing/src/matchers'; import {expect} from '@angular/platform-browser/testing/src/matchers';
import {defineDirective, definePipe} from '../../src/render3/definition'; import {defineDirective, definePipe} from '../../src/render3/definition';
@ -15,7 +15,7 @@ import {RenderFlags} from '../../src/render3/interfaces/definition';
import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe'; import {pipe, pipeBind1, pipeBind3, pipeBind4, pipeBindV} from '../../src/render3/pipe';
import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2'; import {RenderLog, getRendererFactory2, patchLoggingRenderer2} from './imported_renderer2';
import {ComponentFixture, createComponent, getDirectiveOnNode, renderToHtml} from './render_util'; import {ComponentFixture, TemplateFixture, createComponent, getDirectiveOnNode, renderToHtml} from './render_util';
const Directive: typeof _Directive = function(...args: any[]): any { const Directive: typeof _Directive = function(...args: any[]): any {
// In test we use @Directive for documentation only so it's safe to mock out the implementation. // In test we use @Directive for documentation only so it's safe to mock out the implementation.
@ -416,6 +416,45 @@ describe('pipe', () => {
}); });
describe('WrappedValue', () => {
@Pipe({name: 'wrappingPipe'})
class WrappingPipe implements PipeTransform {
transform(value: any) { return new WrappedValue('Bar'); }
static ngPipeDef = definePipe({
name: 'wrappingPipe',
type: WrappingPipe,
factory: function WrappingPipe_Factory() { return new WrappingPipe(); },
pure: false
});
}
function createTemplate() {
text(0);
pipe(1, 'wrappingPipe');
}
function updateTemplate() { textBinding(0, interpolation1('', pipeBind1(1, 1, null), '')); }
it('should unwrap', () => {
const fixture =
new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]);
expect(fixture.html).toEqual('Bar');
});
it('should force change detection', () => {
const fixture =
new TemplateFixture(createTemplate, updateTemplate, 2, 3, undefined, [WrappingPipe]);
expect(fixture.html).toEqual('Bar');
fixture.hostElement.childNodes[0] !.textContent = 'Foo';
expect(fixture.html).toEqual('Foo');
fixture.update();
expect(fixture.html).toEqual('Bar');
});
});
}); });
@Pipe({name: 'countingPipe'}) @Pipe({name: 'countingPipe'})

View File

@ -531,7 +531,6 @@ describe('Integration', () => {
expect(location.path()).toEqual('/record/33'); expect(location.path()).toEqual('/record/33');
})); }));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should skip location update when using NavigationExtras.skipLocationChange with navigateByUrl', it('should skip location update when using NavigationExtras.skipLocationChange with navigateByUrl',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = TestBed.createComponent(RootCmp); const fixture = TestBed.createComponent(RootCmp);
@ -553,7 +552,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]'); expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should skip location update when using NavigationExtras.skipLocationChange with navigate', it('should skip location update when using NavigationExtras.skipLocationChange with navigate',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = TestBed.createComponent(RootCmp); const fixture = TestBed.createComponent(RootCmp);
@ -575,7 +573,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]'); expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should eagerly update the URL with urlUpdateStrategy="eagar"', it('should eagerly update the URL with urlUpdateStrategy="eagar"',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = TestBed.createComponent(RootCmp); const fixture = TestBed.createComponent(RootCmp);
@ -601,7 +598,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]'); expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
}))); })));
fixmeIvy('FW-???: Error: ExpressionChangedAfterItHasBeenCheckedError') &&
it('should eagerly update URL after redirects are applied with urlUpdateStrategy="eagar"', it('should eagerly update URL after redirects are applied with urlUpdateStrategy="eagar"',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = TestBed.createComponent(RootCmp); const fixture = TestBed.createComponent(RootCmp);
@ -636,7 +632,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]'); expect(fixture.nativeElement).toHaveText('team 33 [ , right: ]');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should navigate back and forward', it('should navigate back and forward',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -644,9 +639,8 @@ describe('Integration', () => {
router.resetConfig([{ router.resetConfig([{
path: 'team/:id', path: 'team/:id',
component: TeamCmp, component: TeamCmp,
children: [ children:
{path: 'simple', component: SimpleCmp}, {path: 'user/:name', component: UserCmp} [{path: 'simple', component: SimpleCmp}, {path: 'user/:name', component: UserCmp}]
]
}]); }]);
let event: NavigationStart; let event: NavigationStart;
@ -698,7 +692,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('route'); expect(fixture.nativeElement).toHaveText('route');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should navigate when locations changes', it('should navigate when locations changes',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -871,7 +864,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText(''); expect(fixture.nativeElement).toHaveText('');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should set query params and fragment', fakeAsync(inject([Router], (router: Router) => { it('should set query params and fragment', fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -909,9 +901,7 @@ describe('Integration', () => {
])).toThrowError(`The requested path contains undefined segment at index 0`); ])).toThrowError(`The requested path contains undefined segment at index 0`);
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') && it('should push params only when they change', fakeAsync(inject([Router], (router: Router) => {
it('should push params only when they change',
fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
router.resetConfig([{ router.resetConfig([{
@ -958,7 +948,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('simple'); expect(fixture.nativeElement).toHaveText('simple');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should cancel in-flight navigations', fakeAsync(inject([Router], (router: Router) => { it('should cancel in-flight navigations', fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -1222,7 +1211,6 @@ describe('Integration', () => {
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should replace state when path is equal to current path', it('should replace state when path is equal to current path',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -1230,9 +1218,8 @@ describe('Integration', () => {
router.resetConfig([{ router.resetConfig([{
path: 'team/:id', path: 'team/:id',
component: TeamCmp, component: TeamCmp,
children: [ children:
{path: 'simple', component: SimpleCmp}, {path: 'user/:name', component: UserCmp} [{path: 'simple', component: SimpleCmp}, {path: 'user/:name', component: UserCmp}]
]
}]); }]);
router.navigateByUrl('/team/33/simple'); router.navigateByUrl('/team/33/simple');
@ -1249,7 +1236,6 @@ describe('Integration', () => {
expect(location.path()).toEqual('/team/33/simple'); expect(location.path()).toEqual('/team/33/simple');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should handle componentless paths', it('should handle componentless paths',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmpWithTwoOutlets); const fixture = createRoot(router, RootCmpWithTwoOutlets);
@ -1567,7 +1553,6 @@ describe('Integration', () => {
}); });
describe('router links', () => { describe('router links', () => {
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should support skipping location update for anchor router links', it('should support skipping location update for anchor router links',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = TestBed.createComponent(RootCmp); const fixture = TestBed.createComponent(RootCmp);
@ -1599,7 +1584,6 @@ describe('Integration', () => {
expect(location.path()).toEqual('/team/22'); expect(location.path()).toEqual('/team/22');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should support string router links', fakeAsync(inject([Router], (router: Router) => { it('should support string router links', fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -1607,8 +1591,7 @@ describe('Integration', () => {
path: 'team/:id', path: 'team/:id',
component: TeamCmp, component: TeamCmp,
children: [ children: [
{path: 'link', component: StringLinkCmp}, {path: 'link', component: StringLinkCmp}, {path: 'simple', component: SimpleCmp}
{path: 'simple', component: SimpleCmp}
] ]
}]); }]);
@ -1741,9 +1724,7 @@ describe('Integration', () => {
expect(native.getAttribute('href')).toEqual('/home?a=123&q=456'); expect(native.getAttribute('href')).toEqual('/home?a=123&q=456');
})); }));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') && it('should support using links on non-a tags', fakeAsync(inject([Router], (router: Router) => {
it('should support using links on non-a tags',
fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
router.resetConfig([{ router.resetConfig([{
@ -1767,7 +1748,6 @@ describe('Integration', () => {
expect(fixture.nativeElement).toHaveText('team 33 [ simple, right: ]'); expect(fixture.nativeElement).toHaveText('team 33 [ simple, right: ]');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should support absolute router links', fakeAsync(inject([Router], (router: Router) => { it('should support absolute router links', fakeAsync(inject([Router], (router: Router) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -1775,8 +1755,7 @@ describe('Integration', () => {
path: 'team/:id', path: 'team/:id',
component: TeamCmp, component: TeamCmp,
children: [ children: [
{path: 'link', component: AbsoluteLinkCmp}, {path: 'link', component: AbsoluteLinkCmp}, {path: 'simple', component: SimpleCmp}
{path: 'simple', component: SimpleCmp}
] ]
}]); }]);
@ -2556,14 +2535,11 @@ describe('Integration', () => {
}); });
describe('should not deactivate a route when CanDeactivate returns false', () => { describe('should not deactivate a route when CanDeactivate returns false', () => {
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') && it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
it('works',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
router.resetConfig([ router.resetConfig(
{path: 'team/:id', component: TeamCmp, canDeactivate: ['CanDeactivateTeam']} [{path: 'team/:id', component: TeamCmp, canDeactivate: ['CanDeactivateTeam']}]);
]);
router.navigateByUrl('/team/22'); router.navigateByUrl('/team/22');
advance(fixture); advance(fixture);
@ -2651,7 +2627,6 @@ describe('Integration', () => {
expect(location.path()).toEqual('/two-outlets/(a)'); expect(location.path()).toEqual('/two-outlets/(a)');
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('works with a nested route', it('works with a nested route',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -2660,11 +2635,8 @@ describe('Integration', () => {
path: 'team/:id', path: 'team/:id',
component: TeamCmp, component: TeamCmp,
children: [ children: [
{path: '', pathMatch: 'full', component: SimpleCmp}, { {path: '', pathMatch: 'full', component: SimpleCmp},
path: 'user/:name', {path: 'user/:name', component: UserCmp, canDeactivate: ['CanDeactivateUser']}
component: UserCmp,
canDeactivate: ['CanDeactivateUser']
}
] ]
}]); }]);
@ -2793,7 +2765,6 @@ describe('Integration', () => {
}); });
}); });
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should pass next state as the 4 argument when guard is a class', it('should pass next state as the 4 argument when guard is a class',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
@ -2811,16 +2782,13 @@ describe('Integration', () => {
expect(log).toEqual(['/team/22', '/team/33']); expect(log).toEqual(['/team/22', '/team/33']);
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should pass next state as the 4 argument when guard is a function', it('should pass next state as the 4 argument when guard is a function',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => { fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
router.resetConfig([{ router.resetConfig([
path: 'team/:id', {path: 'team/:id', component: TeamCmp, canDeactivate: ['FunctionWithNextState']}
component: TeamCmp, ]);
canDeactivate: ['FunctionWithNextState']
}]);
router.navigateByUrl('/team/22'); router.navigateByUrl('/team/22');
advance(fixture); advance(fixture);
@ -2844,9 +2812,7 @@ describe('Integration', () => {
beforeEach(() => { TestBed.configureTestingModule({providers: [AlwaysTrue]}); }); beforeEach(() => { TestBed.configureTestingModule({providers: [AlwaysTrue]}); });
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') && it('works', fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
it('works',
fakeAsync(inject([Router, Location], (router: Router, location: Location) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
router.resetConfig( router.resetConfig(
@ -3205,11 +3171,9 @@ describe('Integration', () => {
}); });
}); });
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should call guards in the right order', it('should call guards in the right order',
fakeAsync(inject( fakeAsync(inject(
[Router, Location, Logger], [Router, Location, Logger], (router: Router, location: Location, logger: Logger) => {
(router: Router, location: Location, logger: Logger) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
router.resetConfig([{ router.resetConfig([{
@ -3236,11 +3200,9 @@ describe('Integration', () => {
]); ]);
}))); })));
fixmeIvy('FW-726: Pipe instructions do not support WrappedValue') &&
it('should call deactivate guards from bottom to top', it('should call deactivate guards from bottom to top',
fakeAsync(inject( fakeAsync(inject(
[Router, Location, Logger], [Router, Location, Logger], (router: Router, location: Location, logger: Logger) => {
(router: Router, location: Location, logger: Logger) => {
const fixture = createRoot(router, RootCmp); const fixture = createRoot(router, RootCmp);
router.resetConfig([{ router.resetConfig([{
@ -3248,9 +3210,8 @@ describe('Integration', () => {
children: [{ children: [{
path: 'team/:id', path: 'team/:id',
canDeactivate: ['canDeactivate_team'], canDeactivate: ['canDeactivate_team'],
children: [ children:
{path: '', component: SimpleCmp, canDeactivate: ['canDeactivate_simple']} [{path: '', component: SimpleCmp, canDeactivate: ['canDeactivate_simple']}],
],
component: TeamCmp component: TeamCmp
}] }]
}]); }]);