diff --git a/modules/angular1_router/build.js b/modules/angular1_router/build.js
index c6bfada381..ddaacbaa67 100644
--- a/modules/angular1_router/build.js
+++ b/modules/angular1_router/build.js
@@ -6,13 +6,12 @@ var ts = require('typescript');
var files = [
'lifecycle_annotations_impl.ts',
'url_parser.ts',
- 'route_recognizer.ts',
+ 'path_recognizer.ts',
'route_config_impl.ts',
'async_route_handler.ts',
'sync_route_handler.ts',
- 'component_recognizer.ts',
+ 'route_recognizer.ts',
'instruction.ts',
- 'path_recognizer.ts',
'route_config_nomalizer.ts',
'route_lifecycle_reflector.ts',
'route_registry.ts',
diff --git a/modules/angular1_router/lib/facades.es5 b/modules/angular1_router/lib/facades.es5
index b82d198056..f60f3b986d 100644
--- a/modules/angular1_router/lib/facades.es5
+++ b/modules/angular1_router/lib/facades.es5
@@ -173,10 +173,6 @@ var StringMapWrapper = {
var List = Array;
var ListWrapper = {
- clear: function (l) {
- l.length = 0;
- },
-
create: function () {
return [];
},
diff --git a/modules/angular1_router/src/module_template.js b/modules/angular1_router/src/module_template.js
index 0e25db57a4..aeed65ea3e 100644
--- a/modules/angular1_router/src/module_template.js
+++ b/modules/angular1_router/src/module_template.js
@@ -31,9 +31,7 @@ function routerFactory($q, $location, $$directiveIntrospector, $browser, $rootSc
// property in a route config
exports.assertComponentExists = function () {};
- angular.stringifyInstruction = function (instruction) {
- return instruction.toRootUrl();
- };
+ angular.stringifyInstruction = exports.stringifyInstruction;
var RouteRegistry = exports.RouteRegistry;
var RootRouter = exports.RootRouter;
diff --git a/modules/angular1_router/src/ng_route_shim.js b/modules/angular1_router/src/ng_route_shim.js
index 5ba7aa4fe7..0d3f245b28 100644
--- a/modules/angular1_router/src/ng_route_shim.js
+++ b/modules/angular1_router/src/ng_route_shim.js
@@ -110,7 +110,7 @@
routeMap[path] = routeCopy;
if (route.redirectTo) {
- routeDefinition.redirectTo = [routeMap[route.redirectTo].name];
+ routeDefinition.redirectTo = route.redirectTo;
} else {
if (routeCopy.controller && !routeCopy.controllerAs) {
console.warn('Route for "' + path + '" should use "controllerAs".');
@@ -123,7 +123,7 @@
}
routeDefinition.component = directiveName;
- routeDefinition.name = route.name || upperCase(directiveName);
+ routeDefinition.as = upperCase(directiveName);
var directiveController = routeCopy.controller;
diff --git a/modules/angular1_router/test/integration/navigation_spec.js b/modules/angular1_router/test/integration/navigation_spec.js
index 76b9490bab..1a82f197c6 100644
--- a/modules/angular1_router/test/integration/navigation_spec.js
+++ b/modules/angular1_router/test/integration/navigation_spec.js
@@ -113,7 +113,8 @@ describe('navigation', function () {
});
- it('should work with recursive nested outlets', function () {
+ // TODO: fix this
+ xit('should work with recursive nested outlets', function () {
registerComponent('recurCmp', {
template: '
',
$routeConfig: [
@@ -151,8 +152,8 @@ describe('navigation', function () {
compile('');
$router.config([
- { path: '/', redirectTo: ['/User'] },
- { path: '/user', component: 'userCmp', name: 'User' }
+ { path: '/', redirectTo: '/user' },
+ { path: '/user', component: 'userCmp' }
]);
$router.navigateByUrl('/');
@@ -166,15 +167,16 @@ describe('navigation', function () {
registerComponent('childRouter', {
template: '',
$routeConfig: [
- { path: '/new-child', component: 'oneCmp', name: 'NewChild'},
- { path: '/new-child-two', component: 'twoCmp', name: 'NewChildTwo'}
+ { path: '/old-child', redirectTo: '/new-child' },
+ { path: '/new-child', component: 'oneCmp'},
+ { path: '/old-child-two', redirectTo: '/new-child-two' },
+ { path: '/new-child-two', component: 'twoCmp'}
]
});
$router.config([
- { path: '/old-parent/old-child', redirectTo: ['/NewParent', 'NewChild'] },
- { path: '/old-parent/old-child-two', redirectTo: ['/NewParent', 'NewChildTwo'] },
- { path: '/new-parent/...', component: 'childRouter', name: 'NewParent' }
+ { path: '/old-parent', redirectTo: '/new-parent' },
+ { path: '/new-parent/...', component: 'childRouter' }
]);
compile('');
diff --git a/modules/angular1_router/test/integration/shim_spec.js b/modules/angular1_router/test/integration/shim_spec.js
index e2edffe552..f074bd50fd 100644
--- a/modules/angular1_router/test/integration/shim_spec.js
+++ b/modules/angular1_router/test/integration/shim_spec.js
@@ -139,12 +139,11 @@ describe('ngRoute shim', function () {
it('should adapt routes with redirects', inject(function ($location) {
$routeProvider
- .when('/home', {
- template: 'welcome home!',
- name: 'Home'
- })
.when('/', {
redirectTo: '/home'
+ })
+ .when('/home', {
+ template: 'welcome home!'
});
$rootScope.$digest();
diff --git a/modules/angular2/src/router/async_route_handler.ts b/modules/angular2/src/router/async_route_handler.ts
index a739352151..6e22218344 100644
--- a/modules/angular2/src/router/async_route_handler.ts
+++ b/modules/angular2/src/router/async_route_handler.ts
@@ -1,19 +1,13 @@
+import {RouteHandler} from './route_handler';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {isPresent, Type} from 'angular2/src/facade/lang';
-import {RouteHandler} from './route_handler';
-import {RouteData, BLANK_ROUTE_DATA} from './instruction';
-
-
export class AsyncRouteHandler implements RouteHandler {
/** @internal */
_resolvedComponent: Promise = null;
componentType: Type;
- public data: RouteData;
- constructor(private _loader: Function, data: {[key: string]: any} = null) {
- this.data = isPresent(data) ? new RouteData(data) : BLANK_ROUTE_DATA;
- }
+ constructor(private _loader: Function, public data?: {[key: string]: any}) {}
resolveComponentType(): Promise {
if (isPresent(this._resolvedComponent)) {
diff --git a/modules/angular2/src/router/component_recognizer.ts b/modules/angular2/src/router/component_recognizer.ts
deleted file mode 100644
index 56730566fd..0000000000
--- a/modules/angular2/src/router/component_recognizer.ts
+++ /dev/null
@@ -1,157 +0,0 @@
-import {isBlank, isPresent} from 'angular2/src/facade/lang';
-import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
-import {Map, MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
-
-import {
- AbstractRecognizer,
- RouteRecognizer,
- RedirectRecognizer,
- RouteMatch
-} from './route_recognizer';
-import {Route, AsyncRoute, AuxRoute, Redirect, RouteDefinition} from './route_config_impl';
-import {AsyncRouteHandler} from './async_route_handler';
-import {SyncRouteHandler} from './sync_route_handler';
-import {Url} from './url_parser';
-import {ComponentInstruction} from './instruction';
-
-
-/**
- * `ComponentRecognizer` is responsible for recognizing routes for a single component.
- * It is consumed by `RouteRegistry`, which knows how to recognize an entire hierarchy of
- * components.
- */
-export class ComponentRecognizer {
- names = new Map();
-
- // map from name to recognizer
- auxNames = new Map();
-
- // map from starting path to recognizer
- auxRoutes = new Map();
-
- // TODO: optimize this into a trie
- matchers: AbstractRecognizer[] = [];
-
- defaultRoute: RouteRecognizer = null;
-
- /**
- * returns whether or not the config is terminal
- */
- config(config: RouteDefinition): boolean {
- var handler;
-
- if (isPresent(config.name) && config.name[0].toUpperCase() != config.name[0]) {
- var suggestedName = config.name[0].toUpperCase() + config.name.substring(1);
- throw new BaseException(
- `Route "${config.path}" with name "${config.name}" does not begin with an uppercase letter. Route names should be CamelCase like "${suggestedName}".`);
- }
-
- if (config instanceof AuxRoute) {
- handler = new SyncRouteHandler(config.component, config.data);
- let path = config.path.startsWith('/') ? config.path.substring(1) : config.path;
- var recognizer = new RouteRecognizer(config.path, handler);
- this.auxRoutes.set(path, recognizer);
- if (isPresent(config.name)) {
- this.auxNames.set(config.name, recognizer);
- }
- return recognizer.terminal;
- }
-
- var useAsDefault = false;
-
- if (config instanceof Redirect) {
- let redirector = new RedirectRecognizer(config.path, config.redirectTo);
- this._assertNoHashCollision(redirector.hash, config.path);
- this.matchers.push(redirector);
- return true;
- }
-
- if (config instanceof Route) {
- handler = new SyncRouteHandler(config.component, config.data);
- useAsDefault = isPresent(config.useAsDefault) && config.useAsDefault;
- } else if (config instanceof AsyncRoute) {
- handler = new AsyncRouteHandler(config.loader, config.data);
- useAsDefault = isPresent(config.useAsDefault) && config.useAsDefault;
- }
- var recognizer = new RouteRecognizer(config.path, handler);
-
- this._assertNoHashCollision(recognizer.hash, config.path);
-
- if (useAsDefault) {
- if (isPresent(this.defaultRoute)) {
- throw new BaseException(`Only one route can be default`);
- }
- this.defaultRoute = recognizer;
- }
-
- this.matchers.push(recognizer);
- if (isPresent(config.name)) {
- this.names.set(config.name, recognizer);
- }
- return recognizer.terminal;
- }
-
-
- private _assertNoHashCollision(hash: string, path) {
- this.matchers.forEach((matcher) => {
- if (hash == matcher.hash) {
- throw new BaseException(
- `Configuration '${path}' conflicts with existing route '${matcher.path}'`);
- }
- });
- }
-
-
- /**
- * Given a URL, returns a list of `RouteMatch`es, which are partial recognitions for some route.
- */
- recognize(urlParse: Url): Promise[] {
- var solutions = [];
-
- this.matchers.forEach((routeRecognizer: AbstractRecognizer) => {
- var pathMatch = routeRecognizer.recognize(urlParse);
-
- if (isPresent(pathMatch)) {
- solutions.push(pathMatch);
- }
- });
-
- return solutions;
- }
-
- recognizeAuxiliary(urlParse: Url): Promise[] {
- var routeRecognizer: RouteRecognizer = this.auxRoutes.get(urlParse.path);
- if (isPresent(routeRecognizer)) {
- return [routeRecognizer.recognize(urlParse)];
- }
-
- return [PromiseWrapper.resolve(null)];
- }
-
- hasRoute(name: string): boolean { return this.names.has(name); }
-
- componentLoaded(name: string): boolean {
- return this.hasRoute(name) && isPresent(this.names.get(name).handler.componentType);
- }
-
- loadComponent(name: string): Promise {
- return this.names.get(name).handler.resolveComponentType();
- }
-
- generate(name: string, params: any): ComponentInstruction {
- var pathRecognizer: RouteRecognizer = this.names.get(name);
- if (isBlank(pathRecognizer)) {
- return null;
- }
- return pathRecognizer.generate(params);
- }
-
- generateAuxiliary(name: string, params: any): ComponentInstruction {
- var pathRecognizer: RouteRecognizer = this.auxNames.get(name);
- if (isBlank(pathRecognizer)) {
- return null;
- }
- return pathRecognizer.generate(params);
- }
-}
diff --git a/modules/angular2/src/router/instruction.ts b/modules/angular2/src/router/instruction.ts
index 13e4e9f192..760394c65e 100644
--- a/modules/angular2/src/router/instruction.ts
+++ b/modules/angular2/src/router/instruction.ts
@@ -1,7 +1,10 @@
import {Map, MapWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
+import {unimplemented} from 'angular2/src/facade/exceptions';
import {isPresent, isBlank, normalizeBlank, Type, CONST_EXPR} from 'angular2/src/facade/lang';
-import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
+import {Promise} from 'angular2/src/facade/async';
+import {PathRecognizer} from './path_recognizer';
+import {Url} from './url_parser';
/**
* `RouteParams` is an immutable map of parameters for the given route
@@ -74,7 +77,7 @@ export class RouteData {
get(key: string): any { return normalizeBlank(StringMapWrapper.get(this.data, key)); }
}
-export var BLANK_ROUTE_DATA = new RouteData();
+var BLANK_ROUTE_DATA = new RouteData();
/**
* `Instruction` is a tree of {@link ComponentInstruction}s with all the information needed
@@ -103,184 +106,74 @@ export var BLANK_ROUTE_DATA = new RouteData();
* bootstrap(AppCmp, ROUTER_PROVIDERS);
* ```
*/
-export abstract class Instruction {
- public component: ComponentInstruction;
- public child: Instruction;
- public auxInstruction: {[key: string]: Instruction} = {};
-
- get urlPath(): string { return this.component.urlPath; }
-
- get urlParams(): string[] { return this.component.urlParams; }
-
- get specificity(): number {
- var total = 0;
- if (isPresent(this.component)) {
- total += this.component.specificity;
- }
- if (isPresent(this.child)) {
- total += this.child.specificity;
- }
- return total;
- }
-
- abstract resolveComponent(): Promise;
-
- /**
- * converts the instruction into a URL string
- */
- toRootUrl(): string { return this.toUrlPath() + this.toUrlQuery(); }
-
- /** @internal */
- _toNonRootUrl(): string {
- return this._stringifyPathMatrixAuxPrefixed() +
- (isPresent(this.child) ? this.child._toNonRootUrl() : '');
- }
-
- toUrlQuery(): string { return this.urlParams.length > 0 ? ('?' + this.urlParams.join('&')) : ''; }
+export class Instruction {
+ constructor(public component: ComponentInstruction, public child: Instruction,
+ public auxInstruction: {[key: string]: Instruction}) {}
/**
* Returns a new instruction that shares the state of the existing instruction, but with
* the given child {@link Instruction} replacing the existing child.
*/
replaceChild(child: Instruction): Instruction {
- return new ResolvedInstruction(this.component, child, this.auxInstruction);
+ return new Instruction(this.component, child, this.auxInstruction);
}
+}
- /**
- * If the final URL for the instruction is ``
- */
- toUrlPath(): string {
- return this.urlPath + this._stringifyAux() +
- (isPresent(this.child) ? this.child._toNonRootUrl() : '');
+/**
+ * Represents a partially completed instruction during recognition that only has the
+ * primary (non-aux) route instructions matched.
+ *
+ * `PrimaryInstruction` is an internal class used by `RouteRecognizer` while it's
+ * figuring out where to navigate.
+ */
+export class PrimaryInstruction {
+ constructor(public component: ComponentInstruction, public child: PrimaryInstruction,
+ public auxUrls: Url[]) {}
+}
+
+export function stringifyInstruction(instruction: Instruction): string {
+ return stringifyInstructionPath(instruction) + stringifyInstructionQuery(instruction);
+}
+
+export function stringifyInstructionPath(instruction: Instruction): string {
+ return instruction.component.urlPath + stringifyAux(instruction) +
+ stringifyPrimaryPrefixed(instruction.child);
+}
+
+export function stringifyInstructionQuery(instruction: Instruction): string {
+ return instruction.component.urlParams.length > 0 ?
+ ('?' + instruction.component.urlParams.join('&')) :
+ '';
+}
+
+function stringifyPrimaryPrefixed(instruction: Instruction): string {
+ var primary = stringifyPrimary(instruction);
+ if (primary.length > 0) {
+ primary = '/' + primary;
}
+ return primary;
+}
- // default instructions override these
- toLinkUrl(): string {
- return this.urlPath + this._stringifyAux() +
- (isPresent(this.child) ? this.child._toLinkUrl() : '');
- }
-
- // this is the non-root version (called recursively)
- /** @internal */
- _toLinkUrl(): string {
- return this._stringifyPathMatrixAuxPrefixed() +
- (isPresent(this.child) ? this.child._toLinkUrl() : '');
- }
-
- /** @internal */
- _stringifyPathMatrixAuxPrefixed(): string {
- var primary = this._stringifyPathMatrixAux();
- if (primary.length > 0) {
- primary = '/' + primary;
- }
- return primary;
- }
-
- /** @internal */
- _stringifyMatrixParams(): string {
- return this.urlParams.length > 0 ? (';' + this.component.urlParams.join(';')) : '';
- }
-
- /** @internal */
- _stringifyPathMatrixAux(): string {
- if (isBlank(this.component)) {
- return '';
- }
- return this.urlPath + this._stringifyMatrixParams() + this._stringifyAux();
- }
-
- /** @internal */
- _stringifyAux(): string {
- var routes = [];
- StringMapWrapper.forEach(this.auxInstruction, (auxInstruction, _) => {
- routes.push(auxInstruction._stringifyPathMatrixAux());
- });
- if (routes.length > 0) {
- return '(' + routes.join('//') + ')';
- }
+function stringifyPrimary(instruction: Instruction): string {
+ if (isBlank(instruction)) {
return '';
}
+ var params = instruction.component.urlParams.length > 0 ?
+ (';' + instruction.component.urlParams.join(';')) :
+ '';
+ return instruction.component.urlPath + params + stringifyAux(instruction) +
+ stringifyPrimaryPrefixed(instruction.child);
}
-
-/**
- * a resolved instruction has an outlet instruction for itself, but maybe not for...
- */
-export class ResolvedInstruction extends Instruction {
- constructor(public component: ComponentInstruction, public child: Instruction,
- public auxInstruction: {[key: string]: Instruction}) {
- super();
- }
-
- resolveComponent(): Promise {
- return PromiseWrapper.resolve(this.component);
- }
-}
-
-
-/**
- * Represents a resolved default route
- */
-export class DefaultInstruction extends Instruction {
- constructor(public component: ComponentInstruction, public child: DefaultInstruction) { super(); }
-
- resolveComponent(): Promise {
- return PromiseWrapper.resolve(this.component);
- }
-
- toLinkUrl(): string { return ''; }
-
- /** @internal */
- _toLinkUrl(): string { return ''; }
-}
-
-
-/**
- * Represents a component that may need to do some redirection or lazy loading at a later time.
- */
-export class UnresolvedInstruction extends Instruction {
- constructor(private _resolver: () => Promise, private _urlPath: string = '',
- private _urlParams: string[] = CONST_EXPR([])) {
- super();
- }
-
- get urlPath(): string {
- if (isPresent(this.component)) {
- return this.component.urlPath;
- }
- if (isPresent(this._urlPath)) {
- return this._urlPath;
- }
- return '';
- }
-
- get urlParams(): string[] {
- if (isPresent(this.component)) {
- return this.component.urlParams;
- }
- if (isPresent(this._urlParams)) {
- return this._urlParams;
- }
- return [];
- }
-
- resolveComponent(): Promise {
- if (isPresent(this.component)) {
- return PromiseWrapper.resolve(this.component);
- }
- return this._resolver().then((resolution: Instruction) => {
- this.child = resolution.child;
- return this.component = resolution.component;
- });
- }
-}
-
-
-export class RedirectInstruction extends ResolvedInstruction {
- constructor(component: ComponentInstruction, child: Instruction,
- auxInstruction: {[key: string]: Instruction}) {
- super(component, child, auxInstruction);
+function stringifyAux(instruction: Instruction): string {
+ var routes = [];
+ StringMapWrapper.forEach(instruction.auxInstruction, (auxInstruction, _) => {
+ routes.push(stringifyPrimary(auxInstruction));
+ });
+ if (routes.length > 0) {
+ return '(' + routes.join('//') + ')';
}
+ return '';
}
@@ -292,18 +185,67 @@ export class RedirectInstruction extends ResolvedInstruction {
* to route lifecycle hooks, like {@link CanActivate}.
*
* `ComponentInstruction`s are [https://en.wikipedia.org/wiki/Hash_consing](hash consed). You should
- * never construct one yourself with "new." Instead, rely on {@link Router/RouteRecognizer} to
+ * never construct one yourself with "new." Instead, rely on {@link Router/PathRecognizer} to
* construct `ComponentInstruction`s.
*
* You should not modify this object. It should be treated as immutable.
*/
-export class ComponentInstruction {
+export abstract class ComponentInstruction {
reuse: boolean = false;
- public routeData: RouteData;
+ public urlPath: string;
+ public urlParams: string[];
+ public params: {[key: string]: any};
- constructor(public urlPath: string, public urlParams: string[], data: RouteData,
- public componentType, public terminal: boolean, public specificity: number,
- public params: {[key: string]: any} = null) {
- this.routeData = isPresent(data) ? data : BLANK_ROUTE_DATA;
- }
+ /**
+ * Returns the component type of the represented route, or `null` if this instruction
+ * hasn't been resolved.
+ */
+ get componentType() { return unimplemented(); };
+
+ /**
+ * Returns a promise that will resolve to component type of the represented route.
+ * If this instruction references an {@link AsyncRoute}, the `loader` function of that route
+ * will run.
+ */
+ abstract resolveComponentType(): Promise;
+
+ /**
+ * Returns the specificity of the route associated with this `Instruction`.
+ */
+ get specificity() { return unimplemented(); };
+
+ /**
+ * Returns `true` if the component type of this instruction has no child {@link RouteConfig},
+ * or `false` if it does.
+ */
+ get terminal() { return unimplemented(); };
+
+ /**
+ * Returns the route data of the given route that was specified in the {@link RouteDefinition},
+ * or an empty object if no route data was specified.
+ */
+ get routeData(): RouteData { return unimplemented(); };
+}
+
+export class ComponentInstruction_ extends ComponentInstruction {
+ private _routeData: RouteData;
+
+ constructor(urlPath: string, urlParams: string[], private _recognizer: PathRecognizer,
+ params: {[key: string]: any} = null) {
+ super();
+ this.urlPath = urlPath;
+ this.urlParams = urlParams;
+ this.params = params;
+ if (isPresent(this._recognizer.handler.data)) {
+ this._routeData = new RouteData(this._recognizer.handler.data);
+ } else {
+ this._routeData = BLANK_ROUTE_DATA;
+ }
+ }
+
+ get componentType() { return this._recognizer.handler.componentType; }
+ resolveComponentType(): Promise { return this._recognizer.handler.resolveComponentType(); }
+ get specificity() { return this._recognizer.specificity; }
+ get terminal() { return this._recognizer.terminal; }
+ get routeData(): RouteData { return this._routeData; }
}
diff --git a/modules/angular2/src/router/path_recognizer.ts b/modules/angular2/src/router/path_recognizer.ts
index 1e03e1271d..01e84e0cc0 100644
--- a/modules/angular2/src/router/path_recognizer.ts
+++ b/modules/angular2/src/router/path_recognizer.ts
@@ -7,9 +7,12 @@ import {
isBlank
} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
+
import {Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
+import {RouteHandler} from './route_handler';
import {Url, RootUrl, serializeParams} from './url_parser';
+import {ComponentInstruction, ComponentInstruction_} from './instruction';
class TouchMap {
map: {[key: string]: string} = {};
@@ -30,7 +33,7 @@ class TouchMap {
}
getUnused(): {[key: string]: any} {
- var unused: {[key: string]: any} = {};
+ var unused: {[key: string]: any} = StringMapWrapper.create();
var keys = StringMapWrapper.keys(this.keys);
keys.forEach(key => unused[key] = StringMapWrapper.get(this.map, key));
return unused;
@@ -123,6 +126,7 @@ function parsePathString(route: string): {[key: string]: any} {
results.push(new StarSegment(match[1]));
} else if (segment == '...') {
if (i < limit) {
+ // TODO (matsko): setup a proper error here `
throw new BaseException(`Unexpected "..." before the end of the path for "${route}".`);
}
results.push(new ContinuationSegment());
@@ -171,17 +175,23 @@ function assertPath(path: string) {
}
}
+export class PathMatch {
+ constructor(public instruction: ComponentInstruction, public remaining: Url,
+ public remainingAux: Url[]) {}
+}
-/**
- * Parses a URL string using a given matcher DSL, and generates URLs from param maps
- */
+// represents something like '/foo/:bar'
export class PathRecognizer {
private _segments: Segment[];
specificity: number;
terminal: boolean = true;
hash: string;
+ private _cache: Map = new Map();
- constructor(public path: string) {
+
+ // TODO: cache component instruction instances by params and by ParsedUrl instance
+
+ constructor(public path: string, public handler: RouteHandler) {
assertPath(path);
var parsed = parsePathString(path);
@@ -193,7 +203,8 @@ export class PathRecognizer {
this.terminal = !(lastSegment instanceof ContinuationSegment);
}
- recognize(beginningSegment: Url): {[key: string]: any} {
+
+ recognize(beginningSegment: Url): PathMatch {
var nextSegment = beginningSegment;
var currentSegment: Url;
var positionalParams = {};
@@ -236,6 +247,7 @@ export class PathRecognizer {
var urlPath = captured.join('/');
var auxiliary;
+ var instruction: ComponentInstruction;
var urlParams;
var allParams;
if (isPresent(currentSegment)) {
@@ -255,11 +267,12 @@ export class PathRecognizer {
auxiliary = [];
urlParams = [];
}
- return {urlPath, urlParams, allParams, auxiliary, nextSegment};
+ instruction = this._getInstruction(urlPath, urlParams, this, allParams);
+ return new PathMatch(instruction, nextSegment, auxiliary);
}
- generate(params: {[key: string]: any}): {[key: string]: any} {
+ generate(params: {[key: string]: any}): ComponentInstruction {
var paramTokens = new TouchMap(params);
var path = [];
@@ -275,6 +288,18 @@ export class PathRecognizer {
var nonPositionalParams = paramTokens.getUnused();
var urlParams = serializeParams(nonPositionalParams);
- return {urlPath, urlParams};
+ return this._getInstruction(urlPath, urlParams, this, params);
+ }
+
+ private _getInstruction(urlPath: string, urlParams: string[], _recognizer: PathRecognizer,
+ params: {[key: string]: any}): ComponentInstruction {
+ var hashKey = urlPath + '?' + urlParams.join('?');
+ if (this._cache.has(hashKey)) {
+ return this._cache.get(hashKey);
+ }
+ var instruction = new ComponentInstruction_(urlPath, urlParams, _recognizer, params);
+ this._cache.set(hashKey, instruction);
+
+ return instruction;
}
}
diff --git a/modules/angular2/src/router/route_config_impl.ts b/modules/angular2/src/router/route_config_impl.ts
index b52de354df..b56fb0103f 100644
--- a/modules/angular2/src/router/route_config_impl.ts
+++ b/modules/angular2/src/router/route_config_impl.ts
@@ -21,8 +21,6 @@ export class RouteConfig {
* - `name` is an optional `CamelCase` string representing the name of the route.
* - `data` is an optional property of any type representing arbitrary route metadata for the given
* route. It is injectable via {@link RouteData}.
- * - `useAsDefault` is a boolean value. If `true`, the child route will be navigated to if no child
- * route is specified during the navigation.
*
* ### Example
* ```
@@ -40,20 +38,16 @@ export class Route implements RouteDefinition {
path: string;
component: Type;
name: string;
- useAsDefault: boolean;
// added next three properties to work around https://github.com/Microsoft/TypeScript/issues/4107
aux: string = null;
loader: Function = null;
- redirectTo: any[] = null;
- constructor({path, component, name, data, useAsDefault}: {
- path: string,
- component: Type, name?: string, data?: {[key: string]: any}, useAsDefault?: boolean
- }) {
+ redirectTo: string = null;
+ constructor({path, component, name,
+ data}: {path: string, component: Type, name?: string, data?: {[key: string]: any}}) {
this.path = path;
this.component = component;
this.name = name;
this.data = data;
- this.useAsDefault = useAsDefault;
}
}
@@ -86,8 +80,7 @@ export class AuxRoute implements RouteDefinition {
// added next three properties to work around https://github.com/Microsoft/TypeScript/issues/4107
aux: string = null;
loader: Function = null;
- redirectTo: any[] = null;
- useAsDefault: boolean = false;
+ redirectTo: string = null;
constructor({path, component, name}: {path: string, component: Type, name?: string}) {
this.path = path;
this.component = component;
@@ -105,8 +98,6 @@ export class AuxRoute implements RouteDefinition {
* - `name` is an optional `CamelCase` string representing the name of the route.
* - `data` is an optional property of any type representing arbitrary route metadata for the given
* route. It is injectable via {@link RouteData}.
- * - `useAsDefault` is a boolean value. If `true`, the child route will be navigated to if no child
- * route is specified during the navigation.
*
* ### Example
* ```
@@ -124,37 +115,31 @@ export class AsyncRoute implements RouteDefinition {
path: string;
loader: Function;
name: string;
- useAsDefault: boolean;
aux: string = null;
- constructor({path, loader, name, data, useAsDefault}: {
- path: string,
- loader: Function, name?: string, data?: {[key: string]: any}, useAsDefault?: boolean
- }) {
+ constructor({path, loader, name, data}:
+ {path: string, loader: Function, name?: string, data?: {[key: string]: any}}) {
this.path = path;
this.loader = loader;
this.name = name;
this.data = data;
- this.useAsDefault = useAsDefault;
}
}
/**
- * `Redirect` is a type of {@link RouteDefinition} used to route a path to a canonical route.
+ * `Redirect` is a type of {@link RouteDefinition} used to route a path to an asynchronously loaded
+ * component.
*
* It has the following properties:
* - `path` is a string that uses the route matcher DSL.
- * - `redirectTo` is an array representing the link DSL.
- *
- * Note that redirects **do not** affect how links are generated. For that, see the `useAsDefault`
- * option.
+ * - `redirectTo` is a string representing the new URL to be matched against.
*
* ### Example
* ```
* import {RouteConfig} from 'angular2/router';
*
* @RouteConfig([
- * {path: '/', redirectTo: ['/Home'] },
- * {path: '/home', component: HomeCmp, name: 'Home'}
+ * {path: '/', redirectTo: '/home'},
+ * {path: '/home', component: HomeCmp}
* ])
* class MyApp {}
* ```
@@ -162,14 +147,13 @@ export class AsyncRoute implements RouteDefinition {
@CONST()
export class Redirect implements RouteDefinition {
path: string;
- redirectTo: any[];
+ redirectTo: string;
name: string = null;
// added next three properties to work around https://github.com/Microsoft/TypeScript/issues/4107
loader: Function = null;
data: any = null;
aux: string = null;
- useAsDefault: boolean = false;
- constructor({path, redirectTo}: {path: string, redirectTo: any[]}) {
+ constructor({path, redirectTo}: {path: string, redirectTo: string}) {
this.path = path;
this.redirectTo = redirectTo;
}
diff --git a/modules/angular2/src/router/route_config_nomalizer.dart b/modules/angular2/src/router/route_config_nomalizer.dart
index 6fe053e62d..2d3005295b 100644
--- a/modules/angular2/src/router/route_config_nomalizer.dart
+++ b/modules/angular2/src/router/route_config_nomalizer.dart
@@ -1,22 +1,9 @@
library angular2.src.router.route_config_normalizer;
import "route_config_decorator.dart";
-import "route_registry.dart";
import "package:angular2/src/facade/exceptions.dart" show BaseException;
-RouteDefinition normalizeRouteConfig(RouteDefinition config, RouteRegistry registry) {
- if (config is AsyncRoute) {
-
- configRegistryAndReturnType(componentType) {
- registry.configFromComponent(componentType);
- return componentType;
- }
-
- loader() {
- return config.loader().then(configRegistryAndReturnType);
- }
- return new AsyncRoute(path: config.path, loader: loader, name: config.name, data: config.data, useAsDefault: config.useAsDefault);
- }
+RouteDefinition normalizeRouteConfig(RouteDefinition config) {
return config;
}
diff --git a/modules/angular2/src/router/route_config_nomalizer.ts b/modules/angular2/src/router/route_config_nomalizer.ts
index 16518fa79f..8d0725dbab 100644
--- a/modules/angular2/src/router/route_config_nomalizer.ts
+++ b/modules/angular2/src/router/route_config_nomalizer.ts
@@ -2,29 +2,14 @@ import {AsyncRoute, AuxRoute, Route, Redirect, RouteDefinition} from './route_co
import {ComponentDefinition} from './route_definition';
import {isType, Type} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
-import {RouteRegistry} from './route_registry';
/**
- * Given a JS Object that represents a route config, returns a corresponding Route, AsyncRoute,
- * AuxRoute or Redirect object.
- *
- * Also wraps an AsyncRoute's loader function to add the loaded component's route config to the
- * `RouteRegistry`.
+ * Given a JS Object that represents... returns a corresponding Route, AsyncRoute, or Redirect
*/
-export function normalizeRouteConfig(config: RouteDefinition,
- registry: RouteRegistry): RouteDefinition {
- if (config instanceof AsyncRoute) {
- var wrappedLoader = wrapLoaderToReconfigureRegistry(config.loader, registry);
- return new AsyncRoute({
- path: config.path,
- loader: wrappedLoader,
- name: config.name,
- data: config.data,
- useAsDefault: config.useAsDefault
- });
- }
- if (config instanceof Route || config instanceof Redirect || config instanceof AuxRoute) {
+export function normalizeRouteConfig(config: RouteDefinition): RouteDefinition {
+ if (config instanceof Route || config instanceof Redirect || config instanceof AsyncRoute ||
+ config instanceof AuxRoute) {
return config;
}
@@ -39,13 +24,7 @@ export function normalizeRouteConfig(config: RouteDefinition,
config.name = config.as;
}
if (config.loader) {
- var wrappedLoader = wrapLoaderToReconfigureRegistry(config.loader, registry);
- return new AsyncRoute({
- path: config.path,
- loader: wrappedLoader,
- name: config.name,
- useAsDefault: config.useAsDefault
- });
+ return new AsyncRoute({path: config.path, loader: config.loader, name: config.name});
}
if (config.aux) {
return new AuxRoute({path: config.aux, component:config.component, name: config.name});
@@ -57,17 +36,11 @@ export function normalizeRouteConfig(config: RouteDefinition,
return new Route({
path: config.path,
component:componentDefinitionObject.constructor,
- name: config.name,
- data: config.data,
- useAsDefault: config.useAsDefault
+ name: config.name
});
} else if (componentDefinitionObject.type == 'loader') {
- return new AsyncRoute({
- path: config.path,
- loader: componentDefinitionObject.loader,
- name: config.name,
- useAsDefault: config.useAsDefault
- });
+ return new AsyncRoute(
+ {path: config.path, loader: componentDefinitionObject.loader, name: config.name});
} else {
throw new BaseException(
`Invalid component type "${componentDefinitionObject.type}". Valid types are "constructor" and "loader".`);
@@ -77,8 +50,6 @@ export function normalizeRouteConfig(config: RouteDefinition,
path: string;
component: Type;
name?: string;
- data?: {[key: string]: any};
- useAsDefault?: boolean;
}>config);
}
@@ -89,16 +60,6 @@ export function normalizeRouteConfig(config: RouteDefinition,
return config;
}
-
-function wrapLoaderToReconfigureRegistry(loader: Function, registry: RouteRegistry): Function {
- return () => {
- return loader().then((componentType) => {
- registry.configFromComponent(componentType);
- return componentType;
- });
- };
-}
-
export function assertComponentExists(component: Type, path: string): void {
if (!isType(component)) {
throw new BaseException(`Component for route "${path}" is not defined, or is not a class.`);
diff --git a/modules/angular2/src/router/route_definition.dart b/modules/angular2/src/router/route_definition.dart
index 79c8b5672b..e5d0a22539 100644
--- a/modules/angular2/src/router/route_definition.dart
+++ b/modules/angular2/src/router/route_definition.dart
@@ -3,6 +3,5 @@ library angular2.src.router.route_definition;
abstract class RouteDefinition {
final String path;
final String name;
- final bool useAsDefault;
- const RouteDefinition({this.path, this.name, this.useAsDefault : false});
+ const RouteDefinition({this.path, this.name});
}
diff --git a/modules/angular2/src/router/route_definition.ts b/modules/angular2/src/router/route_definition.ts
index 7d38690ee1..ee1266143d 100644
--- a/modules/angular2/src/router/route_definition.ts
+++ b/modules/angular2/src/router/route_definition.ts
@@ -16,11 +16,10 @@ export interface RouteDefinition {
aux?: string;
component?: Type | ComponentDefinition;
loader?: Function;
- redirectTo?: any[];
+ redirectTo?: string;
as?: string;
name?: string;
data?: any;
- useAsDefault?: boolean;
}
export interface ComponentDefinition {
diff --git a/modules/angular2/src/router/route_handler.ts b/modules/angular2/src/router/route_handler.ts
index 5971267ee8..54ca3b2ef0 100644
--- a/modules/angular2/src/router/route_handler.ts
+++ b/modules/angular2/src/router/route_handler.ts
@@ -1,9 +1,8 @@
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {Type} from 'angular2/src/facade/lang';
-import {RouteData} from './instruction';
export interface RouteHandler {
componentType: Type;
resolveComponentType(): Promise;
- data: RouteData;
+ data?: {[key: string]: any};
}
diff --git a/modules/angular2/src/router/route_recognizer.ts b/modules/angular2/src/router/route_recognizer.ts
index fb0b8973e9..6511fadda7 100644
--- a/modules/angular2/src/router/route_recognizer.ts
+++ b/modules/angular2/src/router/route_recognizer.ts
@@ -1,119 +1,184 @@
-import {isPresent, isBlank} from 'angular2/src/facade/lang';
-import {BaseException} from 'angular2/src/facade/exceptions';
-import {PromiseWrapper, Promise} from 'angular2/src/facade/promise';
-import {Map} from 'angular2/src/facade/collection';
+import {
+ RegExp,
+ RegExpWrapper,
+ isBlank,
+ isPresent,
+ isType,
+ isStringMap,
+ Type
+} from 'angular2/src/facade/lang';
+import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
+import {Map, MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
-import {RouteHandler} from './route_handler';
+import {PathRecognizer, PathMatch} from './path_recognizer';
+import {Route, AsyncRoute, AuxRoute, Redirect, RouteDefinition} from './route_config_impl';
+import {AsyncRouteHandler} from './async_route_handler';
+import {SyncRouteHandler} from './sync_route_handler';
import {Url} from './url_parser';
import {ComponentInstruction} from './instruction';
-import {PathRecognizer} from './path_recognizer';
-export abstract class RouteMatch {}
+/**
+ * `RouteRecognizer` is responsible for recognizing routes for a single component.
+ * It is consumed by `RouteRegistry`, which knows how to recognize an entire hierarchy of
+ * components.
+ */
+export class RouteRecognizer {
+ names = new Map();
-export interface AbstractRecognizer {
- hash: string;
- path: string;
- recognize(beginningSegment: Url): Promise;
- generate(params: {[key: string]: any}): ComponentInstruction;
-}
+ // map from name to recognizer
+ auxNames = new Map();
+
+ // map from starting path to recognizer
+ auxRoutes = new Map();
+
+ // TODO: optimize this into a trie
+ matchers: PathRecognizer[] = [];
+
+ // TODO: optimize this into a trie
+ redirects: Redirector[] = [];
+
+ config(config: RouteDefinition): boolean {
+ var handler;
+
+ if (isPresent(config.name) && config.name[0].toUpperCase() != config.name[0]) {
+ var suggestedName = config.name[0].toUpperCase() + config.name.substring(1);
+ throw new BaseException(
+ `Route "${config.path}" with name "${config.name}" does not begin with an uppercase letter. Route names should be CamelCase like "${suggestedName}".`);
+ }
+
+ if (config instanceof AuxRoute) {
+ handler = new SyncRouteHandler(config.component, config.data);
+ let path = config.path.startsWith('/') ? config.path.substring(1) : config.path;
+ var recognizer = new PathRecognizer(config.path, handler);
+ this.auxRoutes.set(path, recognizer);
+ if (isPresent(config.name)) {
+ this.auxNames.set(config.name, recognizer);
+ }
+ return recognizer.terminal;
+ }
+
+ if (config instanceof Redirect) {
+ this.redirects.push(new Redirector(config.path, config.redirectTo));
+ return true;
+ }
+
+ if (config instanceof Route) {
+ handler = new SyncRouteHandler(config.component, config.data);
+ } else if (config instanceof AsyncRoute) {
+ handler = new AsyncRouteHandler(config.loader, config.data);
+ }
+ var recognizer = new PathRecognizer(config.path, handler);
+
+ this.matchers.forEach((matcher) => {
+ if (recognizer.hash == matcher.hash) {
+ throw new BaseException(
+ `Configuration '${config.path}' conflicts with existing route '${matcher.path}'`);
+ }
+ });
+
+ this.matchers.push(recognizer);
+ if (isPresent(config.name)) {
+ this.names.set(config.name, recognizer);
+ }
+ return recognizer.terminal;
+ }
-export class PathMatch extends RouteMatch {
- constructor(public instruction: ComponentInstruction, public remaining: Url,
- public remainingAux: Url[]) {
- super();
+ /**
+ * Given a URL, returns a list of `RouteMatch`es, which are partial recognitions for some route.
+ *
+ */
+ recognize(urlParse: Url): PathMatch[] {
+ var solutions = [];
+
+ urlParse = this._redirect(urlParse);
+
+ this.matchers.forEach((pathRecognizer: PathRecognizer) => {
+ var pathMatch = pathRecognizer.recognize(urlParse);
+
+ if (isPresent(pathMatch)) {
+ solutions.push(pathMatch);
+ }
+ });
+
+ return solutions;
+ }
+
+ /** @internal */
+ _redirect(urlParse: Url): Url {
+ for (var i = 0; i < this.redirects.length; i += 1) {
+ let redirector = this.redirects[i];
+ var redirectedUrl = redirector.redirect(urlParse);
+ if (isPresent(redirectedUrl)) {
+ return redirectedUrl;
+ }
+ }
+
+ return urlParse;
+ }
+
+ recognizeAuxiliary(urlParse: Url): PathMatch {
+ var pathRecognizer = this.auxRoutes.get(urlParse.path);
+ if (isBlank(pathRecognizer)) {
+ return null;
+ }
+ return pathRecognizer.recognize(urlParse);
+ }
+
+ hasRoute(name: string): boolean { return this.names.has(name); }
+
+ generate(name: string, params: any): ComponentInstruction {
+ var pathRecognizer: PathRecognizer = this.names.get(name);
+ if (isBlank(pathRecognizer)) {
+ return null;
+ }
+ return pathRecognizer.generate(params);
+ }
+
+ generateAuxiliary(name: string, params: any): ComponentInstruction {
+ var pathRecognizer: PathRecognizer = this.auxNames.get(name);
+ if (isBlank(pathRecognizer)) {
+ return null;
+ }
+ return pathRecognizer.generate(params);
}
}
+export class Redirector {
+ segments: string[] = [];
+ toSegments: string[] = [];
-export class RedirectMatch extends RouteMatch {
- constructor(public redirectTo: any[], public specificity) { super(); }
-}
-
-export class RedirectRecognizer implements AbstractRecognizer {
- private _pathRecognizer: PathRecognizer;
- public hash: string;
-
- constructor(public path: string, public redirectTo: any[]) {
- this._pathRecognizer = new PathRecognizer(path);
- this.hash = this._pathRecognizer.hash;
+ constructor(path: string, redirectTo: string) {
+ if (path.startsWith('/')) {
+ path = path.substring(1);
+ }
+ this.segments = path.split('/');
+ if (redirectTo.startsWith('/')) {
+ redirectTo = redirectTo.substring(1);
+ }
+ this.toSegments = redirectTo.split('/');
}
/**
* Returns `null` or a `ParsedUrl` representing the new path to match
*/
- recognize(beginningSegment: Url): Promise {
- var match = null;
- if (isPresent(this._pathRecognizer.recognize(beginningSegment))) {
- match = new RedirectMatch(this.redirectTo, this._pathRecognizer.specificity);
+ redirect(urlParse: Url): Url {
+ for (var i = 0; i < this.segments.length; i += 1) {
+ if (isBlank(urlParse)) {
+ return null;
+ }
+ let segment = this.segments[i];
+ if (segment != urlParse.path) {
+ return null;
+ }
+ urlParse = urlParse.child;
}
- return PromiseWrapper.resolve(match);
- }
- generate(params: {[key: string]: any}): ComponentInstruction {
- throw new BaseException(`Tried to generate a redirect.`);
- }
-}
-
-
-// represents something like '/foo/:bar'
-export class RouteRecognizer implements AbstractRecognizer {
- specificity: number;
- terminal: boolean = true;
- hash: string;
-
- private _cache: Map = new Map();
- private _pathRecognizer: PathRecognizer;
-
- // TODO: cache component instruction instances by params and by ParsedUrl instance
-
- constructor(public path: string, public handler: RouteHandler) {
- this._pathRecognizer = new PathRecognizer(path);
- this.specificity = this._pathRecognizer.specificity;
- this.hash = this._pathRecognizer.hash;
- this.terminal = this._pathRecognizer.terminal;
- }
-
- recognize(beginningSegment: Url): Promise {
- var res = this._pathRecognizer.recognize(beginningSegment);
- if (isBlank(res)) {
- return null;
- }
-
- return this.handler.resolveComponentType().then((_) => {
- var componentInstruction =
- this._getInstruction(res['urlPath'], res['urlParams'], res['allParams']);
- return new PathMatch(componentInstruction, res['nextSegment'], res['auxiliary']);
- });
- }
-
- generate(params: {[key: string]: any}): ComponentInstruction {
- var generated = this._pathRecognizer.generate(params);
- var urlPath = generated['urlPath'];
- var urlParams = generated['urlParams'];
- return this._getInstruction(urlPath, urlParams, params);
- }
-
- generateComponentPathValues(params: {[key: string]: any}): {[key: string]: any} {
- return this._pathRecognizer.generate(params);
- }
-
- private _getInstruction(urlPath: string, urlParams: string[],
- params: {[key: string]: any}): ComponentInstruction {
- if (isBlank(this.handler.componentType)) {
- throw new BaseException(`Tried to get instruction before the type was loaded.`);
- }
-
- var hashKey = urlPath + '?' + urlParams.join('?');
- if (this._cache.has(hashKey)) {
- return this._cache.get(hashKey);
- }
- var instruction =
- new ComponentInstruction(urlPath, urlParams, this.handler.data, this.handler.componentType,
- this.terminal, this.specificity, params);
- this._cache.set(hashKey, instruction);
-
- return instruction;
+ for (var i = this.toSegments.length - 1; i >= 0; i -= 1) {
+ let segment = this.toSegments[i];
+ urlParse = new Url(segment, urlParse);
+ }
+ return urlParse;
}
}
diff --git a/modules/angular2/src/router/route_registry.ts b/modules/angular2/src/router/route_registry.ts
index 716cecc84f..c132d363ab 100644
--- a/modules/angular2/src/router/route_registry.ts
+++ b/modules/angular2/src/router/route_registry.ts
@@ -1,3 +1,6 @@
+import {PathMatch} from './path_recognizer';
+import {RouteRecognizer} from './route_recognizer';
+import {Instruction, ComponentInstruction, PrimaryInstruction} from './instruction';
import {ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
import {
@@ -13,7 +16,6 @@ import {
getTypeNameForDebugging
} from 'angular2/src/facade/lang';
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
-import {reflector} from 'angular2/src/core/reflection/reflection';
import {
RouteConfig,
AsyncRoute,
@@ -22,16 +24,7 @@ import {
Redirect,
RouteDefinition
} from './route_config_impl';
-import {PathMatch, RedirectMatch, RouteMatch} from './route_recognizer';
-import {ComponentRecognizer} from './component_recognizer';
-import {
- Instruction,
- ResolvedInstruction,
- RedirectInstruction,
- UnresolvedInstruction,
- DefaultInstruction
-} from './instruction';
-
+import {reflector} from 'angular2/src/core/reflection/reflection';
import {Injectable} from 'angular2/angular2';
import {normalizeRouteConfig, assertComponentExists} from './route_config_nomalizer';
import {parser, Url, pathSegmentsToUrl} from './url_parser';
@@ -45,13 +38,13 @@ var _resolveToNull = PromiseWrapper.resolve(null);
*/
@Injectable()
export class RouteRegistry {
- private _rules = new Map();
+ private _rules = new Map();
/**
* Given a component and a configuration object, add the route to this registry
*/
config(parentComponent: any, config: RouteDefinition): void {
- config = normalizeRouteConfig(config, this);
+ config = normalizeRouteConfig(config);
// this is here because Dart type guard reasons
if (config instanceof Route) {
@@ -60,10 +53,10 @@ export class RouteRegistry {
assertComponentExists(config.component, config.path);
}
- var recognizer: ComponentRecognizer = this._rules.get(parentComponent);
+ var recognizer: RouteRecognizer = this._rules.get(parentComponent);
if (isBlank(recognizer)) {
- recognizer = new ComponentRecognizer();
+ recognizer = new RouteRecognizer();
this._rules.set(parentComponent, recognizer);
}
@@ -109,162 +102,102 @@ export class RouteRegistry {
* Given a URL and a parent component, return the most specific instruction for navigating
* the application into the state specified by the url
*/
- recognize(url: string, ancestorComponents: any[]): Promise {
+ recognize(url: string, parentComponent: any): Promise {
var parsedUrl = parser.parse(url);
- return this._recognize(parsedUrl, ancestorComponents);
+ return this._recognize(parsedUrl, parentComponent);
}
+ private _recognize(parsedUrl: Url, parentComponent): Promise {
+ return this._recognizePrimaryRoute(parsedUrl, parentComponent)
+ .then((instruction: PrimaryInstruction) =>
+ this._completeAuxiliaryRouteMatches(instruction, parentComponent));
+ }
- /**
- * Recognizes all parent-child routes, but creates unresolved auxiliary routes
- */
-
- private _recognize(parsedUrl: Url, ancestorComponents: any[],
- _aux = false): Promise {
- var parentComponent = ancestorComponents[ancestorComponents.length - 1];
+ private _recognizePrimaryRoute(parsedUrl: Url, parentComponent): Promise {
var componentRecognizer = this._rules.get(parentComponent);
if (isBlank(componentRecognizer)) {
return _resolveToNull;
}
// Matches some beginning part of the given URL
- var possibleMatches: Promise[] =
- _aux ? componentRecognizer.recognizeAuxiliary(parsedUrl) :
- componentRecognizer.recognize(parsedUrl);
+ var possibleMatches = componentRecognizer.recognize(parsedUrl);
- var matchPromises: Promise[] = possibleMatches.map(
- (candidate: Promise) => candidate.then((candidate: RouteMatch) => {
-
- if (candidate instanceof PathMatch) {
- if (candidate.instruction.terminal) {
- var unresolvedAux =
- this._auxRoutesToUnresolved(candidate.remainingAux, parentComponent);
- return new ResolvedInstruction(candidate.instruction, null, unresolvedAux);
- }
-
- var newAncestorComponents =
- ancestorComponents.concat([candidate.instruction.componentType]);
-
- return this._recognize(candidate.remaining, newAncestorComponents)
- .then((childInstruction) => {
- if (isBlank(childInstruction)) {
- return null;
- }
-
- // redirect instructions are already absolute
- if (childInstruction instanceof RedirectInstruction) {
- return childInstruction;
- }
- var unresolvedAux =
- this._auxRoutesToUnresolved(candidate.remainingAux, parentComponent);
- return new ResolvedInstruction(candidate.instruction, childInstruction,
- unresolvedAux);
- });
- }
-
- if (candidate instanceof RedirectMatch) {
- var instruction = this.generate(candidate.redirectTo, ancestorComponents);
- return new RedirectInstruction(instruction.component, instruction.child,
- instruction.auxInstruction);
- }
- }));
-
- if ((isBlank(parsedUrl) || parsedUrl.path == '') && possibleMatches.length == 0) {
- return PromiseWrapper.resolve(this.generateDefault(parentComponent));
- }
+ var matchPromises =
+ possibleMatches.map(candidate => this._completePrimaryRouteMatch(candidate));
return PromiseWrapper.all(matchPromises).then(mostSpecific);
}
- private _auxRoutesToUnresolved(auxRoutes: Url[], parentComponent): {[key: string]: Instruction} {
- var unresolvedAuxInstructions: {[key: string]: Instruction} = {};
+ private _completePrimaryRouteMatch(partialMatch: PathMatch): Promise {
+ var instruction = partialMatch.instruction;
+ return instruction.resolveComponentType().then((componentType) => {
+ this.configFromComponent(componentType);
- auxRoutes.forEach((auxUrl: Url) => {
- unresolvedAuxInstructions[auxUrl.path] = new UnresolvedInstruction(
- () => { return this._recognize(auxUrl, [parentComponent], true); });
+ if (instruction.terminal) {
+ return new PrimaryInstruction(instruction, null, partialMatch.remainingAux);
+ }
+
+ return this._recognizePrimaryRoute(partialMatch.remaining, componentType)
+ .then((childInstruction) => {
+ if (isBlank(childInstruction)) {
+ return null;
+ } else {
+ return new PrimaryInstruction(instruction, childInstruction,
+ partialMatch.remainingAux);
+ }
+ });
});
-
- return unresolvedAuxInstructions;
}
+ private _completeAuxiliaryRouteMatches(instruction: PrimaryInstruction,
+ parentComponent: any): Promise {
+ if (isBlank(instruction)) {
+ return _resolveToNull;
+ }
+
+ var componentRecognizer = this._rules.get(parentComponent);
+ var auxInstructions: {[key: string]: Instruction} = {};
+
+ var promises = instruction.auxUrls.map((auxSegment: Url) => {
+ var match = componentRecognizer.recognizeAuxiliary(auxSegment);
+ if (isBlank(match)) {
+ return _resolveToNull;
+ }
+ return this._completePrimaryRouteMatch(match).then((auxInstruction: PrimaryInstruction) => {
+ if (isPresent(auxInstruction)) {
+ return this._completeAuxiliaryRouteMatches(auxInstruction, parentComponent)
+ .then((finishedAuxRoute: Instruction) => {
+ auxInstructions[auxSegment.path] = finishedAuxRoute;
+ });
+ }
+ });
+ });
+ return PromiseWrapper.all(promises).then((_) => {
+ if (isBlank(instruction.child)) {
+ return new Instruction(instruction.component, null, auxInstructions);
+ }
+ return this._completeAuxiliaryRouteMatches(instruction.child,
+ instruction.component.componentType)
+ .then((completeChild) => {
+ return new Instruction(instruction.component, completeChild, auxInstructions);
+ });
+ });
+ }
+
/**
* Given a normalized list with component names and params like: `['user', {id: 3 }]`
* generates a url with a leading slash relative to the provided `parentComponent`.
- *
- * If the optional param `_aux` is `true`, then we generate starting at an auxiliary
- * route boundary.
*/
- generate(linkParams: any[], ancestorComponents: any[], _aux = false): Instruction {
- let parentComponent = ancestorComponents[ancestorComponents.length - 1];
- let grandparentComponent =
- ancestorComponents.length > 1 ? ancestorComponents[ancestorComponents.length - 2] : null;
-
- let normalizedLinkParams = splitAndFlattenLinkParams(linkParams);
-
- var first = ListWrapper.first(normalizedLinkParams);
- var rest = ListWrapper.slice(normalizedLinkParams, 1);
-
- // The first segment should be either '.' (generate from parent) or '' (generate from root).
- // When we normalize above, we strip all the slashes, './' becomes '.' and '/' becomes ''.
- if (first == '') {
- var firstComponent = ancestorComponents[0];
- ListWrapper.clear(ancestorComponents);
- ancestorComponents.push(firstComponent);
- } else if (first == '..') {
- // we already captured the first instance of "..", so we need to pop off an ancestor
- ancestorComponents.pop();
- while (ListWrapper.first(rest) == '..') {
- rest = ListWrapper.slice(rest, 1);
- ancestorComponents.pop();
- if (ancestorComponents.length <= 0) {
- throw new BaseException(
- `Link "${ListWrapper.toJSON(linkParams)}" has too many "../" segments.`);
- }
- }
- } else if (first != '.') {
- // For a link with no leading `./`, `/`, or `../`, we look for a sibling and child.
- // If both exist, we throw. Otherwise, we prefer whichever exists.
- var childRouteExists = this.hasRoute(first, parentComponent);
- var parentRouteExists =
- isPresent(grandparentComponent) && this.hasRoute(first, grandparentComponent);
-
- if (parentRouteExists && childRouteExists) {
- let msg =
- `Link "${ListWrapper.toJSON(linkParams)}" is ambiguous, use "./" or "../" to disambiguate.`;
- throw new BaseException(msg);
- }
- if (parentRouteExists) {
- ancestorComponents.pop();
- }
- rest = linkParams;
- }
-
- if (rest[rest.length - 1] == '') {
- rest.pop();
- }
-
- if (rest.length < 1) {
- let msg = `Link "${ListWrapper.toJSON(linkParams)}" must include a route name.`;
- throw new BaseException(msg);
- }
-
- return this._generate(rest, ancestorComponents, _aux);
- }
-
-
- /*
- * Internal helper that does not make any assertions about the beginning of the link DSL
- */
- private _generate(linkParams: any[], ancestorComponents: any[], _aux = false): Instruction {
- let parentComponent = ancestorComponents[ancestorComponents.length - 1];
-
- if (linkParams.length == 0) {
- return this.generateDefault(parentComponent);
- }
+ generate(linkParams: any[], parentComponent: any, _aux = false): Instruction {
let linkIndex = 0;
let routeName = linkParams[linkIndex];
+ // TODO: this is kind of odd but it makes existing assertions pass
+ if (isBlank(parentComponent)) {
+ throw new BaseException(`Could not find route named "${routeName}".`);
+ }
+
if (!isString(routeName)) {
throw new BaseException(`Unexpected segment "${routeName}" in link DSL. Expected a string.`);
} else if (routeName == '' || routeName == '.' || routeName == '..') {
@@ -283,10 +216,7 @@ export class RouteRegistry {
let auxInstructions: {[key: string]: Instruction} = {};
var nextSegment;
while (linkIndex + 1 < linkParams.length && isArray(nextSegment = linkParams[linkIndex + 1])) {
- let auxInstruction = this._generate(nextSegment, [parentComponent], true);
-
- // TODO: this will not work for aux routes with parameters or multiple segments
- auxInstructions[auxInstruction.component.urlPath] = auxInstruction;
+ auxInstructions[nextSegment[0]] = this.generate(nextSegment, parentComponent, true);
linkIndex += 1;
}
@@ -296,105 +226,74 @@ export class RouteRegistry {
`Component "${getTypeNameForDebugging(parentComponent)}" has no route config.`);
}
- var routeRecognizer =
- (_aux ? componentRecognizer.auxNames : componentRecognizer.names).get(routeName);
+ var componentInstruction = _aux ? componentRecognizer.generateAuxiliary(routeName, params) :
+ componentRecognizer.generate(routeName, params);
- if (!isPresent(routeRecognizer)) {
+ if (isBlank(componentInstruction)) {
throw new BaseException(
`Component "${getTypeNameForDebugging(parentComponent)}" has no route named "${routeName}".`);
}
- if (!isPresent(routeRecognizer.handler.componentType)) {
- var compInstruction = routeRecognizer.generateComponentPathValues(params);
- return new UnresolvedInstruction(() => {
- return routeRecognizer.handler.resolveComponentType().then(
- (_) => { return this._generate(linkParams, ancestorComponents, _aux); });
- }, compInstruction['urlPath'], compInstruction['urlParams']);
+ var childInstruction = null;
+ if (linkIndex + 1 < linkParams.length) {
+ var remaining = linkParams.slice(linkIndex + 1);
+ childInstruction = this.generate(remaining, componentInstruction.componentType);
+ } else if (!componentInstruction.terminal) {
+ throw new BaseException(
+ `Link "${ListWrapper.toJSON(linkParams)}" does not resolve to a terminal or async instruction.`);
}
- var componentInstruction = _aux ? componentRecognizer.generateAuxiliary(routeName, params) :
- componentRecognizer.generate(routeName, params);
-
-
- var childInstruction: Instruction = null;
-
- var remaining = linkParams.slice(linkIndex + 1);
-
- // the component is sync
- if (isPresent(componentInstruction.componentType)) {
- if (linkIndex + 1 < linkParams.length) {
- let childAncestorComponents =
- ancestorComponents.concat([componentInstruction.componentType]);
- childInstruction = this._generate(remaining, childAncestorComponents);
- } else if (!componentInstruction.terminal) {
- // ... look for defaults
- childInstruction = this.generateDefault(componentInstruction.componentType);
-
- if (isBlank(childInstruction)) {
- throw new BaseException(
- `Link "${ListWrapper.toJSON(linkParams)}" does not resolve to a terminal instruction.`);
- }
- }
- }
-
- return new ResolvedInstruction(componentInstruction, childInstruction, auxInstructions);
+ return new Instruction(componentInstruction, childInstruction, auxInstructions);
}
public hasRoute(name: string, parentComponent: any): boolean {
- var componentRecognizer: ComponentRecognizer = this._rules.get(parentComponent);
+ var componentRecognizer: RouteRecognizer = this._rules.get(parentComponent);
if (isBlank(componentRecognizer)) {
return false;
}
return componentRecognizer.hasRoute(name);
}
- public generateDefault(componentCursor: Type): Instruction {
+ // if the child includes a redirect like : "/" -> "/something",
+ // we want to honor that redirection when creating the link
+ private _generateRedirects(componentCursor: Type): Instruction {
if (isBlank(componentCursor)) {
return null;
}
-
var componentRecognizer = this._rules.get(componentCursor);
- if (isBlank(componentRecognizer) || isBlank(componentRecognizer.defaultRoute)) {
+ if (isBlank(componentRecognizer)) {
return null;
}
+ for (let i = 0; i < componentRecognizer.redirects.length; i += 1) {
+ let redirect = componentRecognizer.redirects[i];
- var defaultChild = null;
- if (isPresent(componentRecognizer.defaultRoute.handler.componentType)) {
- var componentInstruction = componentRecognizer.defaultRoute.generate({});
- if (!componentRecognizer.defaultRoute.terminal) {
- defaultChild = this.generateDefault(componentRecognizer.defaultRoute.handler.componentType);
+ // we only handle redirecting from an empty segment
+ if (redirect.segments.length == 1 && redirect.segments[0] == '') {
+ var toSegments = pathSegmentsToUrl(redirect.toSegments);
+ var matches = componentRecognizer.recognize(toSegments);
+ var primaryInstruction =
+ ListWrapper.maximum(matches, (match: PathMatch) => match.instruction.specificity);
+
+ if (isPresent(primaryInstruction)) {
+ var child = this._generateRedirects(primaryInstruction.instruction.componentType);
+ return new Instruction(primaryInstruction.instruction, child, {});
+ }
+ return null;
}
- return new DefaultInstruction(componentInstruction, defaultChild);
}
- return new UnresolvedInstruction(() => {
- return componentRecognizer.defaultRoute.handler.resolveComponentType().then(
- () => this.generateDefault(componentCursor));
- });
+ return null;
}
}
-/*
- * Given: ['/a/b', {c: 2}]
- * Returns: ['', 'a', 'b', {c: 2}]
- */
-function splitAndFlattenLinkParams(linkParams: any[]): any[] {
- return linkParams.reduce((accumulation: any[], item) => {
- if (isString(item)) {
- let strItem: string = item;
- return accumulation.concat(strItem.split('/'));
- }
- accumulation.push(item);
- return accumulation;
- }, []);
-}
/*
* Given a list of instructions, returns the most specific instruction
*/
-function mostSpecific(instructions: Instruction[]): Instruction {
- return ListWrapper.maximum(instructions, (instruction: Instruction) => instruction.specificity);
+function mostSpecific(instructions: PrimaryInstruction[]): PrimaryInstruction {
+ return ListWrapper.maximum(
+ instructions, (instruction: PrimaryInstruction) => instruction.component.specificity);
}
function assertTerminalComponent(component, path) {
diff --git a/modules/angular2/src/router/router.ts b/modules/angular2/src/router/router.ts
index fbf9701ae3..5eb4a31ec9 100644
--- a/modules/angular2/src/router/router.ts
+++ b/modules/angular2/src/router/router.ts
@@ -6,6 +6,9 @@ import {RouteRegistry} from './route_registry';
import {
ComponentInstruction,
Instruction,
+ stringifyInstruction,
+ stringifyInstructionPath,
+ stringifyInstructionQuery
} from './instruction';
import {RouterOutlet} from './router_outlet';
import {Location} from './location';
@@ -209,7 +212,7 @@ export class Router {
if (result) {
return this.commit(instruction, _skipLocationChange)
.then((_) => {
- this._emitNavigationFinish(instruction.toRootUrl());
+ this._emitNavigationFinish(stringifyInstruction(instruction));
return true;
});
}
@@ -217,20 +220,25 @@ export class Router {
});
}
+ // TODO(btford): it'd be nice to remove this method as part of cleaning up the traversal logic
+ // Since refactoring `Router.generate` to return an instruction rather than a string, it's not
+ // guaranteed that the `componentType`s for the terminal async routes have been loaded by the time
+ // we begin navigation. The method below simply traverses instructions and resolves any components
+ // for which `componentType` is not present
/** @internal */
_settleInstruction(instruction: Instruction): Promise {
- return instruction.resolveComponent().then((_) => {
- var unsettledInstructions: Array> = [];
-
- if (isPresent(instruction.child)) {
- unsettledInstructions.push(this._settleInstruction(instruction.child));
- }
-
- StringMapWrapper.forEach(instruction.auxInstruction, (instruction, _) => {
- unsettledInstructions.push(this._settleInstruction(instruction));
- });
- return PromiseWrapper.all(unsettledInstructions);
+ var unsettledInstructions: Array> = [];
+ if (isBlank(instruction.component.componentType)) {
+ unsettledInstructions.push(instruction.component.resolveComponentType().then(
+ (type: Type) => { this.registry.configFromComponent(type); }));
+ }
+ if (isPresent(instruction.child)) {
+ unsettledInstructions.push(this._settleInstruction(instruction.child));
+ }
+ StringMapWrapper.forEach(instruction.auxInstruction, (instruction, _) => {
+ unsettledInstructions.push(this._settleInstruction(instruction));
});
+ return PromiseWrapper.all(unsettledInstructions);
}
private _emitNavigationFinish(url): void { ObservableWrapper.callEmit(this._subject, url); }
@@ -370,22 +378,7 @@ export class Router {
* Given a URL, returns an instruction representing the component graph
*/
recognize(url: string): Promise {
- var ancestorComponents = this._getAncestorComponents();
- return this.registry.recognize(url, ancestorComponents);
- }
-
- /**
- * get all the host components for this and
- */
- private _getAncestorComponents(): any[] {
- var ancestorComponents = [];
- var ancestorRouter = this;
- do {
- ancestorComponents.unshift(ancestorRouter.hostComponent);
- ancestorRouter = ancestorRouter.parent;
- } while (isPresent(ancestorRouter));
-
- return ancestorComponents;
+ return this.registry.recognize(url, this.hostComponent);
}
@@ -406,27 +399,67 @@ export class Router {
* app's base href.
*/
generate(linkParams: any[]): Instruction {
- var ancestorComponents = this._getAncestorComponents();
- var startingNumberOfAncestors = ancestorComponents.length;
+ let normalizedLinkParams = splitAndFlattenLinkParams(linkParams);
- var nextInstruction = this.registry.generate(linkParams, ancestorComponents);
- if (isBlank(nextInstruction)) {
- return null;
- }
+ var first = ListWrapper.first(normalizedLinkParams);
+ var rest = ListWrapper.slice(normalizedLinkParams, 1);
- var parentInstructionsToClone = startingNumberOfAncestors - ancestorComponents.length;
+ var router = this;
- var router = this.parent;
- for (var i = 0; i < parentInstructionsToClone; i++) {
- if (isBlank(router)) {
- break;
+ // The first segment should be either '.' (generate from parent) or '' (generate from root).
+ // When we normalize above, we strip all the slashes, './' becomes '.' and '/' becomes ''.
+ if (first == '') {
+ while (isPresent(router.parent)) {
+ router = router.parent;
}
+ } else if (first == '..') {
router = router.parent;
+ while (ListWrapper.first(rest) == '..') {
+ rest = ListWrapper.slice(rest, 1);
+ router = router.parent;
+ if (isBlank(router)) {
+ throw new BaseException(
+ `Link "${ListWrapper.toJSON(linkParams)}" has too many "../" segments.`);
+ }
+ }
+ } else if (first != '.') {
+ // For a link with no leading `./`, `/`, or `../`, we look for a sibling and child.
+ // If both exist, we throw. Otherwise, we prefer whichever exists.
+ var childRouteExists = this.registry.hasRoute(first, this.hostComponent);
+ var parentRouteExists =
+ isPresent(this.parent) && this.registry.hasRoute(first, this.parent.hostComponent);
+
+ if (parentRouteExists && childRouteExists) {
+ let msg =
+ `Link "${ListWrapper.toJSON(linkParams)}" is ambiguous, use "./" or "../" to disambiguate.`;
+ throw new BaseException(msg);
+ }
+ if (parentRouteExists) {
+ router = this.parent;
+ }
+ rest = linkParams;
}
- while (isPresent(router) && isPresent(router._currentInstruction)) {
- nextInstruction = router._currentInstruction.replaceChild(nextInstruction);
- router = router.parent;
+ if (rest[rest.length - 1] == '') {
+ rest.pop();
+ }
+
+ if (rest.length < 1) {
+ let msg = `Link "${ListWrapper.toJSON(linkParams)}" must include a route name.`;
+ throw new BaseException(msg);
+ }
+
+ var nextInstruction = this.registry.generate(rest, router.hostComponent);
+
+ var url = [];
+ var parent = router.parent;
+ while (isPresent(parent)) {
+ url.unshift(parent._currentInstruction);
+ parent = parent.parent;
+ }
+
+ while (url.length > 0) {
+ nextInstruction = url.pop().replaceChild(nextInstruction);
}
return nextInstruction;
@@ -449,8 +482,8 @@ export class RootRouter extends Router {
}
commit(instruction: Instruction, _skipLocationChange: boolean = false): Promise {
- var emitPath = instruction.toUrlPath();
- var emitQuery = instruction.toUrlQuery();
+ var emitPath = stringifyInstructionPath(instruction);
+ var emitQuery = stringifyInstructionQuery(instruction);
if (emitPath.length > 0) {
emitPath = '/' + emitPath;
}
@@ -488,6 +521,20 @@ class ChildRouter extends Router {
}
}
+/*
+ * Given: ['/a/b', {c: 2}]
+ * Returns: ['', 'a', 'b', {c: 2}]
+ */
+function splitAndFlattenLinkParams(linkParams: any[]): any[] {
+ return linkParams.reduce((accumulation: any[], item) => {
+ if (isString(item)) {
+ let strItem: string = item;
+ return accumulation.concat(strItem.split('/'));
+ }
+ accumulation.push(item);
+ return accumulation;
+ }, []);
+}
function canActivateOne(nextInstruction: Instruction,
prevInstruction: Instruction): Promise {
diff --git a/modules/angular2/src/router/router_link.ts b/modules/angular2/src/router/router_link.ts
index 0ca76bb4ec..cf96551b00 100644
--- a/modules/angular2/src/router/router_link.ts
+++ b/modules/angular2/src/router/router_link.ts
@@ -3,7 +3,7 @@ import {isString} from 'angular2/src/facade/lang';
import {Router} from './router';
import {Location} from './location';
-import {Instruction} from './instruction';
+import {Instruction, stringifyInstruction} from './instruction';
/**
* The RouterLink directive lets you link to specific parts of your app.
@@ -61,7 +61,7 @@ export class RouterLink {
this._routeParams = changes;
this._navigationInstruction = this._router.generate(this._routeParams);
- var navigationHref = this._navigationInstruction.toLinkUrl();
+ var navigationHref = stringifyInstruction(this._navigationInstruction);
this.visibleHref = this._location.prepareExternalUrl(navigationHref);
}
diff --git a/modules/angular2/src/router/sync_route_handler.ts b/modules/angular2/src/router/sync_route_handler.ts
index 1b951a098a..5ad7f0aa8d 100644
--- a/modules/angular2/src/router/sync_route_handler.ts
+++ b/modules/angular2/src/router/sync_route_handler.ts
@@ -1,19 +1,13 @@
-import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
-import {isPresent, Type} from 'angular2/src/facade/lang';
-
import {RouteHandler} from './route_handler';
-import {RouteData, BLANK_ROUTE_DATA} from './instruction';
-
+import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
+import {Type} from 'angular2/src/facade/lang';
export class SyncRouteHandler implements RouteHandler {
- public data: RouteData;
-
/** @internal */
_resolvedComponent: Promise = null;
- constructor(public componentType: Type, data?: {[key: string]: any}) {
+ constructor(public componentType: Type, public data?: {[key: string]: any}) {
this._resolvedComponent = PromiseWrapper.resolve(componentType);
- this.data = isPresent(data) ? new RouteData(data) : BLANK_ROUTE_DATA;
}
resolveComponentType(): Promise { return this._resolvedComponent; }
diff --git a/modules/angular2/test/router/component_recognizer_spec.ts b/modules/angular2/test/router/component_recognizer_spec.ts
deleted file mode 100644
index 5924bf5b73..0000000000
--- a/modules/angular2/test/router/component_recognizer_spec.ts
+++ /dev/null
@@ -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 {
- 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 {}
diff --git a/modules/angular2/test/router/integration/README.md b/modules/angular2/test/router/integration/README.md
deleted file mode 100644
index 157d7423d2..0000000000
--- a/modules/angular2/test/router/integration/README.md
+++ /dev/null
@@ -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`.
\ No newline at end of file
diff --git a/modules/angular2/test/router/integration/async_route_spec.ts b/modules/angular2/test/router/integration/async_route_spec.ts
deleted file mode 100644
index 40b182bfec..0000000000
--- a/modules/angular2/test/router/integration/async_route_spec.ts
+++ /dev/null
@@ -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); });
- });
- });
-}
diff --git a/modules/angular2/test/router/integration/auxiliary_route_spec.ts b/modules/angular2/test/router/integration/auxiliary_route_spec.ts
deleted file mode 100644
index 166d448ceb..0000000000
--- a/modules/angular2/test/router/integration/auxiliary_route_spec.ts
+++ /dev/null
@@ -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 {} | aux {}`)
- .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 {} | aux {}`)
- .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 {} | ' +
- 'aux {}',
- directives: [ROUTER_DIRECTIVES],
-})
-@RouteConfig([
- new Route({path: '/hello', component: HelloCmp, name: 'Hello'}),
- new AuxRoute({path: '/modal', component: ModalCmp, name: 'Aux'})
-])
-class AuxCmp {
-}
diff --git a/modules/angular2/test/router/integration/impl/async_route_spec_impl.ts b/modules/angular2/test/router/integration/impl/async_route_spec_impl.ts
deleted file mode 100644
index 583e4ecef8..0000000000
--- a/modules/angular2/test/router/integration/impl/async_route_spec_impl.ts
+++ /dev/null
@@ -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, `go to hello | `)
- .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, `go to hello | `)
- .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, `greet naomi | `)
- .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, `greet naomi | `)
- .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 { }`)
- .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 { }`)
- .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, `nav to child | outer { }`)
- .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, `nav to child | outer { }`)
- .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 { }`)
- .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 { }`)
- .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, `link to inner | outer { }`)
- .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, `link to inner | outer { }`)
- .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 { }`)
- .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 { }`)
- .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, `nav to child | outer { }`)
- .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, `nav to child | outer { }`)
- .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 { }`)
- .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 { }`)
- .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, `nav to child | outer { }`)
- .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, `nav to child | outer { }`)
- .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, `{ }`)
- .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, `{ }`)
- .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,
- `nav to matias { }`)
- .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,
- `nav to matias { }`)
- .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;
-}
diff --git a/modules/angular2/test/router/integration/impl/fixture_components.ts b/modules/angular2/test/router/integration/impl/fixture_components.ts
deleted file mode 100644
index f11efd68a5..0000000000
--- a/modules/angular2/test/router/integration/impl/fixture_components.ts
+++ /dev/null
@@ -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 { }`,
- 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 { }`,
- 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 { }`,
- 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 { }`,
- 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 { }`,
- 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 { }`,
- 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 {
-}
diff --git a/modules/angular2/test/router/integration/impl/sync_route_spec_impl.ts b/modules/angular2/test/router/integration/impl/sync_route_spec_impl.ts
deleted file mode 100644
index 15fbc3514c..0000000000
--- a/modules/angular2/test/router/integration/impl/sync_route_spec_impl.ts
+++ /dev/null
@@ -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, `go to hello | `)
- .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, `go to hello | `)
- .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, `greet naomi | `)
- .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, `greet naomi | `)
- .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 { }`)
- .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 { }`)
- .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, `nav to child | outer { }`)
- .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, `nav to child | outer { }`)
- .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, `{ }`)
- .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, `{ }`)
- .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,
- `nav to matias { }`)
- .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,
- `nav to matias { }`)
- .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 { }`)
- .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 { }`)
- .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, `link to inner | outer { }`)
- .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, `link to inner | outer { }`)
- .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;
-}
diff --git a/modules/angular2/test/router/integration/lifecycle_hook_spec.ts b/modules/angular2/test/router/integration/lifecycle_hook_spec.ts
index 5130f2c090..57e5d4f533 100644
--- a/modules/angular2/test/router/integration/lifecycle_hook_spec.ts
+++ b/modules/angular2/test/router/integration/lifecycle_hook_spec.ts
@@ -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 = "") {
+ return tcb.overrideView(MyComp, new View({
+ template: ('' + template + '
'),
+ 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(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(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(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 {}`,
- directives: [RouterOutlet]
-})
+@Component({selector: 'parent-activate-cmp'})
+@View({template: `parent {}`, directives: [RouterOutlet]})
@RouteConfig([new Route({path: '/child-activate', component: ActivateCmp})])
class ParentActivateCmp implements OnActivate {
onActivate(next: ComponentInstruction, prev: ComponentInstruction): Promise {
@@ -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 {
completer = PromiseWrapper.completer();
@@ -428,11 +444,8 @@ class WaitDeactivateCmp implements OnDeactivate {
}
}
-@Component({
- selector: 'parent-deactivate-cmp',
- template: `parent {}`,
- directives: [RouterOutlet]
-})
+@Component({selector: 'parent-deactivate-cmp'})
+@View({template: `parent {}`, 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 {}`,
- directives: [RouterOutlet]
-})
+@Component({selector: 'reuse-cmp'})
+@View({template: `reuse {}`, 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 {}`,
- directives: [RouterOutlet]
-})
+@Component({selector: 'never-reuse-cmp'})
+@View({template: `reuse {}`, 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 {}`,
- directives: [RouterOutlet]
-})
+@Component({selector: 'can-activate-cmp'})
+@View({template: `canActivate {}`, 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 {}`,
- directives: [RouterOutlet]
-})
+@Component({selector: 'can-deactivate-cmp'})
+@View({template: `canDeactivate {}`, 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 {
@@ -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: ``,
- directives: [RouterOutlet]
-})
+@Component({selector: 'all-hooks-parent-cmp'})
+@View({template: ``, 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 {
@@ -577,11 +574,8 @@ class ReuseHooksCmp implements OnActivate, OnReuse, OnDeactivate, CanReuse, CanD
}
}
-@Component({
- selector: 'lifecycle-cmp',
- template: ``,
- directives: [RouterOutlet]
-})
+@Component({selector: 'lifecycle-cmp'})
+@View({template: ``, directives: [RouterOutlet]})
@RouteConfig([
new Route({path: '/a', component: A}),
new Route({path: '/on-activate', component: ActivateCmp}),
diff --git a/modules/angular2/test/router/integration/navigation_spec.ts b/modules/angular2/test/router/integration/navigation_spec.ts
index 8e649ec9c2..cb420974e6 100644
--- a/modules/angular2/test/router/integration/navigation_spec.ts
+++ b/modules/angular2/test/router/integration/navigation_spec.ts
@@ -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 = "") {
+ return tcb.overrideView(MyComp, new View({
+ template: ('' + template + '
'),
+ 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 { }')
- .then((rtc) => {fixture = rtc})
+ compile('outer { }')
.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 { }')
- .then((rtc) => {fixture = rtc})
+ compile('outer { }')
.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 { }')
- .then((rtc) => {fixture = rtc})
+ compile('outer { }')
.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 { }`,
+@Component({selector: 'parent-cmp'})
+@View({
+ template: "inner { }",
directives: [RouterOutlet],
})
@RouteConfig([
@@ -247,12 +298,13 @@ function parentLoader() {
new Route({path: '/', component: HelloCmp}),
])
class ParentCmp {
+ constructor() {}
}
-@Component({
- selector: 'team-cmp',
- template: `team {{id}} { }`,
+@Component({selector: 'team-cmp'})
+@View({
+ template: "team {{id}} { }",
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 {} | ' +
+ 'aux {}',
+ directives: [RouterOutlet],
+})
+@RouteConfig([
+ new Route({path: '/hello', component: HelloCmp}),
+ new AuxRoute({path: '/modal', component: ModalCmp}),
+])
+class AuxCmp {
+}
diff --git a/modules/angular2/test/router/integration/redirect_route_spec.ts b/modules/angular2/test/router/integration/redirect_route_spec.ts
deleted file mode 100644
index 7f8bb28a92..0000000000
--- a/modules/angular2/test/router/integration/redirect_route_spec.ts
+++ /dev/null
@@ -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();
- });
- }));
- });
-}
diff --git a/modules/angular2/test/router/integration/bootstrap_spec.ts b/modules/angular2/test/router/integration/router_integration_spec.ts
similarity index 79%
rename from modules/angular2/test/router/integration/bootstrap_spec.ts
rename to modules/angular2/test/router/integration/router_integration_spec.ts
index ea4fe2a8b6..8d1fe44a59 100644
--- a/modules/angular2/test/router/integration/bootstrap_spec.ts
+++ b/modules/angular2/test/router/integration/router_integration_spec.ts
@@ -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 { }`,
- directives: ROUTER_DIRECTIVES
-})
+@Component({selector: 'app-cmp'})
+@View({template: "outer { }", 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 { }`,
- directives: ROUTER_DIRECTIVES
-})
+@Component({selector: 'parent-cmp'})
+@View({template: `parent { }`, directives: ROUTER_DIRECTIVES})
@RouteConfig([new Route({path: '/child', component: HelloCmp})])
class ParentCmp {
}
-@Component({
- selector: 'super-parent-cmp',
- template: `super-parent { }`,
- directives: ROUTER_DIRECTIVES
-})
+@Component({selector: 'super-parent-cmp'})
+@View({template: `super-parent { }`, directives: ROUTER_DIRECTIVES})
@RouteConfig([new Route({path: '/child', component: Hello2Cmp})])
class SuperParentCmp {
}
-@Component({
- selector: 'app-cmp',
- template: `root { }`,
- directives: ROUTER_DIRECTIVES
-})
+@Component({selector: 'app-cmp'})
+@View({template: `root { }`, 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: ``,
- directives: ROUTER_DIRECTIVES
-})
+@Component({selector: 'app-cmp'})
+@View({template: ``, 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 { }`,
- directives: ROUTER_DIRECTIVES
-})
+@Component({selector: 'app-cmp'})
+@View({template: `outer { }`, directives: ROUTER_DIRECTIVES})
@RouteConfig([new Route({path: '/cause-error', component: BrokenCmp})])
class BrokenAppCmp {
constructor(public router: Router, public location: LocationStrategy) {}
diff --git a/modules/angular2/test/router/integration/router_link_spec.ts b/modules/angular2/test/router/integration/router_link_spec.ts
index 4cf862d6e2..5b76fd538b 100644
--- a/modules/angular2/test/router/integration/router_link_spec.ts
+++ b/modules/angular2/test/router/integration/router_link_spec.ts
@@ -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({
diff --git a/modules/angular2/test/router/integration/sync_route_spec.ts b/modules/angular2/test/router/integration/sync_route_spec.ts
deleted file mode 100644
index 12a4d339fe..0000000000
--- a/modules/angular2/test/router/integration/sync_route_spec.ts
+++ /dev/null
@@ -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); });
-
- });
- });
-}
diff --git a/modules/angular2/test/router/integration/util.ts b/modules/angular2/test/router/integration/util.ts
deleted file mode 100644
index b63fc115f4..0000000000
--- a/modules/angular2/test/router/integration/util.ts
+++ /dev/null
@@ -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: ``,
- directives: [ROUTER_DIRECTIVES]
-})
-export class RootCmp {
- name: string;
-}
-
-export function compile(tcb: TestComponentBuilder,
- template: string = "") {
- return tcb.overrideTemplate(RootCmp, ('' + template + '
')).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);
-}
diff --git a/modules/angular2/test/router/path_recognizer_spec.ts b/modules/angular2/test/router/path_recognizer_spec.ts
index 08fddc1044..979c21a7d0 100644
--- a/modules/angular2/test/router/path_recognizer_spec.ts
+++ b/modules/angular2/test/router/path_recognizer_spec.ts
@@ -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'});
});
});
});
diff --git a/modules/angular2/test/router/route_config_spec.ts b/modules/angular2/test/router/route_config_spec.ts
index 0a178f8207..bc1dc5400f 100644
--- a/modules/angular2/test/router/route_config_spec.ts
+++ b/modules/angular2/test/router/route_config_spec.ts
@@ -214,10 +214,7 @@ class HelloCmp {
@Component({selector: 'app-cmp'})
@View({template: `root { }`, 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) {}
}
diff --git a/modules/angular2/test/router/route_recognizer_spec.ts b/modules/angular2/test/router/route_recognizer_spec.ts
new file mode 100644
index 0000000000..99426fd461
--- /dev/null
+++ b/modules/angular2/test/router/route_recognizer_spec.ts
@@ -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 {}
diff --git a/modules/angular2/test/router/route_registry_spec.ts b/modules/angular2/test/router/route_registry_spec.ts
index 869e4fb6ac..3b906bb749 100644
--- a/modules/angular2/test/router/route_registry_spec.ts
+++ b/modules/angular2/test/router/route_registry_spec.ts
@@ -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'})])
diff --git a/modules/angular2/test/router/router_link_spec.ts b/modules/angular2/test/router/router_link_spec.ts
index 420fe9f0c6..b29d160ff6 100644
--- a/modules/angular2/test/router/router_link_spec.ts
+++ b/modules/angular2/test/router/router_link_spec.ts
@@ -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 {
diff --git a/modules/angular2/test/router/router_spec.ts b/modules/angular2/test/router/router_spec.ts
index 0d611e3985..1fb2a3fb11 100644
--- a/modules/angular2/test/router/router_spec.ts
+++ b/modules/angular2/test/router/router_spec.ts
@@ -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 {
return PromiseWrapper.resolve(DummyComponent);
}