describe('redirects', () => { var tcb: TestComponentBuilder; var rootTC: ComponentFixture<any>; var rtr: any /** TODO #9100 */; beforeEachProviders(() => TEST_ROUTER_PROVIDERS); beforeEach(inject( [TestComponentBuilder, Router], (tcBuilder: any /** TODO #9100 */, router: any /** TODO #9100 */) => { tcb = tcBuilder; rtr = router; childCmpInstanceCount = 0; cmpInstanceCount = 0; })); it('should apply when navigating by URL', inject( [AsyncTestCompleter, Location], (async: AsyncTestCompleter, location: any /** TODO #9100 */) => { compile(tcb) .then((rtc) => { rootTC = rtc; }) .then((_) => rtr.config([ new Redirect({path: '/original', redirectTo: ['Hello']}), new Route({path: '/redirected', component: HelloCmp, name: 'Hello'}) ])) .then((_) => rtr.navigateByUrl('/original')) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('hello'); expect(location.urlChanges).toEqual(['/redirected']); async.done(); }); })); it('should recognize and apply absolute redirects', inject( [AsyncTestCompleter, Location], (async: AsyncTestCompleter, location: any /** TODO #9100 */) => { compile(tcb) .then((rtc) => { rootTC = rtc; }) .then((_) => rtr.config([ new Redirect({path: '/original', redirectTo: ['/Hello']}), new Route({path: '/redirected', component: HelloCmp, name: 'Hello'}) ])) .then((_) => rtr.navigateByUrl('/original')) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('hello'); expect(location.urlChanges).toEqual(['/redirected']); async.done(); }); })); it('should recognize and apply relative child redirects', inject( [AsyncTestCompleter, Location], (async: AsyncTestCompleter, location: any /** TODO #9100 */) => { compile(tcb) .then((rtc) => { rootTC = rtc; }) .then((_) => rtr.config([ new Redirect({path: '/original', redirectTo: ['./Hello']}), new Route({path: '/redirected', component: HelloCmp, name: 'Hello'}) ])) .then((_) => rtr.navigateByUrl('/original')) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('hello'); expect(location.urlChanges).toEqual(['/redirected']); async.done(); }); })); it('should recognize and apply relative parent redirects', inject( [AsyncTestCompleter, Location], (async: AsyncTestCompleter, location: any /** TODO #9100 */) => { compile(tcb) .then((rtc) => { rootTC = rtc; }) .then((_) => rtr.config([ new Route({path: '/original/...', component: RedirectToParentCmp}), new Route({path: '/redirected', component: HelloCmp, name: 'HelloSib'}) ])) .then((_) => rtr.navigateByUrl('/original/child-redirect')) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('hello'); expect(location.urlChanges).toEqual(['/redirected']); async.done(); }); })); it('should not redirect when redirect is less specific than other matching routes', inject( [AsyncTestCompleter, Location], (async: AsyncTestCompleter, location: any /** TODO #9100 */) => { compile(tcb) .then((rtc) => { rootTC = rtc; }) .then((_) => rtr.config([ new Route({path: '/foo', component: HelloCmp, name: 'Hello'}), new Route({path: '/:param', component: GoodbyeCmp, name: 'Goodbye'}), new Redirect({path: '/*rest', redirectTo: ['/Hello']}) ])) .then((_) => rtr.navigateByUrl('/bye')) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('goodbye'); expect(location.urlChanges).toEqual(['/bye']); async.done(); }); })); });
describe('CompileMetadataResolver', () => { beforeEachProviders(() => TEST_PROVIDERS); describe('getMetadata', () => { it('should read metadata', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { var meta = resolver.getDirectiveMetadata(ComponentWithEverything); expect(meta.selector).toEqual('someSelector'); expect(meta.exportAs).toEqual('someExportAs'); expect(meta.isComponent).toBe(true); expect(meta.type.runtime).toBe(ComponentWithEverything); expect(meta.type.name).toEqual(stringify(ComponentWithEverything)); expect(meta.lifecycleHooks).toEqual(LIFECYCLE_HOOKS_VALUES); expect(meta.changeDetection).toBe(ChangeDetectionStrategy.CheckAlways); expect(meta.inputs).toEqual({'someProp': 'someProp'}); expect(meta.outputs).toEqual({'someEvent': 'someEvent'}); expect(meta.hostListeners).toEqual({'someHostListener': 'someHostListenerExpr'}); expect(meta.hostProperties).toEqual({'someHostProp': 'someHostPropExpr'}); expect(meta.hostAttributes).toEqual({'someHostAttr': 'someHostAttrValue'}); expect(meta.template.encapsulation).toBe(ViewEncapsulation.Emulated); expect(meta.template.styles).toEqual(['someStyle']); expect(meta.template.styleUrls).toEqual(['someStyleUrl']); expect(meta.template.template).toEqual('someTemplate'); expect(meta.template.templateUrl).toEqual('someTemplateUrl'); expect(meta.template.interpolation).toEqual(['{{', '}}']); })); it('should use the moduleUrl from the reflector if none is given', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { var value: string = resolver.getDirectiveMetadata(ComponentWithoutModuleId).type.moduleUrl; var expectedEndValue = IS_DART ? 'test/compiler/metadata_resolver_spec.dart' : './ComponentWithoutModuleId'; expect(value.endsWith(expectedEndValue)).toBe(true); })); it('should throw when metadata is incorrectly typed', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(() => resolver.getDirectiveMetadata(MalformedStylesComponent)) .toThrowError(`Expected 'styles' to be an array of strings.`); })); it('should throw with descriptive error message when provider token can not be resolved', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(() => resolver.getDirectiveMetadata(MyBrokenComp1)) .toThrowError(`Can't resolve all parameters for MyBrokenComp1: (?).`); })); it('should throw with descriptive error message when a param token of a dependency is undefined', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(() => resolver.getDirectiveMetadata(MyBrokenComp2)) .toThrowError(`Can't resolve all parameters for NonAnnotatedService: (?).`); })); it('should throw with descriptive error message when one of providers is not present', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(() => resolver.getDirectiveMetadata(MyBrokenComp3)) .toThrowError( `One or more of providers for "MyBrokenComp3" were not defined: [?, SimpleService, ?].`); })); it('should throw with descriptive error message when one of viewProviders is not present', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(() => resolver.getDirectiveMetadata(MyBrokenComp4)) .toThrowError( `One or more of viewProviders for "MyBrokenComp4" were not defined: [?, SimpleService, ?].`); })); it('should throw an error when the interpolation config has invalid symbols', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation1)) .toThrowError(`[' ', ' '] contains unusable interpolation symbol.`); expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation2)) .toThrowError(`['{', '}'] contains unusable interpolation symbol.`); expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation3)) .toThrowError(`['<%', '%>'] contains unusable interpolation symbol.`); expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation4)) .toThrowError(`['&#', '}}'] contains unusable interpolation symbol.`); expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidInterpolation5)) .toThrowError(`['{', '}}'] contains unusable interpolation symbol.`); })); }); describe('getViewDirectivesMetadata', () => { it('should return the directive metadatas', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(resolver.getViewDirectivesMetadata(ComponentWithEverything)) .toContain(resolver.getDirectiveMetadata(SomeDirective)); })); describe('platform directives', () => { beforeEachProviders(() => [{ provide: CompilerConfig, useValue: new CompilerConfig( {genDebugInfo: true, platformDirectives: [ADirective]}) }]); it('should include platform directives when available', inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => { expect(resolver.getViewDirectivesMetadata(ComponentWithEverything)) .toContain(resolver.getDirectiveMetadata(ADirective)); expect(resolver.getViewDirectivesMetadata(ComponentWithEverything)) .toContain(resolver.getDirectiveMetadata(SomeDirective)); })); }); }); });
describe('jit', () => { beforeEachProviders( () => [provide(CompilerConfig, {useValue: new CompilerConfig(true, false, true)})]); declareTests(true); });
describe('router bootstrap', () => { beforeEachProviders(() => [ ROUTER_PROVIDERS, provide(LocationStrategy, {useClass: MockLocationStrategy}), provide(ApplicationRef, {useClass: MockApplicationRef}) ]); // do not refactor out the `bootstrap` functionality. We still want to // keep this test around so we can ensure that bootstrap a router works it('should bootstrap a simple app', inject([AsyncTestCompleter], (async) => { var fakeDoc = getDOM().createHtmlDocument(); var el = getDOM().createElement('app-cmp', fakeDoc); getDOM().appendChild(fakeDoc.body, el); bootstrap(AppCmp, [ ROUTER_PROVIDERS, provide(ROUTER_PRIMARY_COMPONENT, {useValue: AppCmp}), provide(LocationStrategy, {useClass: MockLocationStrategy}), provide(DOCUMENT, {useValue: fakeDoc}), provide(Console, {useClass: DummyConsole}) ]) .then((applicationRef) => { var router = applicationRef.instance.router; router.subscribe((_) => { expect(el).toHaveText('outer { hello }'); expect(applicationRef.instance.location.path()).toEqual(''); async.done(); }); }); })); describe('broken app', () => { beforeEachProviders(() => [provide(ROUTER_PRIMARY_COMPONENT, {useValue: BrokenAppCmp})]); it('should rethrow exceptions from component constructors', inject([AsyncTestCompleter, TestComponentBuilder], (async, tcb: TestComponentBuilder) => { tcb.createAsync(AppCmp).then((fixture) => { var router = fixture.debugElement.componentInstance.router; PromiseWrapper.catchError(router.navigateByUrl('/cause-error'), (error) => { expect(error).toContainError('oops!'); async.done(); }); }); })); }); describe('back button app', () => { beforeEachProviders(() => [provide(ROUTER_PRIMARY_COMPONENT, {useValue: HierarchyAppCmp})]); it('should change the url without pushing a new history state for back navigations', inject([AsyncTestCompleter, TestComponentBuilder], (async, tcb: TestComponentBuilder) => { tcb.createAsync(HierarchyAppCmp) .then((fixture) => { var router = fixture.debugElement.componentInstance.router; var position = 0; var flipped = false; var history = [ ['/parent/child', 'root { parent { hello } }', '/super-parent/child'], ['/super-parent/child', 'root { super-parent { hello2 } }', '/parent/child'], ['/parent/child', 'root { parent { hello } }', false] ]; router.subscribe((_) => { var location = fixture.debugElement.componentInstance.location; var element = fixture.debugElement.nativeElement; var path = location.path(); var entry = history[position]; expect(path).toEqual(entry[0]); expect(element).toHaveText(entry[1]); var nextUrl = entry[2]; if (nextUrl == false) { flipped = true; } if (flipped && position == 0) { async.done(); return; } position = position + (flipped ? -1 : 1); if (flipped) { location.back(); } else { router.navigateByUrl(nextUrl); } }); router.navigateByUrl(history[0][0]); }); }), 1000); }); describe('hierarchical app', () => { beforeEachProviders( () => { return [provide(ROUTER_PRIMARY_COMPONENT, {useValue: HierarchyAppCmp})]; }); it('should bootstrap an app with a hierarchy', inject([AsyncTestCompleter, TestComponentBuilder], (async, tcb: TestComponentBuilder) => { tcb.createAsync(HierarchyAppCmp) .then((fixture) => { var router = fixture.debugElement.componentInstance.router; router.subscribe((_) => { expect(fixture.debugElement.nativeElement) .toHaveText('root { parent { hello } }'); expect(fixture.debugElement.componentInstance.location.path()) .toEqual('/parent/child'); async.done(); }); router.navigateByUrl('/parent/child'); }); })); // TODO(btford): mock out level lower than LocationStrategy once that level exists xdescribe('custom app base ref', () => { beforeEachProviders(() => { return [provide(APP_BASE_HREF, {useValue: '/my/app'})]; }); it('should bootstrap', inject([AsyncTestCompleter, TestComponentBuilder], (async, tcb: TestComponentBuilder) => { tcb.createAsync(HierarchyAppCmp) .then((fixture) => { var router = fixture.debugElement.componentInstance.router; router.subscribe((_) => { expect(fixture.debugElement.nativeElement) .toHaveText('root { parent { hello } }'); expect(fixture.debugElement.componentInstance.location.path()) .toEqual('/my/app/parent/child'); async.done(); }); router.navigateByUrl('/parent/child'); }); })); }); }); describe('querystring params app', () => { beforeEachProviders( () => { return [provide(ROUTER_PRIMARY_COMPONENT, {useValue: QueryStringAppCmp})]; }); it('should recognize and return querystring params with the injected RouteParams', inject([AsyncTestCompleter, TestComponentBuilder], (async, tcb: TestComponentBuilder) => { tcb.createAsync(QueryStringAppCmp) .then((fixture) => { var router = fixture.debugElement.componentInstance.router; router.subscribe((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement) .toHaveText('qParam = search-for-something'); /* expect(applicationRef.hostComponent.location.path()) .toEqual('/qs?q=search-for-something');*/ async.done(); }); router.navigateByUrl('/qs?q=search-for-something'); fixture.detectChanges(); }); })); }); describe('activate event on outlet', () => { let tcb: TestComponentBuilder = null; beforeEachProviders(() => [provide(ROUTER_PRIMARY_COMPONENT, {useValue: AppCmp})]); beforeEach(inject([TestComponentBuilder], (testComponentBuilder) => { tcb = testComponentBuilder; })); it('should get a reference and pass data to components loaded inside of outlets', inject([AsyncTestCompleter], (async) => { tcb.createAsync(AppWithOutletListeners) .then(fixture => { let appInstance = fixture.debugElement.componentInstance; let router = appInstance.router; router.subscribe((_) => { fixture.detectChanges(); expect(appInstance.helloCmp).toBeAnInstanceOf(HelloCmp); expect(appInstance.helloCmp.message).toBe('Ahoy'); async.done(); }); // TODO(juliemr): This isn't necessary for the test to pass - figure // out what's going on. // router.navigateByUrl('/rainbow(pony)'); }); })); }); });
function asyncRoutesWithAsyncChildrenWithoutParamsWithDefaultRoutes() { var rootTC: any /** TODO #9100 */; var tcb: any /** TODO #9100 */; var rtr: any /** TODO #9100 */; beforeEachProviders(() => TEST_ROUTER_PROVIDERS); beforeEach(inject( [TestComponentBuilder, Router], (tcBuilder: any /** TODO #9100 */, router: any /** TODO #9100 */) => { tcb = tcBuilder; rtr = router; })); it('should navigate by URL', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { compile(tcb, `outer { <router-outlet></router-outlet> }`) .then((rtc) => {rootTC = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/a/...', loader: asyncDefaultParentCmpLoader, name: 'Parent'})])) .then((_) => rtr.navigateByUrl('/a')) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('outer { inner { hello } }'); async.done(); }); })); it('should navigate by link DSL', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { compile(tcb, `outer { <router-outlet></router-outlet> }`) .then((rtc) => {rootTC = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/a/...', loader: asyncDefaultParentCmpLoader, name: 'Parent'})])) .then((_) => rtr.navigate(['/Parent'])) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('outer { inner { hello } }'); async.done(); }); })); it('should generate a link URL', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { compile( tcb, `<a [routerLink]="['Parent']">nav to child</a> | outer { <router-outlet></router-outlet> }`) .then((rtc) => {rootTC = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/a/...', loader: asyncDefaultParentCmpLoader, name: 'Parent'})])) .then((_) => { rootTC.detectChanges(); expect(getHref(getLinkElement(rootTC))).toEqual('/a'); async.done(); }); })); it('should navigate from a link click', inject( [AsyncTestCompleter, Location], (async: AsyncTestCompleter, location: any /** TODO #9100 */) => { compile( tcb, `<a [routerLink]="['Parent']">nav to child</a> | outer { <router-outlet></router-outlet> }`) .then((rtc) => {rootTC = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/a/...', loader: asyncDefaultParentCmpLoader, name: 'Parent'})])) .then((_) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement).toHaveText('nav to child | outer { }'); rtr.subscribe((_: any /** TODO #9100 */) => { rootTC.detectChanges(); expect(rootTC.debugElement.nativeElement) .toHaveText('nav to child | outer { inner { hello } }'); expect(location.urlChanges).toEqual(['/a/b']); async.done(); }); clickOnElement(getLinkElement(rootTC)); }); })); }
function asyncRoutesWithoutChildrenWithoutParams() { var fixture: any /** TODO #9100 */; var tcb: any /** TODO #9100 */; var rtr: any /** TODO #9100 */; beforeEachProviders(() => TEST_ROUTER_PROVIDERS); beforeEach(inject( [TestComponentBuilder, Router], (tcBuilder: any /** TODO #9100 */, router: any /** TODO #9100 */) => { tcb = tcBuilder; rtr = router; })); it('should navigate by URL', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/test', loader: helloCmpLoader, name: 'Hello'})])) .then((_) => rtr.navigateByUrl('/test')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('hello'); async.done(); }); })); it('should navigate by link DSL', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/test', loader: helloCmpLoader, name: 'Hello'})])) .then((_) => rtr.navigate(['/Hello'])) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('hello'); async.done(); }); })); it('should generate a link URL', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { compile(tcb, `<a [routerLink]="['Hello']">go to hello</a> | <router-outlet></router-outlet>`) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/test', loader: helloCmpLoader, name: 'Hello'})])) .then((_) => { fixture.detectChanges(); expect(getHref(getLinkElement(fixture))).toEqual('/test'); async.done(); }); })); it('should navigate from a link click', inject( [AsyncTestCompleter, Location], (async: AsyncTestCompleter, location: any /** TODO #9100 */) => { compile( tcb, `<a [routerLink]="['Hello']">go to hello</a> | <router-outlet></router-outlet>`) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new AsyncRoute( {path: '/test', loader: helloCmpLoader, name: 'Hello'})])) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('go to hello | '); rtr.subscribe((_: any /** TODO #9100 */) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('go to hello | hello'); expect(location.urlChanges).toEqual(['/test']); async.done(); }); clickOnElement(getLinkElement(fixture)); }); })); }
describe('HashLocationStrategy', () => { var platformLocation: SpyPlatformLocation; var locationStrategy: HashLocationStrategy; beforeEachProviders( () => [HashLocationStrategy, provide(PlatformLocation, {useClass: SpyPlatformLocation})]); describe('without APP_BASE_HREF', () => { beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => { platformLocation = pl; locationStrategy = ls; platformLocation.spy('pushState'); platformLocation.pathname = ''; })); it('should prepend urls with a hash for non-empty URLs', () => { expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#foo'); locationStrategy.pushState(null, 'Title', 'foo', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#foo'); }); it('should prepend urls with a hash for URLs with query params', () => { expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#foo?bar'); locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz'); expect(platformLocation.spy('pushState')) .toHaveBeenCalledWith(null, 'Title', '#foo?bar=baz'); }); it('should prepend urls with a hash for URLs with just query params', () => { expect(locationStrategy.prepareExternalUrl('?bar')).toEqual('#?bar'); locationStrategy.pushState(null, 'Title', '', 'bar=baz'); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#?bar=baz'); }); it('should not prepend a hash to external urls for an empty internal URL', () => { expect(locationStrategy.prepareExternalUrl('')).toEqual(''); locationStrategy.pushState(null, 'Title', '', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', ''); }); }); describe('with APP_BASE_HREF with neither leading nor trailing slash', () => { beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: 'app'})]); beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => { platformLocation = pl; locationStrategy = ls; platformLocation.spy('pushState'); platformLocation.pathname = ''; })); it('should prepend urls with a hash for non-empty URLs', () => { expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#app/foo'); locationStrategy.pushState(null, 'Title', 'foo', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#app/foo'); }); it('should prepend urls with a hash for URLs with query params', () => { expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#app/foo?bar'); locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz'); expect(platformLocation.spy('pushState')) .toHaveBeenCalledWith(null, 'Title', '#app/foo?bar=baz'); }); it('should not prepend a hash to external urls for an empty internal URL', () => { expect(locationStrategy.prepareExternalUrl('')).toEqual('#app'); locationStrategy.pushState(null, 'Title', '', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#app'); }); }); describe('with APP_BASE_HREF with leading slash', () => { beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: '/app'})]); beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => { platformLocation = pl; locationStrategy = ls; platformLocation.spy('pushState'); platformLocation.pathname = ''; })); it('should prepend urls with a hash for non-empty URLs', () => { expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#/app/foo'); locationStrategy.pushState(null, 'Title', 'foo', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app/foo'); }); it('should prepend urls with a hash for URLs with query params', () => { expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#/app/foo?bar'); locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz'); expect(platformLocation.spy('pushState')) .toHaveBeenCalledWith(null, 'Title', '#/app/foo?bar=baz'); }); it('should not prepend a hash to external urls for an empty internal URL', () => { expect(locationStrategy.prepareExternalUrl('')).toEqual('#/app'); locationStrategy.pushState(null, 'Title', '', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app'); }); }); describe('with APP_BASE_HREF with both leading and trailing slash', () => { beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: '/app/'})]); beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => { platformLocation = pl; locationStrategy = ls; platformLocation.spy('pushState'); platformLocation.pathname = ''; })); it('should prepend urls with a hash for non-empty URLs', () => { expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#/app/foo'); locationStrategy.pushState(null, 'Title', 'foo', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app/foo'); }); it('should prepend urls with a hash for URLs with query params', () => { expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#/app/foo?bar'); locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz'); expect(platformLocation.spy('pushState')) .toHaveBeenCalledWith(null, 'Title', '#/app/foo?bar=baz'); }); it('should not prepend a hash to external urls for an empty internal URL', () => { expect(locationStrategy.prepareExternalUrl('')).toEqual('#/app/'); locationStrategy.pushState(null, 'Title', '', ''); expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app/'); }); }); describe('hashLocationStrategy bugs', () => { beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => { platformLocation = pl; locationStrategy = ls; platformLocation.spy('pushState'); platformLocation.pathname = ''; })); it('should not include platform search', () => { platformLocation.search = '?donotinclude'; expect(locationStrategy.path()).toEqual(''); }); it('should not include platform search even with hash', () => { platformLocation.hash = '#hashPath'; platformLocation.search = '?donotinclude'; expect(locationStrategy.path()).toEqual('hashPath'); }); }); });
describe('navigation', () => { var tcb: TestComponentBuilder; var fixture: ComponentFixture<any>; var rtr; beforeEachProviders(() => TEST_ROUTER_PROVIDERS); beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => { tcb = tcBuilder; rtr = router; childCmpInstanceCount = 0; cmpInstanceCount = 0; })); it('should work in a simple case', inject([AsyncTestCompleter], (async) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/test', component: HelloCmp})])) .then((_) => rtr.navigateByUrl('/test')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('hello'); async.done(); }); })); it('should navigate between components with different parameters', inject([AsyncTestCompleter], (async) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/user/:name', component: UserCmp})])) .then((_) => rtr.navigateByUrl('/user/brian')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('hello brian'); }) .then((_) => rtr.navigateByUrl('/user/igor')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('hello igor'); async.done(); }); })); it('should navigate to child routes', inject([AsyncTestCompleter], (async) => { compile(tcb, 'outer { <router-outlet></router-outlet> }') .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/a/...', component: ParentCmp})])) .then((_) => rtr.navigateByUrl('/a/b')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }'); async.done(); }); })); it('should navigate to child routes that capture an empty path', inject([AsyncTestCompleter], (async) => { compile(tcb, 'outer { <router-outlet></router-outlet> }') .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/a/...', component: ParentCmp})])) .then((_) => rtr.navigateByUrl('/a')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }'); async.done(); }); })); it('should navigate to child routes when the root component has an empty path', inject([AsyncTestCompleter, Location], (async, location) => { compile(tcb, 'outer { <router-outlet></router-outlet> }') .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/...', component: ParentCmp})])) .then((_) => rtr.navigateByUrl('/b')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }'); expect(location.urlChanges).toEqual(['/b']); async.done(); }); })); it('should navigate to child routes of async routes', inject([AsyncTestCompleter], (async) => { compile(tcb, 'outer { <router-outlet></router-outlet> }') .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new AsyncRoute({path: '/a/...', loader: parentLoader})])) .then((_) => rtr.navigateByUrl('/a/b')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }'); async.done(); }); })); it('should reuse common parent components', inject([AsyncTestCompleter], (async) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/team/:id/...', component: TeamCmp})])) .then((_) => rtr.navigateByUrl('/team/angular/user/rado')) .then((_) => { fixture.detectChanges(); expect(cmpInstanceCount).toBe(1); expect(fixture.debugElement.nativeElement).toHaveText('team angular { hello rado }'); }) .then((_) => rtr.navigateByUrl('/team/angular/user/victor')) .then((_) => { fixture.detectChanges(); expect(cmpInstanceCount).toBe(1); expect(fixture.debugElement.nativeElement) .toHaveText('team angular { hello victor }'); async.done(); }); })); it('should not reuse children when parent components change', inject([AsyncTestCompleter], (async) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/team/:id/...', component: TeamCmp})])) .then((_) => rtr.navigateByUrl('/team/angular/user/rado')) .then((_) => { fixture.detectChanges(); expect(cmpInstanceCount).toBe(1); expect(childCmpInstanceCount).toBe(1); expect(fixture.debugElement.nativeElement).toHaveText('team angular { hello rado }'); }) .then((_) => rtr.navigateByUrl('/team/dart/user/rado')) .then((_) => { fixture.detectChanges(); expect(cmpInstanceCount).toBe(2); expect(childCmpInstanceCount).toBe(2); expect(fixture.debugElement.nativeElement).toHaveText('team dart { hello rado }'); async.done(); }); })); it('should inject route data into component', inject([AsyncTestCompleter], (async) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([ new Route({path: '/route-data', component: RouteDataCmp, data: {isAdmin: true}}) ])) .then((_) => rtr.navigateByUrl('/route-data')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('true'); async.done(); }); })); it('should inject route data into component with AsyncRoute', inject([AsyncTestCompleter], (async) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([ new AsyncRoute( {path: '/route-data', loader: asyncRouteDataCmp, data: {isAdmin: true}}) ])) .then((_) => rtr.navigateByUrl('/route-data')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText('true'); async.done(); }); })); it('should inject empty object if the route has no data property', inject([AsyncTestCompleter], (async) => { compile(tcb) .then((rtc) => {fixture = rtc}) .then((_) => rtr.config( [new Route({path: '/route-data-default', component: RouteDataCmp})])) .then((_) => rtr.navigateByUrl('/route-data-default')) .then((_) => { fixture.detectChanges(); expect(fixture.debugElement.nativeElement).toHaveText(''); async.done(); }); })); it('should fire an event for each activated component', inject([AsyncTestCompleter], (async) => { compile(tcb, '<router-outlet (activate)="activatedCmp = $event"></router-outlet>') .then((rtc) => {fixture = rtc}) .then((_) => rtr.config([new Route({path: '/test', component: HelloCmp})])) .then((_) => rtr.navigateByUrl('/test')) .then((_) => { // Note: need a timeout so that all promises are flushed var completer = PromiseWrapper.completer(); TimerWrapper.setTimeout(() => { completer.resolve(null); }, 0); return completer.promise; }) .then((_) => { expect(fixture.componentInstance.activatedCmp).toBeAnInstanceOf(HelloCmp); async.done(); }); })); });
describe('navigation', () => { beforeEachProviders( () => [{provide: RouterUrlSerializer, useClass: DefaultRouterUrlSerializer}, RouterOutletMap, {provide: Location, useClass: SpyLocation}, { provide: RouteSegment, useFactory: (r: any /** TODO #9100 */) => r.routeTree.root, deps: [Router] }, { provide: Router, useFactory: (resolver: any /** TODO #9100 */, urlParser: any /** TODO #9100 */, outletMap: any /** TODO #9100 */, location: any /** TODO #9100 */) => new Router( 'RootComponent', RootCmp, resolver, urlParser, outletMap, location), deps: [ComponentResolver, RouterUrlSerializer, RouterOutletMap, Location] }]); it('should update location when navigating', fakeAsync(inject( [Router, TestComponentBuilder, Location], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */, location: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/user/victor'); advance(fixture); expect(location.path()).toEqual('/team/22/user/victor'); router.navigateByUrl('/team/33/simple'); advance(fixture); expect(location.path()).toEqual('/team/33/simple'); }))); it('should navigate back and forward', fakeAsync(inject( [Router, TestComponentBuilder, Location], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */, location: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/33/simple'); advance(fixture); router.navigateByUrl('/team/22/user/victor'); advance(fixture); location.back(); advance(fixture); expect(location.path()).toEqual('/team/33/simple'); location.forward(); advance(fixture); expect(location.path()).toEqual('/team/22/user/victor'); }))); it('should navigate when locations changes', fakeAsync(inject( [Router, TestComponentBuilder, Location], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */, location: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/user/victor'); advance(fixture); location.simulateHashChange('/team/22/user/fedor'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { hello fedor, aux: }'); }))); it('should support nested routes', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/user/victor'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { hello victor, aux: }'); }))); it('should support aux routes', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/user/victor(/simple)'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { hello victor, aux: simple }'); }))); it('should deactivate outlets', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/user/victor(/simple)'); advance(fixture); router.navigateByUrl('/team/22/user/victor'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { hello victor, aux: }'); }))); it('should deactivate nested outlets', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/user/victor(/simple)'); advance(fixture); router.navigateByUrl('/'); advance(fixture); expect(fixture.debugElement.nativeElement).toHaveText(''); }))); it('should update nested routes when url changes', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/user/victor'); advance(fixture); let team1 = fixture.debugElement.children[1].componentInstance; router.navigateByUrl('/team/22/user/fedor'); advance(fixture); let team2 = fixture.debugElement.children[1].componentInstance; expect(team1).toBe(team2); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { hello fedor, aux: }'); }))); it('should not deactivate the route if can deactivate returns false', fakeAsync(inject( [Router, TestComponentBuilder, Location], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */, location: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/22/cannotDeactivate'); advance(fixture); router.navigateByUrl('/team/22/user/fedor'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { cannotDeactivate, aux: }'); expect(location.path()).toEqual('/team/22/cannotDeactivate'); }))); if (getDOM().supportsDOMEvents()) { it('should support absolute router links', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); advance(fixture); router.navigateByUrl('/team/22/link'); advance(fixture); expect(fixture.debugElement.nativeElement).toHaveText('team 22 { link, aux: }'); let native = getDOM().querySelector(fixture.debugElement.nativeElement, 'a'); expect(getDOM().getAttribute(native, 'href')).toEqual('/team/33/simple'); getDOM().dispatchEvent(native, getDOM().createMouseEvent('click')); advance(fixture); expect(fixture.debugElement.nativeElement).toHaveText('team 33 { simple, aux: }'); }))); it('should support relative router links', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); advance(fixture); router.navigateByUrl('/team/22/relativelink'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { relativelink { }, aux: }'); let native = getDOM().querySelector(fixture.debugElement.nativeElement, 'a'); expect(getDOM().getAttribute(native, 'href')) .toEqual('/team/22/relativelink/simple'); getDOM().dispatchEvent(native, getDOM().createMouseEvent('click')); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { relativelink { simple }, aux: }'); }))); it('should set the router-link-active class', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); advance(fixture); router.navigateByUrl('/team/22/relativelink'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { relativelink { }, aux: }'); let link = getDOM().querySelector(fixture.debugElement.nativeElement, 'a'); expect(getDOM().hasClass(link, 'router-link-active')).toEqual(false); getDOM().dispatchEvent(link, getDOM().createMouseEvent('click')); advance(fixture); expect(getDOM().hasClass(link, 'router-link-active')).toEqual(true); }))); it('should update router links when router changes', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); advance(fixture); router.navigateByUrl('/team/22/link(simple)'); advance(fixture); expect(fixture.debugElement.nativeElement) .toHaveText('team 22 { link, aux: simple }'); let native = getDOM().querySelector(fixture.debugElement.nativeElement, 'a'); expect(getDOM().getAttribute(native, 'href')).toEqual('/team/33/simple(aux:simple)'); router.navigateByUrl('/team/22/link(simple2)'); advance(fixture); expect(getDOM().getAttribute(native, 'href')) .toEqual('/team/33/simple(aux:simple2)'); }))); it('should support top-level link', fakeAsync(inject( [Router, TestComponentBuilder], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(LinkCmp); advance(fixture); expect(fixture.debugElement.nativeElement).toHaveText('link'); }))); it('should replace state when path is equal to current path', fakeAsync(inject( [Router, TestComponentBuilder, Location], (router: any /** TODO #9100 */, tcb: any /** TODO #9100 */, location: any /** TODO #9100 */) => { let fixture = tcb.createFakeAsync(RootCmp); router.navigateByUrl('/team/33/simple'); advance(fixture); router.navigateByUrl('/team/22/user/victor'); advance(fixture); router.navigateByUrl('/team/22/user/victor'); advance(fixture); location.back(); advance(fixture); expect(location.path()).toEqual('/team/33/simple'); }))); } });
describe('no jit', () => { beforeEachProviders( () => [{provide: CompilerConfig, useValue: new CompilerConfig(true, false, false)}]); declareTests(false); });