Revert "refactor(router): improve recognition and generation pipeline"
This reverts commit cf7292fcb1.
This commit triggered an existing race condition in Google code. More work is needed on the Router to fix this condition before this refactor can land.
This commit is contained in:
@@ -1,216 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
describe,
|
||||
it,
|
||||
iit,
|
||||
ddescribe,
|
||||
expect,
|
||||
inject,
|
||||
beforeEach,
|
||||
SpyObject
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {Map, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {RouteMatch, PathMatch, RedirectMatch} from 'angular2/src/router/route_recognizer';
|
||||
import {ComponentRecognizer} from 'angular2/src/router/component_recognizer';
|
||||
|
||||
import {Route, Redirect} from 'angular2/src/router/route_config_decorator';
|
||||
import {parser} from 'angular2/src/router/url_parser';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/promise';
|
||||
|
||||
|
||||
export function main() {
|
||||
describe('ComponentRecognizer', () => {
|
||||
var recognizer: ComponentRecognizer;
|
||||
|
||||
beforeEach(() => { recognizer = new ComponentRecognizer(); });
|
||||
|
||||
|
||||
it('should recognize a static segment', inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(new Route({path: '/test', component: DummyCmpA}));
|
||||
recognize(recognizer, '/test')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
expect(getComponentType(solutions[0])).toEqual(DummyCmpA);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should recognize a single slash', inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(new Route({path: '/', component: DummyCmpA}));
|
||||
recognize(recognizer, '/')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
expect(getComponentType(solutions[0])).toEqual(DummyCmpA);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should recognize a dynamic segment', inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(new Route({path: '/user/:name', component: DummyCmpA}));
|
||||
recognize(recognizer, '/user/brian')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
expect(getComponentType(solutions[0])).toEqual(DummyCmpA);
|
||||
expect(getParams(solutions[0])).toEqual({'name': 'brian'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should recognize a star segment', inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(new Route({path: '/first/*rest', component: DummyCmpA}));
|
||||
recognize(recognizer, '/first/second/third')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
expect(getComponentType(solutions[0])).toEqual(DummyCmpA);
|
||||
expect(getParams(solutions[0])).toEqual({'rest': 'second/third'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should throw when given two routes that start with the same static segment', () => {
|
||||
recognizer.config(new Route({path: '/hello', component: DummyCmpA}));
|
||||
expect(() => recognizer.config(new Route({path: '/hello', component: DummyCmpB})))
|
||||
.toThrowError('Configuration \'/hello\' conflicts with existing route \'/hello\'');
|
||||
});
|
||||
|
||||
|
||||
it('should throw when given two routes that have dynamic segments in the same order', () => {
|
||||
recognizer.config(new Route({path: '/hello/:person/how/:doyoudou', component: DummyCmpA}));
|
||||
expect(() => recognizer.config(
|
||||
new Route({path: '/hello/:friend/how/:areyou', component: DummyCmpA})))
|
||||
.toThrowError(
|
||||
'Configuration \'/hello/:friend/how/:areyou\' conflicts with existing route \'/hello/:person/how/:doyoudou\'');
|
||||
|
||||
expect(() => recognizer.config(
|
||||
new Redirect({path: '/hello/:pal/how/:goesit', redirectTo: ['/Foo']})))
|
||||
.toThrowError(
|
||||
'Configuration \'/hello/:pal/how/:goesit\' conflicts with existing route \'/hello/:person/how/:doyoudou\'');
|
||||
});
|
||||
|
||||
|
||||
it('should recognize redirects', inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(new Route({path: '/b', component: DummyCmpA}));
|
||||
recognizer.config(new Redirect({path: '/a', redirectTo: ['B']}));
|
||||
recognize(recognizer, '/a')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
var solution = solutions[0];
|
||||
expect(solution).toBeAnInstanceOf(RedirectMatch);
|
||||
if (solution instanceof RedirectMatch) {
|
||||
expect(solution.redirectTo).toEqual(['B']);
|
||||
}
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should generate URLs with params', () => {
|
||||
recognizer.config(new Route({path: '/app/user/:name', component: DummyCmpA, name: 'User'}));
|
||||
var instruction = recognizer.generate('User', {'name': 'misko'});
|
||||
expect(instruction.urlPath).toEqual('app/user/misko');
|
||||
});
|
||||
|
||||
|
||||
it('should generate URLs with numeric params', () => {
|
||||
recognizer.config(new Route({path: '/app/page/:number', component: DummyCmpA, name: 'Page'}));
|
||||
expect(recognizer.generate('Page', {'number': 42}).urlPath).toEqual('app/page/42');
|
||||
});
|
||||
|
||||
|
||||
it('should throw in the absence of required params URLs', () => {
|
||||
recognizer.config(new Route({path: 'app/user/:name', component: DummyCmpA, name: 'User'}));
|
||||
expect(() => recognizer.generate('User', {}))
|
||||
.toThrowError('Route generator for \'name\' was not included in parameters passed.');
|
||||
});
|
||||
|
||||
|
||||
it('should throw if the route alias is not TitleCase', () => {
|
||||
expect(() => recognizer.config(
|
||||
new Route({path: 'app/user/:name', component: DummyCmpA, name: 'user'})))
|
||||
.toThrowError(
|
||||
`Route "app/user/:name" with name "user" does not begin with an uppercase letter. Route names should be CamelCase like "User".`);
|
||||
});
|
||||
|
||||
|
||||
describe('params', () => {
|
||||
it('should recognize parameters within the URL path',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(
|
||||
new Route({path: 'profile/:name', component: DummyCmpA, name: 'User'}));
|
||||
recognize(recognizer, '/profile/matsko?comments=all')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
expect(getParams(solutions[0])).toEqual({'name': 'matsko', 'comments': 'all'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should generate and populate the given static-based route with querystring params',
|
||||
() => {
|
||||
recognizer.config(
|
||||
new Route({path: 'forum/featured', component: DummyCmpA, name: 'ForumPage'}));
|
||||
|
||||
var params = {'start': 10, 'end': 100};
|
||||
|
||||
var result = recognizer.generate('ForumPage', params);
|
||||
expect(result.urlPath).toEqual('forum/featured');
|
||||
expect(result.urlParams).toEqual(['start=10', 'end=100']);
|
||||
});
|
||||
|
||||
|
||||
it('should prefer positional params over query params',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(
|
||||
new Route({path: 'profile/:name', component: DummyCmpA, name: 'User'}));
|
||||
recognize(recognizer, '/profile/yegor?name=igor')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
expect(getParams(solutions[0])).toEqual({'name': 'yegor'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should ignore matrix params for the top-level component',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
recognizer.config(
|
||||
new Route({path: '/home/:subject', component: DummyCmpA, name: 'User'}));
|
||||
recognize(recognizer, '/home;sort=asc/zero;one=1?two=2')
|
||||
.then((solutions: RouteMatch[]) => {
|
||||
expect(solutions.length).toBe(1);
|
||||
expect(getParams(solutions[0])).toEqual({'subject': 'zero', 'two': '2'});
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function recognize(recognizer: ComponentRecognizer, url: string): Promise<RouteMatch[]> {
|
||||
var parsedUrl = parser.parse(url);
|
||||
return PromiseWrapper.all(recognizer.recognize(parsedUrl));
|
||||
}
|
||||
|
||||
function getComponentType(routeMatch: RouteMatch): any {
|
||||
if (routeMatch instanceof PathMatch) {
|
||||
return routeMatch.instruction.componentType;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function getParams(routeMatch: RouteMatch): any {
|
||||
if (routeMatch instanceof PathMatch) {
|
||||
return routeMatch.instruction.params;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
class DummyCmpA {}
|
||||
class DummyCmpB {}
|
||||
@@ -1,9 +0,0 @@
|
||||
# Router integration tests
|
||||
|
||||
These tests only mock out `Location`, and otherwise use all the real parts of routing to ensure that
|
||||
various routing scenarios work as expected.
|
||||
|
||||
The Component Router in Angular 2 exposes only a handful of different options, but because they can
|
||||
be combined and nested in so many ways, it's difficult to rigorously test all the cases.
|
||||
|
||||
The address this problem, we introduce `describeRouter`, `describeWith`, and `describeWithout`.
|
||||
@@ -1,28 +0,0 @@
|
||||
import {
|
||||
describeRouter,
|
||||
ddescribeRouter,
|
||||
describeWith,
|
||||
describeWithout,
|
||||
describeWithAndWithout,
|
||||
itShouldRoute
|
||||
} from './util';
|
||||
|
||||
import {registerSpecs} from './impl/async_route_spec_impl';
|
||||
|
||||
export function main() {
|
||||
registerSpecs();
|
||||
|
||||
ddescribeRouter('async routes', () => {
|
||||
describeWithout('children', () => {
|
||||
describeWith('route data', itShouldRoute);
|
||||
describeWithAndWithout('params', itShouldRoute);
|
||||
});
|
||||
|
||||
describeWith('sync children',
|
||||
() => { describeWithAndWithout('default routes', itShouldRoute); });
|
||||
|
||||
describeWith('async children', () => {
|
||||
describeWithAndWithout('params', () => { describeWithout('default routes', itShouldRoute); });
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import {
|
||||
RootTestComponent,
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {provide, Component, Injector, Inject} from 'angular2/core';
|
||||
|
||||
import {Router, ROUTER_DIRECTIVES, RouteParams, RouteData, Location} from 'angular2/router';
|
||||
import {RouteConfig, Route, AuxRoute, Redirect} from 'angular2/src/router/route_config_decorator';
|
||||
|
||||
import {TEST_ROUTER_PROVIDERS, RootCmp, compile} from './util';
|
||||
|
||||
var cmpInstanceCount;
|
||||
var childCmpInstanceCount;
|
||||
|
||||
export function main() {
|
||||
describe('auxiliary routes', () => {
|
||||
|
||||
var tcb: TestComponentBuilder;
|
||||
var rootTC: RootTestComponent;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
childCmpInstanceCount = 0;
|
||||
cmpInstanceCount = 0;
|
||||
}));
|
||||
|
||||
it('should recognize and navigate from the URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `main {<router-outlet></router-outlet>} | aux {<router-outlet name="modal"></router-outlet>}`)
|
||||
.then((rtc) => {rootTC = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
||||
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Aux'})
|
||||
]))
|
||||
.then((_) => rtr.navigateByUrl('/hello(modal)'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.debugElement.nativeElement).toHaveText('main {hello} | aux {modal}');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate via the link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `main {<router-outlet></router-outlet>} | aux {<router-outlet name="modal"></router-outlet>}`)
|
||||
.then((rtc) => {rootTC = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
||||
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Modal'})
|
||||
]))
|
||||
.then((_) => rtr.navigate(['/Hello', ['Modal']]))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.debugElement.nativeElement).toHaveText('main {hello} | aux {modal}');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'hello-cmp', template: `{{greeting}}`})
|
||||
class HelloCmp {
|
||||
greeting: string;
|
||||
constructor() { this.greeting = 'hello'; }
|
||||
}
|
||||
|
||||
@Component({selector: 'modal-cmp', template: `modal`})
|
||||
class ModalCmp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'aux-cmp',
|
||||
template: 'main {<router-outlet></router-outlet>} | ' +
|
||||
'aux {<router-outlet name="modal"></router-outlet>}',
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
})
|
||||
@RouteConfig([
|
||||
new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
|
||||
new AuxRoute({path: '/modal', component: ModalCmp, name: 'Aux'})
|
||||
])
|
||||
class AuxCmp {
|
||||
}
|
||||
@@ -1,655 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
expect,
|
||||
iit,
|
||||
flushMicrotasks,
|
||||
inject,
|
||||
it,
|
||||
TestComponentBuilder,
|
||||
RootTestComponent,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {specs, compile, TEST_ROUTER_PROVIDERS, clickOnElement, getHref} from '../util';
|
||||
|
||||
import {Router, AsyncRoute, Route, Location} from 'angular2/router';
|
||||
|
||||
import {
|
||||
HelloCmp,
|
||||
helloCmpLoader,
|
||||
UserCmp,
|
||||
userCmpLoader,
|
||||
TeamCmp,
|
||||
asyncTeamLoader,
|
||||
ParentCmp,
|
||||
parentCmpLoader,
|
||||
asyncParentCmpLoader,
|
||||
asyncDefaultParentCmpLoader,
|
||||
ParentWithDefaultCmp,
|
||||
parentWithDefaultCmpLoader,
|
||||
asyncRouteDataCmp
|
||||
} from './fixture_components';
|
||||
|
||||
function getLinkElement(rtc: RootTestComponent) {
|
||||
return rtc.debugElement.componentViewChildren[0].nativeElement;
|
||||
}
|
||||
|
||||
function asyncRoutesWithoutChildrenWithRouteData() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should inject route data into the component', 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 AsyncRoute({path: '/route-data-default', loader: asyncRouteDataCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/route-data-default'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
function asyncRoutesWithoutChildrenWithoutParams() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
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) => {
|
||||
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) => {
|
||||
compile(tcb, `<a [router-link]="['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, location) => {
|
||||
compile(tcb, `<a [router-link]="['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((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('go to hello | hello');
|
||||
expect(location.urlChanges).toEqual(['/test']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function asyncRoutesWithoutChildrenWithParams() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/user/:name', loader: userCmpLoader, name: 'User'})]))
|
||||
.then((_) => rtr.navigateByUrl('/user/igor'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello igor');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/user/:name', component: UserCmp, name: 'User'})]))
|
||||
.then((_) => rtr.navigate(['/User', {name: 'brian'}]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello brian');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `<a [router-link]="['User', {name: 'naomi'}]">greet naomi</a> | <router-outlet></router-outlet>`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/user/:name', loader: userCmpLoader, name: 'User'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/user/naomi');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['User', {name: 'naomi'}]">greet naomi</a> | <router-outlet></router-outlet>`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/user/:name', loader: userCmpLoader, name: 'User'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('greet naomi | ');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('greet naomi | hello naomi');
|
||||
expect(location.urlChanges).toEqual(['/user/naomi']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate between components with different parameters',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/user/:name', loader: userCmpLoader, name: 'User'})]))
|
||||
.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();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function asyncRoutesWithSyncChildrenWithoutDefaultRoutes() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/a/...', loader: parentCmpLoader, name: 'Parent'})]))
|
||||
.then((_) => rtr.navigateByUrl('/a/b'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/a/...', loader: parentCmpLoader, name: 'Parent'})]))
|
||||
.then((_) => rtr.navigate(['/Parent', 'Child']))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `<a [router-link]="['Parent']">nav to child</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/a/...', loader: parentCmpLoader, name: 'Parent'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/a');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['Parent', 'Child']">nav to child</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new AsyncRoute({path: '/a/...', loader: parentCmpLoader, name: 'Parent'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('nav to child | outer { }');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('nav to child | outer { inner { hello } }');
|
||||
expect(location.urlChanges).toEqual(['/a/b']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function asyncRoutesWithSyncChildrenWithDefaultRoutes() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: parentWithDefaultCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => rtr.navigateByUrl('/a'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: parentWithDefaultCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => rtr.navigate(['/Parent']))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `<a [router-link]="['/Parent']">link to inner</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: parentWithDefaultCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/a');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['/Parent']">link to inner</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: parentWithDefaultCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('link to inner | outer { }');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('link to inner | outer { inner { hello } }');
|
||||
expect(location.urlChanges).toEqual(['/a/b']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function asyncRoutesWithAsyncChildrenWithoutParamsWithoutDefaultRoutes() {
|
||||
var rootTC;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {rootTC = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: asyncParentCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => rtr.navigateByUrl('/a/b'))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {rootTC = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: asyncParentCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => rtr.navigate(['/Parent', 'Child']))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `<a [router-link]="['Parent', 'Child']">nav to child</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {rootTC = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: asyncParentCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(getHref(getLinkElement(rootTC))).toEqual('/a');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['Parent', 'Child']">nav to child</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {rootTC = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/a/...', loader: asyncParentCmpLoader, name: 'Parent'})
|
||||
]))
|
||||
.then((_) => {
|
||||
rootTC.detectChanges();
|
||||
expect(rootTC.debugElement.nativeElement).toHaveText('nav to child | outer { }');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
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 asyncRoutesWithAsyncChildrenWithoutParamsWithDefaultRoutes() {
|
||||
var rootTC;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
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) => {
|
||||
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) => {
|
||||
compile(tcb, `<a [router-link]="['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, location) => {
|
||||
compile(tcb, `<a [router-link]="['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((_) => {
|
||||
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 asyncRoutesWithAsyncChildrenWithParamsWithoutDefaultRoutes() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `{ <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/team/:id/...', loader: asyncTeamLoader, name: 'Team'})
|
||||
]))
|
||||
.then((_) => rtr.navigateByUrl('/team/angular/user/matias'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('{ team angular | user { hello matias } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `{ <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/team/:id/...', loader: asyncTeamLoader, name: 'Team'})
|
||||
]))
|
||||
.then((_) => rtr.navigate(['/Team', {id: 'angular'}, 'User', {name: 'matias'}]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('{ team angular | user { hello matias } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(
|
||||
tcb,
|
||||
`<a [router-link]="['/Team', {id: 'angular'}, 'User', {name: 'matias'}]">nav to matias</a> { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/team/:id/...', loader: asyncTeamLoader, name: 'Team'})
|
||||
]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/team/angular');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(
|
||||
tcb,
|
||||
`<a [router-link]="['/Team', {id: 'angular'}, 'User', {name: 'matias'}]">nav to matias</a> { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute({path: '/team/:id/...', loader: asyncTeamLoader, name: 'Team'})
|
||||
]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('nav to matias { }');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('nav to matias { team angular | user { hello matias } }');
|
||||
expect(location.urlChanges).toEqual(['/team/angular/user/matias']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
export function registerSpecs() {
|
||||
specs['asyncRoutesWithoutChildrenWithRouteData'] = asyncRoutesWithoutChildrenWithRouteData;
|
||||
specs['asyncRoutesWithoutChildrenWithoutParams'] = asyncRoutesWithoutChildrenWithoutParams;
|
||||
specs['asyncRoutesWithoutChildrenWithParams'] = asyncRoutesWithoutChildrenWithParams;
|
||||
specs['asyncRoutesWithSyncChildrenWithoutDefaultRoutes'] =
|
||||
asyncRoutesWithSyncChildrenWithoutDefaultRoutes;
|
||||
specs['asyncRoutesWithSyncChildrenWithDefaultRoutes'] =
|
||||
asyncRoutesWithSyncChildrenWithDefaultRoutes;
|
||||
specs['asyncRoutesWithAsyncChildrenWithoutParamsWithoutDefaultRoutes'] =
|
||||
asyncRoutesWithAsyncChildrenWithoutParamsWithoutDefaultRoutes;
|
||||
specs['asyncRoutesWithAsyncChildrenWithoutParamsWithDefaultRoutes'] =
|
||||
asyncRoutesWithAsyncChildrenWithoutParamsWithDefaultRoutes;
|
||||
specs['asyncRoutesWithAsyncChildrenWithParamsWithoutDefaultRoutes'] =
|
||||
asyncRoutesWithAsyncChildrenWithParamsWithoutDefaultRoutes;
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
import {Component} from 'angular2/angular2';
|
||||
import {
|
||||
AsyncRoute,
|
||||
Route,
|
||||
Redirect,
|
||||
RouteConfig,
|
||||
RouteParams,
|
||||
RouteData,
|
||||
ROUTER_DIRECTIVES
|
||||
} from 'angular2/router';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
@Component({selector: 'hello-cmp', template: `{{greeting}}`})
|
||||
export class HelloCmp {
|
||||
greeting: string;
|
||||
constructor() { this.greeting = 'hello'; }
|
||||
}
|
||||
|
||||
export function helloCmpLoader() {
|
||||
return PromiseWrapper.resolve(HelloCmp);
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'user-cmp', template: `hello {{user}}`})
|
||||
export class UserCmp {
|
||||
user: string;
|
||||
constructor(params: RouteParams) { this.user = params.get('name'); }
|
||||
}
|
||||
|
||||
export function userCmpLoader() {
|
||||
return PromiseWrapper.resolve(UserCmp);
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'parent-cmp',
|
||||
template: `inner { <router-outlet></router-outlet> }`,
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
})
|
||||
@RouteConfig([new Route({path: '/b', component: HelloCmp, name: 'Child'})])
|
||||
export class ParentCmp {
|
||||
}
|
||||
|
||||
export function parentCmpLoader() {
|
||||
return PromiseWrapper.resolve(ParentCmp);
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'parent-cmp',
|
||||
template: `inner { <router-outlet></router-outlet> }`,
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
})
|
||||
@RouteConfig([new AsyncRoute({path: '/b', loader: helloCmpLoader, name: 'Child'})])
|
||||
export class AsyncParentCmp {
|
||||
}
|
||||
|
||||
export function asyncParentCmpLoader() {
|
||||
return PromiseWrapper.resolve(AsyncParentCmp);
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent-cmp',
|
||||
template: `inner { <router-outlet></router-outlet> }`,
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
})
|
||||
@RouteConfig(
|
||||
[new AsyncRoute({path: '/b', loader: helloCmpLoader, name: 'Child', useAsDefault: true})])
|
||||
export class AsyncDefaultParentCmp {
|
||||
}
|
||||
|
||||
export function asyncDefaultParentCmpLoader() {
|
||||
return PromiseWrapper.resolve(AsyncDefaultParentCmp);
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'parent-cmp',
|
||||
template: `inner { <router-outlet></router-outlet> }`,
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
})
|
||||
@RouteConfig([new Route({path: '/b', component: HelloCmp, name: 'Child', useAsDefault: true})])
|
||||
export class ParentWithDefaultCmp {
|
||||
}
|
||||
|
||||
export function parentWithDefaultCmpLoader() {
|
||||
return PromiseWrapper.resolve(ParentWithDefaultCmp);
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'team-cmp',
|
||||
template: `team {{id}} | user { <router-outlet></router-outlet> }`,
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
})
|
||||
@RouteConfig([new Route({path: '/user/:name', component: UserCmp, name: 'User'})])
|
||||
export class TeamCmp {
|
||||
id: string;
|
||||
constructor(params: RouteParams) { this.id = params.get('id'); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'team-cmp',
|
||||
template: `team {{id}} | user { <router-outlet></router-outlet> }`,
|
||||
directives: [ROUTER_DIRECTIVES],
|
||||
})
|
||||
@RouteConfig([new AsyncRoute({path: '/user/:name', loader: userCmpLoader, name: 'User'})])
|
||||
export class AsyncTeamCmp {
|
||||
id: string;
|
||||
constructor(params: RouteParams) { this.id = params.get('id'); }
|
||||
}
|
||||
|
||||
export function asyncTeamLoader() {
|
||||
return PromiseWrapper.resolve(AsyncTeamCmp);
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'data-cmp', template: `{{myData}}`})
|
||||
export class RouteDataCmp {
|
||||
myData: boolean;
|
||||
constructor(data: RouteData) { this.myData = data.get('isAdmin'); }
|
||||
}
|
||||
|
||||
export function asyncRouteDataCmp() {
|
||||
return PromiseWrapper.resolve(RouteDataCmp);
|
||||
}
|
||||
|
||||
@Component({selector: 'redirect-to-parent-cmp', template: 'redirect-to-parent'})
|
||||
@RouteConfig([new Redirect({path: '/child-redirect', redirectTo: ['../HelloSib']})])
|
||||
export class RedirectToParentCmp {
|
||||
}
|
||||
@@ -1,431 +0,0 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
beforeEachProviders,
|
||||
expect,
|
||||
iit,
|
||||
flushMicrotasks,
|
||||
inject,
|
||||
it,
|
||||
TestComponentBuilder,
|
||||
RootTestComponent,
|
||||
xit,
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {specs, compile, TEST_ROUTER_PROVIDERS, clickOnElement, getHref} from '../util';
|
||||
|
||||
import {Router, Route, Location} from 'angular2/router';
|
||||
|
||||
import {HelloCmp, UserCmp, TeamCmp, ParentCmp, ParentWithDefaultCmp} from './fixture_components';
|
||||
|
||||
|
||||
function getLinkElement(rtc: RootTestComponent) {
|
||||
return rtc.debugElement.componentViewChildren[0].nativeElement;
|
||||
}
|
||||
|
||||
function syncRoutesWithoutChildrenWithoutParams() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) =>
|
||||
rtr.config([new Route({path: '/test', component: HelloCmp, 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) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) =>
|
||||
rtr.config([new Route({path: '/test', component: HelloCmp, 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) => {
|
||||
compile(tcb, `<a [router-link]="['Hello']">go to hello</a> | <router-outlet></router-outlet>`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) =>
|
||||
rtr.config([new Route({path: '/test', component: HelloCmp, name: 'Hello'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/test');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['Hello']">go to hello</a> | <router-outlet></router-outlet>`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) =>
|
||||
rtr.config([new Route({path: '/test', component: HelloCmp, name: 'Hello'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('go to hello | ');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('go to hello | hello');
|
||||
expect(location.urlChanges).toEqual(['/test']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function syncRoutesWithoutChildrenWithParams() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/user/:name', component: UserCmp, name: 'User'})]))
|
||||
.then((_) => rtr.navigateByUrl('/user/igor'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello igor');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/user/:name', component: UserCmp, name: 'User'})]))
|
||||
.then((_) => rtr.navigate(['/User', {name: 'brian'}]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello brian');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `<a [router-link]="['User', {name: 'naomi'}]">greet naomi</a> | <router-outlet></router-outlet>`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/user/:name', component: UserCmp, name: 'User'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/user/naomi');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['User', {name: 'naomi'}]">greet naomi</a> | <router-outlet></router-outlet>`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/user/:name', component: UserCmp, name: 'User'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('greet naomi | ');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('greet naomi | hello naomi');
|
||||
expect(location.urlChanges).toEqual(['/user/naomi']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
|
||||
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, name: 'User'})]))
|
||||
.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();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function syncRoutesWithSyncChildrenWithoutDefaultRoutesWithoutParams() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentCmp, name: 'Parent'})]))
|
||||
.then((_) => rtr.navigateByUrl('/a/b'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentCmp, name: 'Parent'})]))
|
||||
.then((_) => rtr.navigate(['/Parent', 'Child']))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `<a [router-link]="['Parent', 'Child']">nav to child</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentCmp, name: 'Parent'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/a/b');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['Parent', 'Child']">nav to child</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentCmp, name: 'Parent'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('nav to child | outer { }');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('nav to child | outer { inner { hello } }');
|
||||
expect(location.urlChanges).toEqual(['/a/b']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function syncRoutesWithSyncChildrenWithoutDefaultRoutesWithParams() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `{ <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/team/:id/...', component: TeamCmp, name: 'Team'})]))
|
||||
.then((_) => rtr.navigateByUrl('/team/angular/user/matias'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('{ team angular | user { hello matias } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `{ <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/team/:id/...', component: TeamCmp, name: 'Team'})]))
|
||||
.then((_) => rtr.navigate(['/Team', {id: 'angular'}, 'User', {name: 'matias'}]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('{ team angular | user { hello matias } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(
|
||||
tcb,
|
||||
`<a [router-link]="['/Team', {id: 'angular'}, 'User', {name: 'matias'}]">nav to matias</a> { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/team/:id/...', component: TeamCmp, name: 'Team'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/team/angular/user/matias');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(
|
||||
tcb,
|
||||
`<a [router-link]="['/Team', {id: 'angular'}, 'User', {name: 'matias'}]">nav to matias</a> { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/team/:id/...', component: TeamCmp, name: 'Team'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('nav to matias { }');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('nav to matias { team angular | user { hello matias } }');
|
||||
expect(location.urlChanges).toEqual(['/team/angular/user/matias']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
function syncRoutesWithSyncChildrenWithDefaultRoutesWithoutParams() {
|
||||
var fixture;
|
||||
var tcb;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
}));
|
||||
|
||||
it('should navigate by URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then(
|
||||
(_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentWithDefaultCmp, name: 'Parent'})]))
|
||||
.then((_) => rtr.navigateByUrl('/a'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate by link DSL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then(
|
||||
(_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentWithDefaultCmp, name: 'Parent'})]))
|
||||
.then((_) => rtr.navigate(['/Parent']))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('outer { inner { hello } }');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should generate a link URL', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, `<a [router-link]="['/Parent']">link to inner</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then(
|
||||
(_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentWithDefaultCmp, name: 'Parent'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(getHref(getLinkElement(fixture))).toEqual('/a');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should navigate from a link click',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile(tcb, `<a [router-link]="['/Parent']">link to inner</a> | outer { <router-outlet></router-outlet> }`)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
.then(
|
||||
(_) => rtr.config(
|
||||
[new Route({path: '/a/...', component: ParentWithDefaultCmp, name: 'Parent'})]))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('link to inner | outer { }');
|
||||
|
||||
rtr.subscribe((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('link to inner | outer { inner { hello } }');
|
||||
expect(location.urlChanges).toEqual(['/a/b']);
|
||||
async.done();
|
||||
});
|
||||
|
||||
clickOnElement(getLinkElement(fixture));
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
export function registerSpecs() {
|
||||
specs['syncRoutesWithoutChildrenWithoutParams'] = syncRoutesWithoutChildrenWithoutParams;
|
||||
specs['syncRoutesWithoutChildrenWithParams'] = syncRoutesWithoutChildrenWithParams;
|
||||
specs['syncRoutesWithSyncChildrenWithoutDefaultRoutesWithoutParams'] =
|
||||
syncRoutesWithSyncChildrenWithoutDefaultRoutesWithoutParams;
|
||||
specs['syncRoutesWithSyncChildrenWithoutDefaultRoutesWithParams'] =
|
||||
syncRoutesWithSyncChildrenWithoutDefaultRoutesWithParams;
|
||||
specs['syncRoutesWithSyncChildrenWithDefaultRoutesWithoutParams'] =
|
||||
syncRoutesWithSyncChildrenWithDefaultRoutesWithoutParams;
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
ObservableWrapper
|
||||
} from 'angular2/src/facade/async';
|
||||
|
||||
import {RootRouter} from 'angular2/src/router/router';
|
||||
import {Router, RouterOutlet, RouterLink, RouteParams} from 'angular2/router';
|
||||
import {
|
||||
RouteConfig,
|
||||
@@ -34,6 +35,9 @@ import {
|
||||
Redirect
|
||||
} from 'angular2/src/router/route_config_decorator';
|
||||
|
||||
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
||||
import {Location} from 'angular2/src/router/location';
|
||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||
import {
|
||||
OnActivate,
|
||||
OnDeactivate,
|
||||
@@ -43,9 +47,7 @@ import {
|
||||
} from 'angular2/src/router/interfaces';
|
||||
import {CanActivate} from 'angular2/src/router/lifecycle_annotations';
|
||||
import {ComponentInstruction} from 'angular2/src/router/instruction';
|
||||
|
||||
|
||||
import {TEST_ROUTER_PROVIDERS, RootCmp, compile} from './util';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
|
||||
var cmpInstanceCount;
|
||||
var log: string[];
|
||||
@@ -59,7 +61,17 @@ export function main() {
|
||||
var fixture: ComponentFixture;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
beforeEachBindings(() => [
|
||||
RouteRegistry,
|
||||
DirectiveResolver,
|
||||
provide(Location, {useClass: SpyLocation}),
|
||||
provide(Router,
|
||||
{
|
||||
useFactory:
|
||||
(registry, location) => { return new RootRouter(registry, location, MyComp); },
|
||||
deps: [RouteRegistry, Location]
|
||||
})
|
||||
]);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
@@ -69,9 +81,17 @@ export function main() {
|
||||
eventBus = new EventEmitter();
|
||||
}));
|
||||
|
||||
function compile(template: string = "<router-outlet></router-outlet>") {
|
||||
return tcb.overrideView(MyComp, new View({
|
||||
template: ('<div>' + template + '</div>'),
|
||||
directives: [RouterOutlet, RouterLink]
|
||||
}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => { fixture = tc; });
|
||||
}
|
||||
|
||||
it('should call the onActivate hook', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/on-activate'))
|
||||
.then((_) => {
|
||||
@@ -84,8 +104,7 @@ export function main() {
|
||||
|
||||
it('should wait for a parent component\'s onActivate hook to resolve before calling its child\'s',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => {
|
||||
ObservableWrapper.subscribe<string>(eventBus, (ev) => {
|
||||
@@ -107,8 +126,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should call the onDeactivate hook', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/on-deactivate'))
|
||||
.then((_) => rtr.navigateByUrl('/a'))
|
||||
@@ -122,8 +140,7 @@ export function main() {
|
||||
|
||||
it('should wait for a child component\'s onDeactivate hook to resolve before calling its parent\'s',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/parent-deactivate/child-deactivate'))
|
||||
.then((_) => {
|
||||
@@ -148,8 +165,7 @@ export function main() {
|
||||
|
||||
it('should reuse a component when the canReuse hook returns true',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/on-reuse/1/a'))
|
||||
.then((_) => {
|
||||
@@ -171,8 +187,7 @@ export function main() {
|
||||
|
||||
it('should not reuse a component when the canReuse hook returns false',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/never-reuse/1/a'))
|
||||
.then((_) => {
|
||||
@@ -193,8 +208,7 @@ export function main() {
|
||||
|
||||
|
||||
it('should navigate when canActivate returns true', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => {
|
||||
ObservableWrapper.subscribe<string>(eventBus, (ev) => {
|
||||
@@ -214,8 +228,7 @@ export function main() {
|
||||
|
||||
it('should not navigate when canActivate returns false',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => {
|
||||
ObservableWrapper.subscribe<string>(eventBus, (ev) => {
|
||||
@@ -235,8 +248,7 @@ export function main() {
|
||||
|
||||
it('should navigate away when canDeactivate returns true',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/can-deactivate/a'))
|
||||
.then((_) => {
|
||||
@@ -261,8 +273,7 @@ export function main() {
|
||||
|
||||
it('should not navigate away when canDeactivate returns false',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/can-deactivate/a'))
|
||||
.then((_) => {
|
||||
@@ -288,8 +299,7 @@ export function main() {
|
||||
|
||||
it('should run activation and deactivation hooks in the correct order',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/activation-hooks/child'))
|
||||
.then((_) => {
|
||||
@@ -315,8 +325,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should only run reuse hooks when reusing', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/reuse-hooks/1'))
|
||||
.then((_) => {
|
||||
@@ -343,7 +352,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should not run reuse hooks when not reusing', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: LifecycleCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/reuse-hooks/1'))
|
||||
.then((_) => {
|
||||
@@ -374,16 +383,23 @@ export function main() {
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'a-cmp', template: "A"})
|
||||
@Component({selector: 'a-cmp'})
|
||||
@View({template: "A"})
|
||||
class A {
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'b-cmp', template: "B"})
|
||||
@Component({selector: 'b-cmp'})
|
||||
@View({template: "B"})
|
||||
class B {
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'my-comp'})
|
||||
class MyComp {
|
||||
name;
|
||||
}
|
||||
|
||||
function logHook(name: string, next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
var message = name + ': ' + (isPresent(prev) ? ('/' + prev.urlPath) : 'null') + ' -> ' +
|
||||
(isPresent(next) ? ('/' + next.urlPath) : 'null');
|
||||
@@ -391,18 +407,16 @@ function logHook(name: string, next: ComponentInstruction, prev: ComponentInstru
|
||||
ObservableWrapper.callEmit(eventBus, message);
|
||||
}
|
||||
|
||||
@Component({selector: 'activate-cmp', template: 'activate cmp'})
|
||||
@Component({selector: 'activate-cmp'})
|
||||
@View({template: 'activate cmp'})
|
||||
class ActivateCmp implements OnActivate {
|
||||
onActivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
logHook('activate', next, prev);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent-activate-cmp',
|
||||
template: `parent {<router-outlet></router-outlet>}`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'parent-activate-cmp'})
|
||||
@View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([new Route({path: '/child-activate', component: ActivateCmp})])
|
||||
class ParentActivateCmp implements OnActivate {
|
||||
onActivate(next: ComponentInstruction, prev: ComponentInstruction): Promise<any> {
|
||||
@@ -412,14 +426,16 @@ class ParentActivateCmp implements OnActivate {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'deactivate-cmp', template: 'deactivate cmp'})
|
||||
@Component({selector: 'deactivate-cmp'})
|
||||
@View({template: 'deactivate cmp'})
|
||||
class DeactivateCmp implements OnDeactivate {
|
||||
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
logHook('deactivate', next, prev);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'deactivate-cmp', template: 'deactivate cmp'})
|
||||
@Component({selector: 'deactivate-cmp'})
|
||||
@View({template: 'deactivate cmp'})
|
||||
class WaitDeactivateCmp implements OnDeactivate {
|
||||
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction): Promise<any> {
|
||||
completer = PromiseWrapper.completer();
|
||||
@@ -428,11 +444,8 @@ class WaitDeactivateCmp implements OnDeactivate {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent-deactivate-cmp',
|
||||
template: `parent {<router-outlet></router-outlet>}`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'parent-deactivate-cmp'})
|
||||
@View({template: `parent {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([new Route({path: '/child-deactivate', component: WaitDeactivateCmp})])
|
||||
class ParentDeactivateCmp implements OnDeactivate {
|
||||
onDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
@@ -440,37 +453,26 @@ class ParentDeactivateCmp implements OnDeactivate {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'reuse-cmp',
|
||||
template: `reuse {<router-outlet></router-outlet>}`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'reuse-cmp'})
|
||||
@View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
|
||||
class ReuseCmp implements OnReuse,
|
||||
CanReuse {
|
||||
class ReuseCmp implements OnReuse, CanReuse {
|
||||
constructor() { cmpInstanceCount += 1; }
|
||||
canReuse(next: ComponentInstruction, prev: ComponentInstruction) { return true; }
|
||||
onReuse(next: ComponentInstruction, prev: ComponentInstruction) { logHook('reuse', next, prev); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'never-reuse-cmp',
|
||||
template: `reuse {<router-outlet></router-outlet>}`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'never-reuse-cmp'})
|
||||
@View({template: `reuse {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
|
||||
class NeverReuseCmp implements OnReuse,
|
||||
CanReuse {
|
||||
class NeverReuseCmp implements OnReuse, CanReuse {
|
||||
constructor() { cmpInstanceCount += 1; }
|
||||
canReuse(next: ComponentInstruction, prev: ComponentInstruction) { return false; }
|
||||
onReuse(next: ComponentInstruction, prev: ComponentInstruction) { logHook('reuse', next, prev); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'can-activate-cmp',
|
||||
template: `canActivate {<router-outlet></router-outlet>}`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'can-activate-cmp'})
|
||||
@View({template: `canActivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
|
||||
@CanActivate(CanActivateCmp.canActivate)
|
||||
class CanActivateCmp {
|
||||
@@ -481,11 +483,8 @@ class CanActivateCmp {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'can-deactivate-cmp',
|
||||
template: `canDeactivate {<router-outlet></router-outlet>}`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'can-deactivate-cmp'})
|
||||
@View({template: `canDeactivate {<router-outlet></router-outlet>}`, directives: [RouterOutlet]})
|
||||
@RouteConfig([new Route({path: '/a', component: A}), new Route({path: '/b', component: B})])
|
||||
class CanDeactivateCmp implements CanDeactivate {
|
||||
canDeactivate(next: ComponentInstruction, prev: ComponentInstruction): Promise<boolean> {
|
||||
@@ -495,7 +494,8 @@ class CanDeactivateCmp implements CanDeactivate {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'all-hooks-child-cmp', template: `child`})
|
||||
@Component({selector: 'all-hooks-child-cmp'})
|
||||
@View({template: `child`})
|
||||
@CanActivate(AllHooksChildCmp.canActivate)
|
||||
class AllHooksChildCmp implements CanDeactivate, OnDeactivate, OnActivate {
|
||||
canDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
@@ -517,15 +517,11 @@ class AllHooksChildCmp implements CanDeactivate, OnDeactivate, OnActivate {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'all-hooks-parent-cmp',
|
||||
template: `<router-outlet></router-outlet>`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'all-hooks-parent-cmp'})
|
||||
@View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]})
|
||||
@RouteConfig([new Route({path: '/child', component: AllHooksChildCmp})])
|
||||
@CanActivate(AllHooksParentCmp.canActivate)
|
||||
class AllHooksParentCmp implements CanDeactivate,
|
||||
OnDeactivate, OnActivate {
|
||||
class AllHooksParentCmp implements CanDeactivate, OnDeactivate, OnActivate {
|
||||
canDeactivate(next: ComponentInstruction, prev: ComponentInstruction) {
|
||||
logHook('canDeactivate parent', next, prev);
|
||||
return true;
|
||||
@@ -545,7 +541,8 @@ class AllHooksParentCmp implements CanDeactivate,
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'reuse-hooks-cmp', template: 'reuse hooks cmp'})
|
||||
@Component({selector: 'reuse-hooks-cmp'})
|
||||
@View({template: 'reuse hooks cmp'})
|
||||
@CanActivate(ReuseHooksCmp.canActivate)
|
||||
class ReuseHooksCmp implements OnActivate, OnReuse, OnDeactivate, CanReuse, CanDeactivate {
|
||||
canReuse(next: ComponentInstruction, prev: ComponentInstruction): Promise<any> {
|
||||
@@ -577,11 +574,8 @@ class ReuseHooksCmp implements OnActivate, OnReuse, OnDeactivate, CanReuse, CanD
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'lifecycle-cmp',
|
||||
template: `<router-outlet></router-outlet>`,
|
||||
directives: [RouterOutlet]
|
||||
})
|
||||
@Component({selector: 'lifecycle-cmp'})
|
||||
@View({template: `<router-outlet></router-outlet>`, directives: [RouterOutlet]})
|
||||
@RouteConfig([
|
||||
new Route({path: '/a', component: A}),
|
||||
new Route({path: '/on-activate', component: ActivateCmp}),
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
@@ -18,7 +18,8 @@ import {
|
||||
import {provide, Component, View, Injector, Inject} from 'angular2/core';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
import {Router, RouterOutlet, RouterLink, RouteParams, RouteData, Location} from 'angular2/router';
|
||||
import {RootRouter} from 'angular2/src/router/router';
|
||||
import {Router, RouterOutlet, RouterLink, RouteParams, RouteData} from 'angular2/router';
|
||||
import {
|
||||
RouteConfig,
|
||||
Route,
|
||||
@@ -27,10 +28,14 @@ import {
|
||||
Redirect
|
||||
} from 'angular2/src/router/route_config_decorator';
|
||||
|
||||
import {TEST_ROUTER_PROVIDERS, RootCmp, compile} from './util';
|
||||
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
||||
import {Location} from 'angular2/src/router/location';
|
||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
|
||||
var cmpInstanceCount;
|
||||
var childCmpInstanceCount;
|
||||
var log: string[];
|
||||
|
||||
export function main() {
|
||||
describe('navigation', () => {
|
||||
@@ -39,18 +44,37 @@ export function main() {
|
||||
var fixture: ComponentFixture;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
beforeEachBindings(() => [
|
||||
RouteRegistry,
|
||||
DirectiveResolver,
|
||||
provide(Location, {useClass: SpyLocation}),
|
||||
provide(Router,
|
||||
{
|
||||
useFactory:
|
||||
(registry, location) => { return new RootRouter(registry, location, MyComp); },
|
||||
deps: [RouteRegistry, Location]
|
||||
})
|
||||
]);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
childCmpInstanceCount = 0;
|
||||
cmpInstanceCount = 0;
|
||||
log = [];
|
||||
}));
|
||||
|
||||
function compile(template: string = "<router-outlet></router-outlet>") {
|
||||
return tcb.overrideView(MyComp, new View({
|
||||
template: ('<div>' + template + '</div>'),
|
||||
directives: [RouterOutlet, RouterLink]
|
||||
}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => { fixture = tc; });
|
||||
}
|
||||
|
||||
it('should work in a simple case', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/test', component: HelloCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/test'))
|
||||
.then((_) => {
|
||||
@@ -63,8 +87,7 @@ export function main() {
|
||||
|
||||
it('should navigate between components with different parameters',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/user/:name', component: UserCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/user/brian'))
|
||||
.then((_) => {
|
||||
@@ -79,9 +102,9 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should navigate to child routes', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, 'outer { <router-outlet></router-outlet> }')
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile('outer { <router-outlet></router-outlet> }')
|
||||
.then((_) => rtr.config([new Route({path: '/a/...', component: ParentCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/a/b'))
|
||||
.then((_) => {
|
||||
@@ -93,9 +116,7 @@ export function main() {
|
||||
|
||||
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})
|
||||
compile('outer { <router-outlet></router-outlet> }')
|
||||
.then((_) => rtr.config([new Route({path: '/a/...', component: ParentCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/a'))
|
||||
.then((_) => {
|
||||
@@ -105,9 +126,9 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should navigate to child routes of async routes', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb, 'outer { <router-outlet></router-outlet> }')
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile('outer { <router-outlet></router-outlet> }')
|
||||
.then((_) => rtr.config([new AsyncRoute({path: '/a/...', loader: parentLoader})]))
|
||||
.then((_) => rtr.navigateByUrl('/a/b'))
|
||||
.then((_) => {
|
||||
@@ -117,9 +138,26 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should recognize and apply redirects',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
compile()
|
||||
.then((_) => rtr.config([
|
||||
new Redirect({path: '/original', redirectTo: '/redirected'}),
|
||||
new Route({path: '/redirected', component: HelloCmp})
|
||||
]))
|
||||
.then((_) => rtr.navigateByUrl('/original'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement).toHaveText('hello');
|
||||
expect(location.urlChanges).toEqual(['/redirected']);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
|
||||
it('should reuse common parent components', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/team/:id/...', component: TeamCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/team/angular/user/rado'))
|
||||
.then((_) => {
|
||||
@@ -139,8 +177,7 @@ export function main() {
|
||||
|
||||
it('should not reuse children when parent components change',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/team/:id/...', component: TeamCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/team/angular/user/rado'))
|
||||
.then((_) => {
|
||||
@@ -160,8 +197,7 @@ export function main() {
|
||||
}));
|
||||
|
||||
it('should inject route data into component', inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([
|
||||
new Route({path: '/route-data', component: RouteDataCmp, data: {isAdmin: true}})
|
||||
]))
|
||||
@@ -175,11 +211,10 @@ export function main() {
|
||||
|
||||
it('should inject route data into component with AsyncRoute',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config([
|
||||
new AsyncRoute(
|
||||
{path: '/route-data', loader: asyncRouteDataCmp, data: {isAdmin: true}})
|
||||
{path: '/route-data', loader: AsyncRouteDataCmp, data: {isAdmin: true}})
|
||||
]))
|
||||
.then((_) => rtr.navigateByUrl('/route-data'))
|
||||
.then((_) => {
|
||||
@@ -191,8 +226,7 @@ export function main() {
|
||||
|
||||
it('should inject empty object if the route has no data property',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
compile(tcb)
|
||||
.then((rtc) => {fixture = rtc})
|
||||
compile()
|
||||
.then((_) => rtr.config(
|
||||
[new Route({path: '/route-data-default', component: RouteDataCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/route-data-default'))
|
||||
@@ -202,28 +236,45 @@ export function main() {
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
describe('auxiliary routes', () => {
|
||||
it('should recognize a simple case', inject([AsyncTestCompleter], (async) => {
|
||||
compile()
|
||||
.then((_) => rtr.config([new Route({path: '/...', component: AuxCmp})]))
|
||||
.then((_) => rtr.navigateByUrl('/hello(modal)'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.nativeElement)
|
||||
.toHaveText('main {hello} | aux {modal}');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'hello-cmp', template: `{{greeting}}`})
|
||||
@Component({selector: 'hello-cmp'})
|
||||
@View({template: "{{greeting}}"})
|
||||
class HelloCmp {
|
||||
greeting: string;
|
||||
constructor() { this.greeting = 'hello'; }
|
||||
constructor() { this.greeting = "hello"; }
|
||||
}
|
||||
|
||||
|
||||
function asyncRouteDataCmp() {
|
||||
function AsyncRouteDataCmp() {
|
||||
return PromiseWrapper.resolve(RouteDataCmp);
|
||||
}
|
||||
|
||||
@Component({selector: 'data-cmp', template: `{{myData}}`})
|
||||
@Component({selector: 'data-cmp'})
|
||||
@View({template: "{{myData}}"})
|
||||
class RouteDataCmp {
|
||||
myData: boolean;
|
||||
constructor(data: RouteData) { this.myData = data.get('isAdmin'); }
|
||||
}
|
||||
|
||||
@Component({selector: 'user-cmp', template: `hello {{user}}`})
|
||||
@Component({selector: 'user-cmp'})
|
||||
@View({template: "hello {{user}}"})
|
||||
class UserCmp {
|
||||
user: string;
|
||||
constructor(params: RouteParams) {
|
||||
@@ -237,9 +288,9 @@ function parentLoader() {
|
||||
return PromiseWrapper.resolve(ParentCmp);
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent-cmp',
|
||||
template: `inner { <router-outlet></router-outlet> }`,
|
||||
@Component({selector: 'parent-cmp'})
|
||||
@View({
|
||||
template: "inner { <router-outlet></router-outlet> }",
|
||||
directives: [RouterOutlet],
|
||||
})
|
||||
@RouteConfig([
|
||||
@@ -247,12 +298,13 @@ function parentLoader() {
|
||||
new Route({path: '/', component: HelloCmp}),
|
||||
])
|
||||
class ParentCmp {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'team-cmp',
|
||||
template: `team {{id}} { <router-outlet></router-outlet> }`,
|
||||
@Component({selector: 'team-cmp'})
|
||||
@View({
|
||||
template: "team {{id}} { <router-outlet></router-outlet> }",
|
||||
directives: [RouterOutlet],
|
||||
})
|
||||
@RouteConfig([new Route({path: '/user/:name', component: UserCmp})])
|
||||
@@ -263,3 +315,27 @@ class TeamCmp {
|
||||
cmpInstanceCount += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'my-comp'})
|
||||
class MyComp {
|
||||
name;
|
||||
}
|
||||
|
||||
@Component({selector: 'modal-cmp'})
|
||||
@View({template: "modal"})
|
||||
class ModalCmp {
|
||||
}
|
||||
|
||||
@Component({selector: 'aux-cmp'})
|
||||
@View({
|
||||
template: 'main {<router-outlet></router-outlet>} | ' +
|
||||
'aux {<router-outlet name="modal"></router-outlet>}',
|
||||
directives: [RouterOutlet],
|
||||
})
|
||||
@RouteConfig([
|
||||
new Route({path: '/hello', component: HelloCmp}),
|
||||
new AuxRoute({path: '/modal', component: ModalCmp}),
|
||||
])
|
||||
class AuxCmp {
|
||||
}
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import {
|
||||
RootTestComponent,
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {Router, RouterOutlet, RouterLink, RouteParams, RouteData, Location} from 'angular2/router';
|
||||
import {
|
||||
RouteConfig,
|
||||
Route,
|
||||
AuxRoute,
|
||||
AsyncRoute,
|
||||
Redirect
|
||||
} from 'angular2/src/router/route_config_decorator';
|
||||
|
||||
import {TEST_ROUTER_PROVIDERS, RootCmp, compile} from './util';
|
||||
import {HelloCmp, RedirectToParentCmp} from './impl/fixture_components';
|
||||
|
||||
var cmpInstanceCount;
|
||||
var childCmpInstanceCount;
|
||||
|
||||
export function main() {
|
||||
describe('redirects', () => {
|
||||
|
||||
var tcb: TestComponentBuilder;
|
||||
var rootTC: RootTestComponent;
|
||||
var rtr;
|
||||
|
||||
beforeEachProviders(() => TEST_ROUTER_PROVIDERS);
|
||||
|
||||
beforeEach(inject([TestComponentBuilder, Router], (tcBuilder, router) => {
|
||||
tcb = tcBuilder;
|
||||
rtr = router;
|
||||
childCmpInstanceCount = 0;
|
||||
cmpInstanceCount = 0;
|
||||
}));
|
||||
|
||||
|
||||
it('should apply when navigating by URL',
|
||||
inject([AsyncTestCompleter, Location], (async, location) => {
|
||||
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, location) => {
|
||||
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, location) => {
|
||||
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, location) => {
|
||||
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();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
+54
-62
@@ -38,39 +38,44 @@ import {ApplicationRef} from 'angular2/src/core/application_ref';
|
||||
import {MockApplicationRef} from 'angular2/src/mock/mock_application_ref';
|
||||
|
||||
export function main() {
|
||||
describe('router bootstrap', () => {
|
||||
beforeEachProviders(() => [
|
||||
ROUTER_PROVIDERS,
|
||||
provide(LocationStrategy, {useClass: MockLocationStrategy}),
|
||||
provide(ApplicationRef, {useClass: MockApplicationRef})
|
||||
]);
|
||||
describe('router injectables', () => {
|
||||
beforeEachProviders(() => {
|
||||
return [
|
||||
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 bootstrapping a router works
|
||||
it('should bootstrap a simple app', inject([AsyncTestCompleter], (async) => {
|
||||
var fakeDoc = DOM.createHtmlDocument();
|
||||
var el = DOM.createElement('app-cmp', fakeDoc);
|
||||
DOM.appendChild(fakeDoc.body, el);
|
||||
describe('bootstrap functionality', () => {
|
||||
it('should bootstrap a simple app', inject([AsyncTestCompleter], (async) => {
|
||||
var fakeDoc = DOM.createHtmlDocument();
|
||||
var el = DOM.createElement('app-cmp', fakeDoc);
|
||||
DOM.appendChild(fakeDoc.body, el);
|
||||
|
||||
bootstrap(AppCmp,
|
||||
[
|
||||
ROUTER_PROVIDERS,
|
||||
provide(ROUTER_PRIMARY_COMPONENT, {useValue: AppCmp}),
|
||||
provide(LocationStrategy, {useClass: MockLocationStrategy}),
|
||||
provide(DOCUMENT, {useValue: fakeDoc})
|
||||
])
|
||||
.then((applicationRef) => {
|
||||
var router = applicationRef.hostComponent.router;
|
||||
router.subscribe((_) => {
|
||||
expect(el).toHaveText('outer { hello }');
|
||||
expect(applicationRef.hostComponent.location.path()).toEqual('');
|
||||
async.done();
|
||||
bootstrap(AppCmp,
|
||||
[
|
||||
ROUTER_PROVIDERS,
|
||||
provide(ROUTER_PRIMARY_COMPONENT, {useValue: AppCmp}),
|
||||
provide(LocationStrategy, {useClass: MockLocationStrategy}),
|
||||
provide(DOCUMENT, {useValue: fakeDoc})
|
||||
])
|
||||
.then((applicationRef) => {
|
||||
var router = applicationRef.hostComponent.router;
|
||||
router.subscribe((_) => {
|
||||
expect(el).toHaveText('outer { hello }');
|
||||
expect(applicationRef.hostComponent.location.path()).toEqual('');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
}));
|
||||
});
|
||||
|
||||
describe('broken app', () => {
|
||||
beforeEachProviders(() => [provide(ROUTER_PRIMARY_COMPONENT, {useValue: BrokenAppCmp})]);
|
||||
beforeEachProviders(
|
||||
() => { return [provide(ROUTER_PRIMARY_COMPONENT, {useValue: BrokenAppCmp})]; });
|
||||
|
||||
it('should rethrow exceptions from component constructors',
|
||||
inject([AsyncTestCompleter, TestComponentBuilder], (async, tcb: TestComponentBuilder) => {
|
||||
@@ -86,7 +91,8 @@ export function main() {
|
||||
});
|
||||
|
||||
describe('back button app', () => {
|
||||
beforeEachProviders(() => [provide(ROUTER_PRIMARY_COMPONENT, {useValue: HierarchyAppCmp})]);
|
||||
beforeEachProviders(
|
||||
() => { return [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) => {
|
||||
@@ -178,7 +184,7 @@ export function main() {
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: add a test in which the child component has bindings
|
||||
|
||||
describe('querystring params app', () => {
|
||||
beforeEachProviders(
|
||||
@@ -237,21 +243,20 @@ export function main() {
|
||||
}
|
||||
|
||||
|
||||
@Component({selector: 'hello-cmp', template: 'hello'})
|
||||
@Component({selector: 'hello-cmp'})
|
||||
@View({template: 'hello'})
|
||||
class HelloCmp {
|
||||
public message: string;
|
||||
}
|
||||
|
||||
@Component({selector: 'hello2-cmp', template: 'hello2'})
|
||||
@Component({selector: 'hello2-cmp'})
|
||||
@View({template: 'hello2'})
|
||||
class Hello2Cmp {
|
||||
public greeting: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-cmp',
|
||||
template: `outer { <router-outlet></router-outlet> }`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
@Component({selector: 'app-cmp'})
|
||||
@View({template: "outer { <router-outlet></router-outlet> }", directives: ROUTER_DIRECTIVES})
|
||||
@RouteConfig([new Route({path: '/', component: HelloCmp})])
|
||||
class AppCmp {
|
||||
constructor(public router: Router, public location: LocationStrategy) {}
|
||||
@@ -278,29 +283,20 @@ class AppWithViewChildren implements AfterViewInit {
|
||||
afterViewInit() { this.helloCmp.message = 'Ahoy'; }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'parent-cmp',
|
||||
template: `parent { <router-outlet></router-outlet> }`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
@Component({selector: 'parent-cmp'})
|
||||
@View({template: `parent { <router-outlet></router-outlet> }`, directives: ROUTER_DIRECTIVES})
|
||||
@RouteConfig([new Route({path: '/child', component: HelloCmp})])
|
||||
class ParentCmp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'super-parent-cmp',
|
||||
template: `super-parent { <router-outlet></router-outlet> }`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
@Component({selector: 'super-parent-cmp'})
|
||||
@View({template: `super-parent { <router-outlet></router-outlet> }`, directives: ROUTER_DIRECTIVES})
|
||||
@RouteConfig([new Route({path: '/child', component: Hello2Cmp})])
|
||||
class SuperParentCmp {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-cmp',
|
||||
template: `root { <router-outlet></router-outlet> }`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
@Component({selector: 'app-cmp'})
|
||||
@View({template: `root { <router-outlet></router-outlet> }`, directives: ROUTER_DIRECTIVES})
|
||||
@RouteConfig([
|
||||
new Route({path: '/parent/...', component: ParentCmp}),
|
||||
new Route({path: '/super-parent/...', component: SuperParentCmp})
|
||||
@@ -309,32 +305,28 @@ class HierarchyAppCmp {
|
||||
constructor(public router: Router, public location: LocationStrategy) {}
|
||||
}
|
||||
|
||||
@Component({selector: 'qs-cmp', template: `qParam = {{q}}`})
|
||||
@Component({selector: 'qs-cmp'})
|
||||
@View({template: "qParam = {{q}}"})
|
||||
class QSCmp {
|
||||
q: string;
|
||||
constructor(params: RouteParams) { this.q = params.get('q'); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-cmp',
|
||||
template: `<router-outlet></router-outlet>`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
@Component({selector: 'app-cmp'})
|
||||
@View({template: `<router-outlet></router-outlet>`, directives: ROUTER_DIRECTIVES})
|
||||
@RouteConfig([new Route({path: '/qs', component: QSCmp})])
|
||||
class QueryStringAppCmp {
|
||||
constructor(public router: Router, public location: LocationStrategy) {}
|
||||
}
|
||||
|
||||
@Component({selector: 'oops-cmp', template: "oh no"})
|
||||
@Component({selector: 'oops-cmp'})
|
||||
@View({template: "oh no"})
|
||||
class BrokenCmp {
|
||||
constructor() { throw new BaseException('oops!'); }
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-cmp',
|
||||
template: `outer { <router-outlet></router-outlet> }`,
|
||||
directives: ROUTER_DIRECTIVES
|
||||
})
|
||||
@Component({selector: 'app-cmp'})
|
||||
@View({template: `outer { <router-outlet></router-outlet> }`, directives: ROUTER_DIRECTIVES})
|
||||
@RouteConfig([new Route({path: '/cause-error', component: BrokenCmp})])
|
||||
class BrokenAppCmp {
|
||||
constructor(public router: Router, public location: LocationStrategy) {}
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder,
|
||||
@@ -21,7 +21,7 @@ import {NumberWrapper} from 'angular2/src/facade/lang';
|
||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {provide, Component, View, DirectiveResolver} from 'angular2/core';
|
||||
import {provide, Component, DirectiveResolver} from 'angular2/core';
|
||||
|
||||
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
||||
import {
|
||||
@@ -47,7 +47,7 @@ export function main() {
|
||||
var fixture: ComponentFixture;
|
||||
var router, location;
|
||||
|
||||
beforeEachProviders(() => [
|
||||
beforeEachBindings(() => [
|
||||
RouteRegistry,
|
||||
DirectiveResolver,
|
||||
provide(Location, {useClass: SpyLocation}),
|
||||
@@ -240,8 +240,8 @@ export function main() {
|
||||
.then((_) => router.config([new Route({path: '/...', component: AuxLinkCmp})]))
|
||||
.then((_) => router.navigateByUrl('/'))
|
||||
.then((_) => {
|
||||
fixture.detectChanges();
|
||||
expect(DOM.getAttribute(fixture.debugElement.componentViewChildren[1]
|
||||
rootTC.detectChanges();
|
||||
expect(DOM.getAttribute(rootTC.debugElement.componentViewChildren[1]
|
||||
.componentViewChildren[0]
|
||||
.nativeElement,
|
||||
'href'))
|
||||
@@ -386,7 +386,10 @@ class MyComp {
|
||||
name;
|
||||
}
|
||||
|
||||
@Component({selector: 'user-cmp', template: "hello {{user}}"})
|
||||
@Component({
|
||||
selector: 'user-cmp',
|
||||
template: "hello {{user}}"
|
||||
})
|
||||
class UserCmp {
|
||||
user: string;
|
||||
constructor(params: RouteParams) { this.user = params.get('name'); }
|
||||
@@ -422,11 +425,17 @@ class NoPrefixSiblingPageCmp {
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'hello-cmp', template: 'hello'})
|
||||
@Component({
|
||||
selector: 'hello-cmp',
|
||||
template: 'hello'
|
||||
})
|
||||
class HelloCmp {
|
||||
}
|
||||
|
||||
@Component({selector: 'hello2-cmp', template: 'hello2'})
|
||||
@Component({
|
||||
selector: 'hello2-cmp',
|
||||
template: 'hello2'
|
||||
})
|
||||
class Hello2Cmp {
|
||||
}
|
||||
|
||||
@@ -446,6 +455,7 @@ function parentCmpLoader() {
|
||||
new Route({path: '/better-grandchild', component: Hello2Cmp, name: 'BetterGrandchild'})
|
||||
])
|
||||
class ParentCmp {
|
||||
constructor(public router: Router) {}
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import {
|
||||
describeRouter,
|
||||
ddescribeRouter,
|
||||
describeWith,
|
||||
describeWithout,
|
||||
describeWithAndWithout,
|
||||
itShouldRoute
|
||||
} from './util';
|
||||
|
||||
import {registerSpecs} from './impl/sync_route_spec_impl';
|
||||
|
||||
export function main() {
|
||||
registerSpecs();
|
||||
|
||||
describeRouter('sync routes', () => {
|
||||
describeWithout('children', () => { describeWithAndWithout('params', itShouldRoute); });
|
||||
|
||||
describeWith('sync children', () => {
|
||||
describeWithout('default routes', () => { describeWithAndWithout('params', itShouldRoute); });
|
||||
describeWith('default routes', () => { describeWithout('params', itShouldRoute); });
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
import {provide, Provider, Component, View} from 'angular2/core';
|
||||
export {Provider} from 'angular2/core';
|
||||
import {Type, isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {
|
||||
RootTestComponent,
|
||||
AsyncTestCompleter,
|
||||
TestComponentBuilder,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
xdescribe,
|
||||
describe,
|
||||
el,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
it,
|
||||
xit
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {RootRouter} from 'angular2/src/router/router';
|
||||
import {Router, ROUTER_DIRECTIVES} from 'angular2/router';
|
||||
|
||||
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
||||
import {Location} from 'angular2/src/router/location';
|
||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
export {ComponentFixture} from 'angular2/testing_internal';
|
||||
|
||||
|
||||
/**
|
||||
* Router test helpers and fixtures
|
||||
*/
|
||||
|
||||
@Component({
|
||||
selector: 'root-comp',
|
||||
template: `<router-outlet></router-outlet>`,
|
||||
directives: [ROUTER_DIRECTIVES]
|
||||
})
|
||||
export class RootCmp {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function compile(tcb: TestComponentBuilder,
|
||||
template: string = "<router-outlet></router-outlet>") {
|
||||
return tcb.overrideTemplate(RootCmp, ('<div>' + template + '</div>')).createAsync(RootCmp);
|
||||
}
|
||||
|
||||
export var TEST_ROUTER_PROVIDERS = [
|
||||
RouteRegistry,
|
||||
DirectiveResolver,
|
||||
provide(Location, {useClass: SpyLocation}),
|
||||
provide(
|
||||
Router,
|
||||
{
|
||||
useFactory: (registry, location) => { return new RootRouter(registry, location, RootCmp);},
|
||||
deps: [RouteRegistry, Location]
|
||||
})
|
||||
];
|
||||
|
||||
export function clickOnElement(anchorEl) {
|
||||
var dispatchedEvent = DOM.createMouseEvent('click');
|
||||
DOM.dispatchEvent(anchorEl, dispatchedEvent);
|
||||
return dispatchedEvent;
|
||||
}
|
||||
|
||||
export function getHref(elt) {
|
||||
return DOM.getAttribute(elt, 'href');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Router integration suite DSL
|
||||
*/
|
||||
|
||||
var specNameBuilder = [];
|
||||
|
||||
// we add the specs themselves onto this map
|
||||
export var specs = {};
|
||||
|
||||
export function describeRouter(description: string, fn: Function, exclusive = false): void {
|
||||
var specName = descriptionToSpecName(description);
|
||||
specNameBuilder.push(specName);
|
||||
describe(description, fn);
|
||||
specNameBuilder.pop();
|
||||
}
|
||||
|
||||
export function ddescribeRouter(description: string, fn: Function, exclusive = false): void {
|
||||
describeRouter(description, fn, true);
|
||||
}
|
||||
|
||||
export function describeWithAndWithout(description: string, fn: Function): void {
|
||||
// the "without" case is usually simpler, so we opt to run this spec first
|
||||
describeWithout(description, fn);
|
||||
describeWith(description, fn);
|
||||
}
|
||||
|
||||
export function describeWith(description: string, fn: Function): void {
|
||||
var specName = 'with ' + description;
|
||||
specNameBuilder.push(specName);
|
||||
describe(specName, fn);
|
||||
specNameBuilder.pop();
|
||||
}
|
||||
|
||||
export function describeWithout(description: string, fn: Function): void {
|
||||
var specName = 'without ' + description;
|
||||
specNameBuilder.push(specName);
|
||||
describe(specName, fn);
|
||||
specNameBuilder.pop();
|
||||
}
|
||||
|
||||
function descriptionToSpecName(description: string): string {
|
||||
return spaceCaseToCamelCase(description);
|
||||
}
|
||||
|
||||
// this helper looks up the suite registered from the "impl" folder in this directory
|
||||
export function itShouldRoute() {
|
||||
var specSuiteName = spaceCaseToCamelCase(specNameBuilder.join(' '));
|
||||
|
||||
var spec = specs[specSuiteName];
|
||||
if (isBlank(spec)) {
|
||||
throw new BaseException(`Router integration spec suite "${specSuiteName}" was not found.`);
|
||||
} else {
|
||||
// todo: remove spec from map, throw if there are extra left over??
|
||||
spec();
|
||||
}
|
||||
}
|
||||
|
||||
function spaceCaseToCamelCase(str: string): string {
|
||||
var words = str.split(' ');
|
||||
var first = words.shift();
|
||||
return first + words.map(title).join('');
|
||||
}
|
||||
|
||||
function title(str: string): string {
|
||||
return str[0].toUpperCase() + str.substring(1);
|
||||
}
|
||||
@@ -12,82 +12,100 @@ import {
|
||||
|
||||
import {PathRecognizer} from 'angular2/src/router/path_recognizer';
|
||||
import {parser, Url, RootUrl} from 'angular2/src/router/url_parser';
|
||||
import {SyncRouteHandler} from 'angular2/src/router/sync_route_handler';
|
||||
|
||||
class DummyClass {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
var mockRouteHandler = new SyncRouteHandler(DummyClass);
|
||||
|
||||
export function main() {
|
||||
describe('PathRecognizer', () => {
|
||||
|
||||
it('should throw when given an invalid path', () => {
|
||||
expect(() => new PathRecognizer('/hi#'))
|
||||
expect(() => new PathRecognizer('/hi#', mockRouteHandler))
|
||||
.toThrowError(`Path "/hi#" should not include "#". Use "HashLocationStrategy" instead.`);
|
||||
expect(() => new PathRecognizer('hi?'))
|
||||
expect(() => new PathRecognizer('hi?', mockRouteHandler))
|
||||
.toThrowError(`Path "hi?" contains "?" which is not allowed in a route config.`);
|
||||
expect(() => new PathRecognizer('hi;'))
|
||||
expect(() => new PathRecognizer('hi;', mockRouteHandler))
|
||||
.toThrowError(`Path "hi;" contains ";" which is not allowed in a route config.`);
|
||||
expect(() => new PathRecognizer('hi='))
|
||||
expect(() => new PathRecognizer('hi=', mockRouteHandler))
|
||||
.toThrowError(`Path "hi=" contains "=" which is not allowed in a route config.`);
|
||||
expect(() => new PathRecognizer('hi('))
|
||||
expect(() => new PathRecognizer('hi(', mockRouteHandler))
|
||||
.toThrowError(`Path "hi(" contains "(" which is not allowed in a route config.`);
|
||||
expect(() => new PathRecognizer('hi)'))
|
||||
expect(() => new PathRecognizer('hi)', mockRouteHandler))
|
||||
.toThrowError(`Path "hi)" contains ")" which is not allowed in a route config.`);
|
||||
expect(() => new PathRecognizer('hi//there'))
|
||||
expect(() => new PathRecognizer('hi//there', mockRouteHandler))
|
||||
.toThrowError(`Path "hi//there" contains "//" which is not allowed in a route config.`);
|
||||
});
|
||||
|
||||
it('should return the same instruction instance when recognizing the same path', () => {
|
||||
var rec = new PathRecognizer('/one', mockRouteHandler);
|
||||
|
||||
var one = new Url('one', null, null, {});
|
||||
|
||||
var firstMatch = rec.recognize(one);
|
||||
var secondMatch = rec.recognize(one);
|
||||
|
||||
expect(firstMatch.instruction).toBe(secondMatch.instruction);
|
||||
});
|
||||
|
||||
describe('querystring params', () => {
|
||||
it('should parse querystring params so long as the recognizer is a root', () => {
|
||||
var rec = new PathRecognizer('/hello/there');
|
||||
var rec = new PathRecognizer('/hello/there', mockRouteHandler);
|
||||
var url = parser.parse('/hello/there?name=igor');
|
||||
var match = rec.recognize(url);
|
||||
expect(match['allParams']).toEqual({'name': 'igor'});
|
||||
expect(match.instruction.params).toEqual({'name': 'igor'});
|
||||
});
|
||||
|
||||
it('should return a combined map of parameters with the param expected in the URL path',
|
||||
() => {
|
||||
var rec = new PathRecognizer('/hello/:name');
|
||||
var rec = new PathRecognizer('/hello/:name', mockRouteHandler);
|
||||
var url = parser.parse('/hello/paul?topic=success');
|
||||
var match = rec.recognize(url);
|
||||
expect(match['allParams']).toEqual({'name': 'paul', 'topic': 'success'});
|
||||
expect(match.instruction.params).toEqual({'name': 'paul', 'topic': 'success'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('matrix params', () => {
|
||||
it('should be parsed along with dynamic paths', () => {
|
||||
var rec = new PathRecognizer('/hello/:id');
|
||||
var rec = new PathRecognizer('/hello/:id', mockRouteHandler);
|
||||
var url = new Url('hello', new Url('matias', null, null, {'key': 'value'}));
|
||||
var match = rec.recognize(url);
|
||||
expect(match['allParams']).toEqual({'id': 'matias', 'key': 'value'});
|
||||
expect(match.instruction.params).toEqual({'id': 'matias', 'key': 'value'});
|
||||
});
|
||||
|
||||
it('should be parsed on a static path', () => {
|
||||
var rec = new PathRecognizer('/person');
|
||||
var rec = new PathRecognizer('/person', mockRouteHandler);
|
||||
var url = new Url('person', null, null, {'name': 'dave'});
|
||||
var match = rec.recognize(url);
|
||||
expect(match['allParams']).toEqual({'name': 'dave'});
|
||||
expect(match.instruction.params).toEqual({'name': 'dave'});
|
||||
});
|
||||
|
||||
it('should be ignored on a wildcard segment', () => {
|
||||
var rec = new PathRecognizer('/wild/*everything');
|
||||
var rec = new PathRecognizer('/wild/*everything', mockRouteHandler);
|
||||
var url = parser.parse('/wild/super;variable=value');
|
||||
var match = rec.recognize(url);
|
||||
expect(match['allParams']).toEqual({'everything': 'super;variable=value'});
|
||||
expect(match.instruction.params).toEqual({'everything': 'super;variable=value'});
|
||||
});
|
||||
|
||||
it('should set matrix param values to true when no value is present', () => {
|
||||
var rec = new PathRecognizer('/path');
|
||||
var rec = new PathRecognizer('/path', mockRouteHandler);
|
||||
var url = new Url('path', null, null, {'one': true, 'two': true, 'three': '3'});
|
||||
var match = rec.recognize(url);
|
||||
expect(match['allParams']).toEqual({'one': true, 'two': true, 'three': '3'});
|
||||
expect(match.instruction.params).toEqual({'one': true, 'two': true, 'three': '3'});
|
||||
});
|
||||
|
||||
it('should be parsed on the final segment of the path', () => {
|
||||
var rec = new PathRecognizer('/one/two/three');
|
||||
var rec = new PathRecognizer('/one/two/three', mockRouteHandler);
|
||||
|
||||
var three = new Url('three', null, null, {'c': '3'});
|
||||
var two = new Url('two', three, null, {'b': '2'});
|
||||
var one = new Url('one', two, null, {'a': '1'});
|
||||
|
||||
var match = rec.recognize(one);
|
||||
expect(match['allParams']).toEqual({'c': '3'});
|
||||
expect(match.instruction.params).toEqual({'c': '3'});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -214,10 +214,7 @@ class HelloCmp {
|
||||
|
||||
@Component({selector: 'app-cmp'})
|
||||
@View({template: `root { <router-outlet></router-outlet> }`, directives: ROUTER_DIRECTIVES})
|
||||
@RouteConfig([
|
||||
{path: '/before', redirectTo: ['Hello']},
|
||||
{path: '/after', component: HelloCmp, name: 'Hello'}
|
||||
])
|
||||
@RouteConfig([{path: '/before', redirectTo: '/after'}, {path: '/after', component: HelloCmp}])
|
||||
class RedirectAppCmp {
|
||||
constructor(public router: Router, public location: LocationStrategy) {}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
describe,
|
||||
it,
|
||||
iit,
|
||||
ddescribe,
|
||||
expect,
|
||||
inject,
|
||||
beforeEach,
|
||||
SpyObject
|
||||
} from 'angular2/testing_internal';
|
||||
|
||||
import {Map, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {RouteRecognizer} from 'angular2/src/router/route_recognizer';
|
||||
import {ComponentInstruction} from 'angular2/src/router/instruction';
|
||||
|
||||
import {Route, Redirect} from 'angular2/src/router/route_config_decorator';
|
||||
import {parser} from 'angular2/src/router/url_parser';
|
||||
|
||||
export function main() {
|
||||
describe('RouteRecognizer', () => {
|
||||
var recognizer;
|
||||
|
||||
beforeEach(() => { recognizer = new RouteRecognizer(); });
|
||||
|
||||
|
||||
it('should recognize a static segment', () => {
|
||||
recognizer.config(new Route({path: '/test', component: DummyCmpA}));
|
||||
var solution = recognize(recognizer, '/test');
|
||||
expect(getComponentType(solution)).toEqual(DummyCmpA);
|
||||
});
|
||||
|
||||
|
||||
it('should recognize a single slash', () => {
|
||||
recognizer.config(new Route({path: '/', component: DummyCmpA}));
|
||||
var solution = recognize(recognizer, '/');
|
||||
expect(getComponentType(solution)).toEqual(DummyCmpA);
|
||||
});
|
||||
|
||||
|
||||
it('should recognize a dynamic segment', () => {
|
||||
recognizer.config(new Route({path: '/user/:name', component: DummyCmpA}));
|
||||
var solution = recognize(recognizer, '/user/brian');
|
||||
expect(getComponentType(solution)).toEqual(DummyCmpA);
|
||||
expect(solution.params).toEqual({'name': 'brian'});
|
||||
});
|
||||
|
||||
|
||||
it('should recognize a star segment', () => {
|
||||
recognizer.config(new Route({path: '/first/*rest', component: DummyCmpA}));
|
||||
var solution = recognize(recognizer, '/first/second/third');
|
||||
expect(getComponentType(solution)).toEqual(DummyCmpA);
|
||||
expect(solution.params).toEqual({'rest': 'second/third'});
|
||||
});
|
||||
|
||||
|
||||
it('should throw when given two routes that start with the same static segment', () => {
|
||||
recognizer.config(new Route({path: '/hello', component: DummyCmpA}));
|
||||
expect(() => recognizer.config(new Route({path: '/hello', component: DummyCmpB})))
|
||||
.toThrowError('Configuration \'/hello\' conflicts with existing route \'/hello\'');
|
||||
});
|
||||
|
||||
|
||||
it('should throw when given two routes that have dynamic segments in the same order', () => {
|
||||
recognizer.config(new Route({path: '/hello/:person/how/:doyoudou', component: DummyCmpA}));
|
||||
expect(() => recognizer.config(
|
||||
new Route({path: '/hello/:friend/how/:areyou', component: DummyCmpA})))
|
||||
.toThrowError(
|
||||
'Configuration \'/hello/:friend/how/:areyou\' conflicts with existing route \'/hello/:person/how/:doyoudou\'');
|
||||
});
|
||||
|
||||
|
||||
it('should recognize redirects', () => {
|
||||
recognizer.config(new Route({path: '/b', component: DummyCmpA}));
|
||||
recognizer.config(new Redirect({path: '/a', redirectTo: 'b'}));
|
||||
var solution = recognize(recognizer, '/a');
|
||||
expect(getComponentType(solution)).toEqual(DummyCmpA);
|
||||
expect(solution.urlPath).toEqual('b');
|
||||
});
|
||||
|
||||
|
||||
it('should not perform root URL redirect on a non-root route', () => {
|
||||
recognizer.config(new Redirect({path: '/', redirectTo: '/foo'}));
|
||||
recognizer.config(new Route({path: '/bar', component: DummyCmpA}));
|
||||
var solution = recognize(recognizer, '/bar');
|
||||
expect(solution.componentType).toEqual(DummyCmpA);
|
||||
expect(solution.urlPath).toEqual('bar');
|
||||
});
|
||||
|
||||
|
||||
it('should perform a root URL redirect only for root routes', () => {
|
||||
recognizer.config(new Redirect({path: '/', redirectTo: '/matias'}));
|
||||
recognizer.config(new Route({path: '/matias', component: DummyCmpA}));
|
||||
recognizer.config(new Route({path: '/fatias', component: DummyCmpA}));
|
||||
|
||||
var solution;
|
||||
|
||||
solution = recognize(recognizer, '/');
|
||||
expect(solution.urlPath).toEqual('matias');
|
||||
|
||||
solution = recognize(recognizer, '/fatias');
|
||||
expect(solution.urlPath).toEqual('fatias');
|
||||
|
||||
solution = recognize(recognizer, '');
|
||||
expect(solution.urlPath).toEqual('matias');
|
||||
});
|
||||
|
||||
|
||||
it('should generate URLs with params', () => {
|
||||
recognizer.config(new Route({path: '/app/user/:name', component: DummyCmpA, name: 'User'}));
|
||||
var instruction = recognizer.generate('User', {'name': 'misko'});
|
||||
expect(instruction.urlPath).toEqual('app/user/misko');
|
||||
});
|
||||
|
||||
|
||||
it('should generate URLs with numeric params', () => {
|
||||
recognizer.config(new Route({path: '/app/page/:number', component: DummyCmpA, name: 'Page'}));
|
||||
expect(recognizer.generate('Page', {'number': 42}).urlPath).toEqual('app/page/42');
|
||||
});
|
||||
|
||||
|
||||
it('should throw in the absence of required params URLs', () => {
|
||||
recognizer.config(new Route({path: 'app/user/:name', component: DummyCmpA, name: 'User'}));
|
||||
expect(() => recognizer.generate('User', {}))
|
||||
.toThrowError('Route generator for \'name\' was not included in parameters passed.');
|
||||
});
|
||||
|
||||
|
||||
it('should throw if the route alias is not CamelCase', () => {
|
||||
expect(() => recognizer.config(
|
||||
new Route({path: 'app/user/:name', component: DummyCmpA, name: 'user'})))
|
||||
.toThrowError(
|
||||
`Route "app/user/:name" with name "user" does not begin with an uppercase letter. Route names should be CamelCase like "User".`);
|
||||
});
|
||||
|
||||
|
||||
describe('params', () => {
|
||||
it('should recognize parameters within the URL path', () => {
|
||||
recognizer.config(new Route({path: 'profile/:name', component: DummyCmpA, name: 'User'}));
|
||||
var solution = recognize(recognizer, '/profile/matsko?comments=all');
|
||||
expect(solution.params).toEqual({'name': 'matsko', 'comments': 'all'});
|
||||
});
|
||||
|
||||
|
||||
it('should generate and populate the given static-based route with querystring params',
|
||||
() => {
|
||||
recognizer.config(
|
||||
new Route({path: 'forum/featured', component: DummyCmpA, name: 'ForumPage'}));
|
||||
|
||||
var params = {'start': 10, 'end': 100};
|
||||
|
||||
var result = recognizer.generate('ForumPage', params);
|
||||
expect(result.urlPath).toEqual('forum/featured');
|
||||
expect(result.urlParams).toEqual(['start=10', 'end=100']);
|
||||
});
|
||||
|
||||
|
||||
it('should prefer positional params over query params', () => {
|
||||
recognizer.config(new Route({path: 'profile/:name', component: DummyCmpA, name: 'User'}));
|
||||
|
||||
var solution = recognize(recognizer, '/profile/yegor?name=igor');
|
||||
expect(solution.params).toEqual({'name': 'yegor'});
|
||||
});
|
||||
|
||||
|
||||
it('should ignore matrix params for the top-level component', () => {
|
||||
recognizer.config(new Route({path: '/home/:subject', component: DummyCmpA, name: 'User'}));
|
||||
var solution = recognize(recognizer, '/home;sort=asc/zero;one=1?two=2');
|
||||
expect(solution.params).toEqual({'subject': 'zero', 'two': '2'});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function recognize(recognizer: RouteRecognizer, url: string): ComponentInstruction {
|
||||
return recognizer.recognize(parser.parse(url))[0].instruction;
|
||||
}
|
||||
|
||||
function getComponentType(routeMatch: ComponentInstruction): any {
|
||||
return routeMatch.componentType;
|
||||
}
|
||||
|
||||
class DummyCmpA {}
|
||||
class DummyCmpB {}
|
||||
@@ -21,9 +21,9 @@ import {
|
||||
AuxRoute,
|
||||
AsyncRoute
|
||||
} from 'angular2/src/router/route_config_decorator';
|
||||
import {stringifyInstruction} from 'angular2/src/router/instruction';
|
||||
import {IS_DART} from 'angular2/src/facade/lang';
|
||||
|
||||
|
||||
export function main() {
|
||||
describe('RouteRegistry', () => {
|
||||
var registry;
|
||||
@@ -34,7 +34,7 @@ export function main() {
|
||||
registry.config(RootHostCmp, new Route({path: '/', component: DummyCmpA}));
|
||||
registry.config(RootHostCmp, new Route({path: '/test', component: DummyCmpB}));
|
||||
|
||||
registry.recognize('/test', [RootHostCmp])
|
||||
registry.recognize('/test', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyCmpB);
|
||||
async.done();
|
||||
@@ -45,32 +45,28 @@ export function main() {
|
||||
registry.config(RootHostCmp,
|
||||
new Route({path: '/first/...', component: DummyParentCmp, name: 'FirstCmp'}));
|
||||
|
||||
expect(stringifyInstruction(registry.generate(['FirstCmp', 'SecondCmp'], [RootHostCmp])))
|
||||
expect(stringifyInstruction(registry.generate(['FirstCmp', 'SecondCmp'], RootHostCmp)))
|
||||
.toEqual('first/second');
|
||||
expect(stringifyInstruction(registry.generate(['SecondCmp'], [DummyParentCmp])))
|
||||
expect(stringifyInstruction(registry.generate(['SecondCmp'], DummyParentCmp)))
|
||||
.toEqual('second');
|
||||
});
|
||||
|
||||
it('should generate URLs that account for default routes', () => {
|
||||
xit('should generate URLs that account for redirects', () => {
|
||||
registry.config(
|
||||
RootHostCmp,
|
||||
new Route({path: '/first/...', component: ParentWithDefaultRouteCmp, name: 'FirstCmp'}));
|
||||
new Route({path: '/first/...', component: DummyParentRedirectCmp, name: 'FirstCmp'}));
|
||||
|
||||
var instruction = registry.generate(['FirstCmp'], [RootHostCmp]);
|
||||
|
||||
expect(instruction.toLinkUrl()).toEqual('first');
|
||||
expect(instruction.toRootUrl()).toEqual('first/second');
|
||||
expect(stringifyInstruction(registry.generate(['FirstCmp'], RootHostCmp)))
|
||||
.toEqual('first/second');
|
||||
});
|
||||
|
||||
it('should generate URLs in a hierarchy of default routes', () => {
|
||||
xit('should generate URLs in a hierarchy of redirects', () => {
|
||||
registry.config(
|
||||
RootHostCmp,
|
||||
new Route({path: '/first/...', component: MultipleDefaultCmp, name: 'FirstCmp'}));
|
||||
new Route({path: '/first/...', component: DummyMultipleRedirectCmp, name: 'FirstCmp'}));
|
||||
|
||||
var instruction = registry.generate(['FirstCmp'], [RootHostCmp]);
|
||||
|
||||
expect(instruction.toLinkUrl()).toEqual('first');
|
||||
expect(instruction.toRootUrl()).toEqual('first/second/third');
|
||||
expect(stringifyInstruction(registry.generate(['FirstCmp'], RootHostCmp)))
|
||||
.toEqual('first/second/third');
|
||||
});
|
||||
|
||||
it('should generate URLs with params', () => {
|
||||
@@ -79,13 +75,13 @@ export function main() {
|
||||
new Route({path: '/first/:param/...', component: DummyParentParamCmp, name: 'FirstCmp'}));
|
||||
|
||||
var url = stringifyInstruction(registry.generate(
|
||||
['FirstCmp', {param: 'one'}, 'SecondCmp', {param: 'two'}], [RootHostCmp]));
|
||||
['FirstCmp', {param: 'one'}, 'SecondCmp', {param: 'two'}], RootHostCmp));
|
||||
expect(url).toEqual('first/one/second/two');
|
||||
});
|
||||
|
||||
it('should generate params as an empty StringMap when no params are given', () => {
|
||||
registry.config(RootHostCmp, new Route({path: '/test', component: DummyCmpA, name: 'Test'}));
|
||||
var instruction = registry.generate(['Test'], [RootHostCmp]);
|
||||
var instruction = registry.generate(['Test'], RootHostCmp);
|
||||
expect(instruction.component.params).toEqual({});
|
||||
});
|
||||
|
||||
@@ -95,20 +91,20 @@ export function main() {
|
||||
RootHostCmp,
|
||||
new AsyncRoute({path: '/first/...', loader: asyncParentLoader, name: 'FirstCmp'}));
|
||||
|
||||
var instruction = registry.generate(['FirstCmp', 'SecondCmp'], [RootHostCmp]);
|
||||
expect(() => registry.generate(['FirstCmp', 'SecondCmp'], RootHostCmp))
|
||||
.toThrowError('Could not find route named "SecondCmp".');
|
||||
|
||||
expect(stringifyInstruction(instruction)).toEqual('first');
|
||||
|
||||
registry.recognize('/first/second', [RootHostCmp])
|
||||
registry.recognize('/first/second', RootHostCmp)
|
||||
.then((_) => {
|
||||
var instruction = registry.generate(['FirstCmp', 'SecondCmp'], [RootHostCmp]);
|
||||
expect(stringifyInstruction(instruction)).toEqual('first/second');
|
||||
expect(
|
||||
stringifyInstruction(registry.generate(['FirstCmp', 'SecondCmp'], RootHostCmp)))
|
||||
.toEqual('first/second');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should throw when generating a url and a parent has no config', () => {
|
||||
expect(() => registry.generate(['FirstCmp', 'SecondCmp'], [RootHostCmp]))
|
||||
expect(() => registry.generate(['FirstCmp', 'SecondCmp'], RootHostCmp))
|
||||
.toThrowError('Component "RootHostCmp" has no route config.');
|
||||
});
|
||||
|
||||
@@ -117,7 +113,7 @@ export function main() {
|
||||
new Route({path: '/primary', component: DummyCmpA, name: 'Primary'}));
|
||||
registry.config(RootHostCmp, new AuxRoute({path: '/aux', component: DummyCmpB, name: 'Aux'}));
|
||||
|
||||
expect(stringifyInstruction(registry.generate(['Primary', ['Aux']], [RootHostCmp])))
|
||||
expect(stringifyInstruction(registry.generate(['Primary', ['Aux']], RootHostCmp)))
|
||||
.toEqual('primary(aux)');
|
||||
});
|
||||
|
||||
@@ -125,7 +121,7 @@ export function main() {
|
||||
registry.config(RootHostCmp, new Route({path: '/:site', component: DummyCmpB}));
|
||||
registry.config(RootHostCmp, new Route({path: '/home', component: DummyCmpA}));
|
||||
|
||||
registry.recognize('/home', [RootHostCmp])
|
||||
registry.recognize('/home', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyCmpA);
|
||||
async.done();
|
||||
@@ -136,7 +132,7 @@ export function main() {
|
||||
registry.config(RootHostCmp, new Route({path: '/:site', component: DummyCmpA}));
|
||||
registry.config(RootHostCmp, new Route({path: '/*site', component: DummyCmpB}));
|
||||
|
||||
registry.recognize('/home', [RootHostCmp])
|
||||
registry.recognize('/home', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyCmpA);
|
||||
async.done();
|
||||
@@ -147,7 +143,7 @@ export function main() {
|
||||
registry.config(RootHostCmp, new Route({path: '/:first/*rest', component: DummyCmpA}));
|
||||
registry.config(RootHostCmp, new Route({path: '/*all', component: DummyCmpB}));
|
||||
|
||||
registry.recognize('/some/path', [RootHostCmp])
|
||||
registry.recognize('/some/path', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyCmpA);
|
||||
async.done();
|
||||
@@ -158,7 +154,7 @@ export function main() {
|
||||
registry.config(RootHostCmp, new Route({path: '/first/:second', component: DummyCmpA}));
|
||||
registry.config(RootHostCmp, new Route({path: '/:first/:second', component: DummyCmpB}));
|
||||
|
||||
registry.recognize('/first/second', [RootHostCmp])
|
||||
registry.recognize('/first/second', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyCmpA);
|
||||
async.done();
|
||||
@@ -172,7 +168,7 @@ export function main() {
|
||||
registry.config(RootHostCmp,
|
||||
new Route({path: '/first/:second/third', component: DummyCmpA}));
|
||||
|
||||
registry.recognize('/first/second/third', [RootHostCmp])
|
||||
registry.recognize('/first/second/third', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyCmpB);
|
||||
async.done();
|
||||
@@ -182,7 +178,7 @@ export function main() {
|
||||
it('should match the full URL using child components', inject([AsyncTestCompleter], (async) => {
|
||||
registry.config(RootHostCmp, new Route({path: '/first/...', component: DummyParentCmp}));
|
||||
|
||||
registry.recognize('/first/second', [RootHostCmp])
|
||||
registry.recognize('/first/second', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyParentCmp);
|
||||
expect(instruction.child.component.componentType).toBe(DummyCmpB);
|
||||
@@ -194,14 +190,11 @@ export function main() {
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
registry.config(RootHostCmp, new Route({path: '/first/...', component: DummyAsyncCmp}));
|
||||
|
||||
registry.recognize('/first/second', [RootHostCmp])
|
||||
registry.recognize('/first/second', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyAsyncCmp);
|
||||
|
||||
instruction.child.resolveComponent().then((childComponentInstruction) => {
|
||||
expect(childComponentInstruction.componentType).toBe(DummyCmpB);
|
||||
async.done();
|
||||
});
|
||||
expect(instruction.child.component.componentType).toBe(DummyCmpB);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -210,14 +203,11 @@ export function main() {
|
||||
registry.config(RootHostCmp,
|
||||
new AsyncRoute({path: '/first/...', loader: asyncParentLoader}));
|
||||
|
||||
registry.recognize('/first/second', [RootHostCmp])
|
||||
registry.recognize('/first/second', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyParentCmp);
|
||||
|
||||
instruction.child.resolveComponent().then((childType) => {
|
||||
expect(childType.componentType).toBe(DummyCmpB);
|
||||
async.done();
|
||||
});
|
||||
expect(instruction.child.component.componentType).toBe(DummyCmpB);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -252,15 +242,15 @@ export function main() {
|
||||
it('should throw when linkParams are not terminal', () => {
|
||||
registry.config(RootHostCmp,
|
||||
new Route({path: '/first/...', component: DummyParentCmp, name: 'First'}));
|
||||
expect(() => { registry.generate(['First'], [RootHostCmp]); })
|
||||
.toThrowError('Link "["First"]" does not resolve to a terminal instruction.');
|
||||
expect(() => { registry.generate(['First'], RootHostCmp); })
|
||||
.toThrowError('Link "["First"]" does not resolve to a terminal or async instruction.');
|
||||
});
|
||||
|
||||
it('should match matrix params on child components and query params on the root component',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
registry.config(RootHostCmp, new Route({path: '/first/...', component: DummyParentCmp}));
|
||||
|
||||
registry.recognize('/first/second;filter=odd?comments=all', [RootHostCmp])
|
||||
registry.recognize('/first/second;filter=odd?comments=all', RootHostCmp)
|
||||
.then((instruction) => {
|
||||
expect(instruction.component.componentType).toBe(DummyParentCmp);
|
||||
expect(instruction.component.params).toEqual({'comments': 'all'});
|
||||
@@ -286,18 +276,13 @@ export function main() {
|
||||
sort: 'asc',
|
||||
}
|
||||
],
|
||||
[RootHostCmp]));
|
||||
RootHostCmp));
|
||||
expect(url).toEqual('first/one/second/two;sort=asc?query=cats');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function stringifyInstruction(instruction): string {
|
||||
return instruction.toRootUrl();
|
||||
}
|
||||
|
||||
|
||||
function asyncParentLoader() {
|
||||
return PromiseWrapper.resolve(DummyParentCmp);
|
||||
}
|
||||
@@ -315,22 +300,26 @@ class DummyAsyncCmp {
|
||||
class DummyCmpA {}
|
||||
class DummyCmpB {}
|
||||
|
||||
@RouteConfig(
|
||||
[new Route({path: '/third', component: DummyCmpB, name: 'ThirdCmp', useAsDefault: true})])
|
||||
class DefaultRouteCmp {
|
||||
@RouteConfig([
|
||||
new Redirect({path: '/', redirectTo: '/third'}),
|
||||
new Route({path: '/third', component: DummyCmpB, name: 'ThirdCmp'})
|
||||
])
|
||||
class DummyRedirectCmp {
|
||||
}
|
||||
|
||||
|
||||
@RouteConfig([
|
||||
new Route(
|
||||
{path: '/second/...', component: DefaultRouteCmp, name: 'SecondCmp', useAsDefault: true})
|
||||
new Redirect({path: '/', redirectTo: '/second'}),
|
||||
new Route({path: '/second/...', component: DummyRedirectCmp, name: 'SecondCmp'})
|
||||
])
|
||||
class MultipleDefaultCmp {
|
||||
class DummyMultipleRedirectCmp {
|
||||
}
|
||||
|
||||
@RouteConfig(
|
||||
[new Route({path: '/second', component: DummyCmpB, name: 'SecondCmp', useAsDefault: true})])
|
||||
class ParentWithDefaultRouteCmp {
|
||||
@RouteConfig([
|
||||
new Redirect({path: '/', redirectTo: '/second'}),
|
||||
new Route({path: '/second', component: DummyCmpB, name: 'SecondCmp'})
|
||||
])
|
||||
class DummyParentRedirectCmp {
|
||||
}
|
||||
|
||||
@RouteConfig([new Route({path: '/second', component: DummyCmpB, name: 'SecondCmp'})])
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
beforeEachProviders,
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
TestComponentBuilder
|
||||
@@ -27,20 +27,24 @@ import {
|
||||
RouterOutlet,
|
||||
Route,
|
||||
RouteParams,
|
||||
Instruction,
|
||||
ComponentInstruction
|
||||
} from 'angular2/router';
|
||||
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {ResolvedInstruction} from 'angular2/src/router/instruction';
|
||||
import {ComponentInstruction_} from 'angular2/src/router/instruction';
|
||||
import {PathRecognizer} from 'angular2/src/router/path_recognizer';
|
||||
import {SyncRouteHandler} from 'angular2/src/router/sync_route_handler';
|
||||
|
||||
let dummyPathRecognizer = new PathRecognizer('', new SyncRouteHandler(null));
|
||||
let dummyInstruction =
|
||||
new ResolvedInstruction(new ComponentInstruction('detail', [], null, null, true, 0), null, {});
|
||||
new Instruction(new ComponentInstruction_('detail', [], dummyPathRecognizer), null, {});
|
||||
|
||||
export function main() {
|
||||
describe('router-link directive', function() {
|
||||
var tcb: TestComponentBuilder;
|
||||
|
||||
beforeEachProviders(() => [
|
||||
beforeEachBindings(() => [
|
||||
provide(Location, {useValue: makeDummyLocation()}),
|
||||
provide(Router, {useValue: makeDummyRouter()})
|
||||
]);
|
||||
@@ -102,6 +106,11 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp'})
|
||||
class MyComp {
|
||||
name;
|
||||
}
|
||||
|
||||
@Component({selector: 'user-cmp'})
|
||||
@View({template: "hello {{user}}"})
|
||||
class UserCmp {
|
||||
|
||||
@@ -18,6 +18,7 @@ import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {Router, RootRouter} from 'angular2/src/router/router';
|
||||
import {SpyLocation} from 'angular2/src/mock/location_mock';
|
||||
import {Location} from 'angular2/src/router/location';
|
||||
import {stringifyInstruction} from 'angular2/src/router/instruction';
|
||||
|
||||
import {RouteRegistry} from 'angular2/src/router/route_registry';
|
||||
import {RouteConfig, AsyncRoute, Route} from 'angular2/src/router/route_config_decorator';
|
||||
@@ -224,11 +225,6 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function stringifyInstruction(instruction): string {
|
||||
return instruction.toRootUrl();
|
||||
}
|
||||
|
||||
function loader(): Promise<Type> {
|
||||
return PromiseWrapper.resolve(DummyComponent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user