feat(router): add support for APP_BASE_HREF to HashLocationStrategy
Closes #4935 Closes #5368 Closes #5451
This commit is contained in:
parent
b6ec2387b3
commit
1bec4f6c61
|
@ -9,6 +9,7 @@ export {RouterOutlet} from './src/router/router_outlet';
|
||||||
export {RouterLink} from './src/router/router_link';
|
export {RouterLink} from './src/router/router_link';
|
||||||
export {RouteParams, RouteData} from './src/router/instruction';
|
export {RouteParams, RouteData} from './src/router/instruction';
|
||||||
export {RouteRegistry} from './src/router/route_registry';
|
export {RouteRegistry} from './src/router/route_registry';
|
||||||
|
export {PlatformLocation} from './src/router/platform_location';
|
||||||
export {LocationStrategy, APP_BASE_HREF} from './src/router/location_strategy';
|
export {LocationStrategy, APP_BASE_HREF} from './src/router/location_strategy';
|
||||||
export {HashLocationStrategy} from './src/router/hash_location_strategy';
|
export {HashLocationStrategy} from './src/router/hash_location_strategy';
|
||||||
export {PathLocationStrategy} from './src/router/path_location_strategy';
|
export {PathLocationStrategy} from './src/router/path_location_strategy';
|
||||||
|
@ -20,6 +21,7 @@ export {CanActivate} from './src/router/lifecycle_annotations';
|
||||||
export {Instruction, ComponentInstruction} from './src/router/instruction';
|
export {Instruction, ComponentInstruction} from './src/router/instruction';
|
||||||
export {OpaqueToken} from 'angular2/core';
|
export {OpaqueToken} from 'angular2/core';
|
||||||
|
|
||||||
|
import {PlatformLocation} from './src/router/platform_location';
|
||||||
import {LocationStrategy} from './src/router/location_strategy';
|
import {LocationStrategy} from './src/router/location_strategy';
|
||||||
import {PathLocationStrategy} from './src/router/path_location_strategy';
|
import {PathLocationStrategy} from './src/router/path_location_strategy';
|
||||||
import {Router, RootRouter} from './src/router/router';
|
import {Router, RootRouter} from './src/router/router';
|
||||||
|
@ -111,6 +113,7 @@ export const ROUTER_DIRECTIVES: any[] = CONST_EXPR([RouterOutlet, RouterLink]);
|
||||||
export const ROUTER_PROVIDERS: any[] = CONST_EXPR([
|
export const ROUTER_PROVIDERS: any[] = CONST_EXPR([
|
||||||
RouteRegistry,
|
RouteRegistry,
|
||||||
CONST_EXPR(new Provider(LocationStrategy, {useClass: PathLocationStrategy})),
|
CONST_EXPR(new Provider(LocationStrategy, {useClass: PathLocationStrategy})),
|
||||||
|
PlatformLocation,
|
||||||
Location,
|
Location,
|
||||||
CONST_EXPR(new Provider(
|
CONST_EXPR(new Provider(
|
||||||
Router,
|
Router,
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
import {Injectable, Inject, Optional} from 'angular2/core';
|
||||||
import {Injectable} from 'angular2/core';
|
import {
|
||||||
import {LocationStrategy, normalizeQueryParams} from './location_strategy';
|
LocationStrategy,
|
||||||
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
joinWithSlash,
|
||||||
|
APP_BASE_HREF,
|
||||||
|
normalizeQueryParams
|
||||||
|
} from './location_strategy';
|
||||||
|
import {EventListener} from 'angular2/src/facade/browser';
|
||||||
|
import {isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {PlatformLocation} from './platform_location';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
|
* `HashLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||||
|
@ -43,48 +49,45 @@ import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HashLocationStrategy extends LocationStrategy {
|
export class HashLocationStrategy extends LocationStrategy {
|
||||||
private _location: Location;
|
private _baseHref: string = '';
|
||||||
private _history: History;
|
constructor(private _platformLocation: PlatformLocation,
|
||||||
|
@Optional() @Inject(APP_BASE_HREF) _baseHref?: string) {
|
||||||
constructor() {
|
|
||||||
super();
|
super();
|
||||||
this._location = DOM.getLocation();
|
if (isPresent(_baseHref)) {
|
||||||
this._history = DOM.getHistory();
|
this._baseHref = _baseHref;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onPopState(fn: EventListener): void {
|
onPopState(fn: EventListener): void { this._platformLocation.onPopState(fn); }
|
||||||
DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
getBaseHref(): string { return ''; }
|
getBaseHref(): string { return this._baseHref; }
|
||||||
|
|
||||||
path(): string {
|
path(): string {
|
||||||
// the hash value is always prefixed with a `#`
|
// the hash value is always prefixed with a `#`
|
||||||
// and if it is empty then it will stay empty
|
// and if it is empty then it will stay empty
|
||||||
var path = this._location.hash;
|
var path = this._platformLocation.hash;
|
||||||
|
|
||||||
// Dart will complain if a call to substring is
|
// Dart will complain if a call to substring is
|
||||||
// executed with a position value that extends the
|
// executed with a position value that extends the
|
||||||
// length of string.
|
// length of string.
|
||||||
return (path.length > 0 ? path.substring(1) : path) +
|
return (path.length > 0 ? path.substring(1) : path) +
|
||||||
normalizeQueryParams(this._location.search);
|
normalizeQueryParams(this._platformLocation.search);
|
||||||
}
|
}
|
||||||
|
|
||||||
prepareExternalUrl(internal: string): string {
|
prepareExternalUrl(internal: string): string {
|
||||||
return internal.length > 0 ? ('#' + internal) : internal;
|
var url = joinWithSlash(this._baseHref, internal);
|
||||||
|
return url.length > 0 ? ('#' + url) : url;
|
||||||
}
|
}
|
||||||
|
|
||||||
pushState(state: any, title: string, path: string, queryParams: string) {
|
pushState(state: any, title: string, path: string, queryParams: string) {
|
||||||
var url = path + normalizeQueryParams(queryParams);
|
var url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams));
|
||||||
if (url.length == 0) {
|
if (url.length == 0) {
|
||||||
url = this._location.pathname;
|
url = this._platformLocation.pathname;
|
||||||
} else {
|
|
||||||
url = this.prepareExternalUrl(url);
|
|
||||||
}
|
}
|
||||||
this._history.pushState(state, title, url);
|
this._platformLocation.pushState(state, title, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
forward(): void { this._history.forward(); }
|
forward(): void { this._platformLocation.forward(); }
|
||||||
|
|
||||||
back(): void { this._history.back(); }
|
back(): void { this._platformLocation.back(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,3 +62,26 @@ export const APP_BASE_HREF: OpaqueToken = CONST_EXPR(new OpaqueToken('appBaseHre
|
||||||
export function normalizeQueryParams(params: string): string {
|
export function normalizeQueryParams(params: string): string {
|
||||||
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
return (params.length > 0 && params.substring(0, 1) != '?') ? ('?' + params) : params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function joinWithSlash(start: string, end: string): string {
|
||||||
|
if (start.length == 0) {
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
if (end.length == 0) {
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
var slashes = 0;
|
||||||
|
if (start.endsWith('/')) {
|
||||||
|
slashes++;
|
||||||
|
}
|
||||||
|
if (end.startsWith('/')) {
|
||||||
|
slashes++;
|
||||||
|
}
|
||||||
|
if (slashes == 2) {
|
||||||
|
return start + end.substring(1);
|
||||||
|
}
|
||||||
|
if (slashes == 1) {
|
||||||
|
return start + end;
|
||||||
|
}
|
||||||
|
return start + '/' + end;
|
||||||
|
}
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
import {Injectable, Inject, Optional} from 'angular2/core';
|
||||||
import {Injectable, Inject} from 'angular2/core';
|
|
||||||
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
||||||
import {isBlank} from 'angular2/src/facade/lang';
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||||
import {LocationStrategy, APP_BASE_HREF, normalizeQueryParams} from './location_strategy';
|
import {
|
||||||
|
LocationStrategy,
|
||||||
|
APP_BASE_HREF,
|
||||||
|
normalizeQueryParams,
|
||||||
|
joinWithSlash
|
||||||
|
} from './location_strategy';
|
||||||
|
import {PlatformLocation} from './platform_location';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
|
* `PathLocationStrategy` is a {@link LocationStrategy} used to configure the
|
||||||
|
@ -52,15 +57,14 @@ import {LocationStrategy, APP_BASE_HREF, normalizeQueryParams} from './location_
|
||||||
*/
|
*/
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PathLocationStrategy extends LocationStrategy {
|
export class PathLocationStrategy extends LocationStrategy {
|
||||||
private _location: Location;
|
|
||||||
private _history: History;
|
|
||||||
private _baseHref: string;
|
private _baseHref: string;
|
||||||
|
|
||||||
constructor(@Inject(APP_BASE_HREF) href?: string) {
|
constructor(private _platformLocation: PlatformLocation,
|
||||||
|
@Optional() @Inject(APP_BASE_HREF) href?: string) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (isBlank(href)) {
|
if (isBlank(href)) {
|
||||||
href = DOM.getBaseHref();
|
href = this._platformLocation.getBaseHrefFromDOM();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isBlank(href)) {
|
if (isBlank(href)) {
|
||||||
|
@ -68,33 +72,28 @@ export class PathLocationStrategy extends LocationStrategy {
|
||||||
`No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.`);
|
`No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._location = DOM.getLocation();
|
|
||||||
this._history = DOM.getHistory();
|
|
||||||
this._baseHref = href;
|
this._baseHref = href;
|
||||||
}
|
}
|
||||||
|
|
||||||
onPopState(fn: EventListener): void {
|
onPopState(fn: EventListener): void {
|
||||||
DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false);
|
this._platformLocation.onPopState(fn);
|
||||||
DOM.getGlobalEventTarget('window').addEventListener('hashchange', fn, false);
|
this._platformLocation.onHashChange(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
getBaseHref(): string { return this._baseHref; }
|
getBaseHref(): string { return this._baseHref; }
|
||||||
|
|
||||||
prepareExternalUrl(internal: string): string {
|
prepareExternalUrl(internal: string): string { return joinWithSlash(this._baseHref, internal); }
|
||||||
if (internal.startsWith('/') && this._baseHref.endsWith('/')) {
|
|
||||||
return this._baseHref + internal.substring(1);
|
|
||||||
}
|
|
||||||
return this._baseHref + internal;
|
|
||||||
}
|
|
||||||
|
|
||||||
path(): string { return this._location.pathname + normalizeQueryParams(this._location.search); }
|
path(): string {
|
||||||
|
return this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search);
|
||||||
|
}
|
||||||
|
|
||||||
pushState(state: any, title: string, url: string, queryParams: string) {
|
pushState(state: any, title: string, url: string, queryParams: string) {
|
||||||
var externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
|
var externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
|
||||||
this._history.pushState(state, title, externalUrl);
|
this._platformLocation.pushState(state, title, externalUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
forward(): void { this._history.forward(); }
|
forward(): void { this._platformLocation.forward(); }
|
||||||
|
|
||||||
back(): void { this._history.back(); }
|
back(): void { this._platformLocation.back(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||||
|
import {Injectable} from 'angular2/core';
|
||||||
|
import {EventListener, History, Location} from 'angular2/src/facade/browser';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `PlatformLocation` encapsulates all of the direct calls to platform APIs.
|
||||||
|
* This class should not be used directly by an application developer. Instead, use
|
||||||
|
* {@link Location}.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class PlatformLocation {
|
||||||
|
private _location: Location;
|
||||||
|
private _history: History;
|
||||||
|
|
||||||
|
constructor() { this._init(); }
|
||||||
|
|
||||||
|
// This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it
|
||||||
|
/** @internal */
|
||||||
|
_init() {
|
||||||
|
this._location = DOM.getLocation();
|
||||||
|
this._history = DOM.getHistory();
|
||||||
|
}
|
||||||
|
|
||||||
|
getBaseHrefFromDOM(): string { return DOM.getBaseHref(); }
|
||||||
|
|
||||||
|
onPopState(fn: EventListener): void {
|
||||||
|
DOM.getGlobalEventTarget('window').addEventListener('popstate', fn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
onHashChange(fn: EventListener): void {
|
||||||
|
DOM.getGlobalEventTarget('window').addEventListener('hashchange', fn, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
get pathname(): string { return this._location.pathname; }
|
||||||
|
get search(): string { return this._location.search; }
|
||||||
|
get hash(): string { return this._location.hash; }
|
||||||
|
set pathname(newPath: string) { this._location.pathname = newPath; }
|
||||||
|
|
||||||
|
pushState(state: any, title: string, url: string): void {
|
||||||
|
this._history.pushState(state, title, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
forward(): void { this._history.forward(); }
|
||||||
|
|
||||||
|
back(): void { this._history.back(); }
|
||||||
|
}
|
|
@ -0,0 +1,167 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
describe,
|
||||||
|
proxy,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
ddescribe,
|
||||||
|
expect,
|
||||||
|
inject,
|
||||||
|
beforeEach,
|
||||||
|
beforeEachProviders,
|
||||||
|
SpyObject
|
||||||
|
} from 'angular2/testing_internal';
|
||||||
|
|
||||||
|
import {Injector, provide} from 'angular2/core';
|
||||||
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
import {PlatformLocation} from 'angular2/src/router/platform_location';
|
||||||
|
import {LocationStrategy, APP_BASE_HREF} from 'angular2/src/router/location_strategy';
|
||||||
|
import {HashLocationStrategy} from 'angular2/src/router/hash_location_strategy';
|
||||||
|
import {SpyPlatformLocation} from './spies';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('HashLocationStrategy', () => {
|
||||||
|
var platformLocation, locationStrategy;
|
||||||
|
|
||||||
|
beforeEachProviders(
|
||||||
|
() => [HashLocationStrategy, provide(PlatformLocation, {useClass: SpyPlatformLocation})]);
|
||||||
|
|
||||||
|
describe('without APP_BASE_HREF', () => {
|
||||||
|
beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', '#foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with just query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('?bar')).toEqual('#?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with APP_BASE_HREF with neither leading nor trailing slash', () => {
|
||||||
|
beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: 'app'})]);
|
||||||
|
|
||||||
|
beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#app/foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#app/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#app/foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', '#app/foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('#app');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#app');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with APP_BASE_HREF with leading slash', () => {
|
||||||
|
beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: '/app'})]);
|
||||||
|
|
||||||
|
beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#/app/foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#/app/foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', '#/app/foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('#/app');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with APP_BASE_HREF with both leading and trailing slash', () => {
|
||||||
|
beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: '/app/'})]);
|
||||||
|
|
||||||
|
beforeEach(inject([PlatformLocation, HashLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('#/app/foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('#/app/foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', '#/app/foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('#/app/');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '#/app/');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,178 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
describe,
|
||||||
|
proxy,
|
||||||
|
it,
|
||||||
|
iit,
|
||||||
|
ddescribe,
|
||||||
|
expect,
|
||||||
|
inject,
|
||||||
|
beforeEach,
|
||||||
|
beforeEachProviders,
|
||||||
|
SpyObject
|
||||||
|
} from 'angular2/testing_internal';
|
||||||
|
|
||||||
|
import {Injector, provide} from 'angular2/core';
|
||||||
|
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
import {PlatformLocation} from 'angular2/src/router/platform_location';
|
||||||
|
import {LocationStrategy, APP_BASE_HREF} from 'angular2/src/router/location_strategy';
|
||||||
|
import {PathLocationStrategy} from 'angular2/src/router/path_location_strategy';
|
||||||
|
import {SpyPlatformLocation} from './spies';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('PathLocationStrategy', () => {
|
||||||
|
var platformLocation, locationStrategy;
|
||||||
|
|
||||||
|
beforeEachProviders(() => [
|
||||||
|
PathLocationStrategy,
|
||||||
|
provide(PlatformLocation, {useFactory: makeSpyPlatformLocation})
|
||||||
|
]);
|
||||||
|
|
||||||
|
it('should throw without a base element or APP_BASE_HREF', () => {
|
||||||
|
platformLocation = new SpyPlatformLocation();
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
platformLocation.spy('getBaseHrefFromDOM').andReturn(null);
|
||||||
|
|
||||||
|
expect(() => new PathLocationStrategy(platformLocation))
|
||||||
|
.toThrowError(
|
||||||
|
'No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('without APP_BASE_HREF', () => {
|
||||||
|
beforeEach(inject([PlatformLocation, PathLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', 'foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', 'foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with APP_BASE_HREF with neither leading nor trailing slash', () => {
|
||||||
|
beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: 'app'})]);
|
||||||
|
|
||||||
|
beforeEach(inject([PlatformLocation, PathLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('app/foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', 'app/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('app/foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', 'app/foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('app');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', 'app');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with APP_BASE_HREF with leading slash', () => {
|
||||||
|
beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: '/app'})]);
|
||||||
|
|
||||||
|
beforeEach(inject([PlatformLocation, PathLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('/app/foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '/app/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('/app/foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', '/app/foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('/app');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '/app');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with APP_BASE_HREF with both leading and trailing slash', () => {
|
||||||
|
beforeEachProviders(() => [provide(APP_BASE_HREF, {useValue: '/app/'})]);
|
||||||
|
|
||||||
|
beforeEach(inject([PlatformLocation, PathLocationStrategy], (pl, ls) => {
|
||||||
|
platformLocation = pl;
|
||||||
|
locationStrategy = ls;
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for non-empty URLs', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo')).toEqual('/app/foo');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '/app/foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should prepend urls with a hash for URLs with query params', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('foo?bar')).toEqual('/app/foo?bar');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', 'foo', 'bar=baz');
|
||||||
|
expect(platformLocation.spy('pushState'))
|
||||||
|
.toHaveBeenCalledWith(null, 'Title', '/app/foo?bar=baz');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not prepend a hash to external urls for an empty internal URL', () => {
|
||||||
|
expect(locationStrategy.prepareExternalUrl('')).toEqual('/app/');
|
||||||
|
|
||||||
|
locationStrategy.pushState(null, 'Title', '', '');
|
||||||
|
expect(platformLocation.spy('pushState')).toHaveBeenCalledWith(null, 'Title', '/app/');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeSpyPlatformLocation() {
|
||||||
|
var platformLocation = new SpyPlatformLocation();
|
||||||
|
platformLocation.spy('getBaseHrefFromDOM').andReturn('');
|
||||||
|
platformLocation.spy('pushState');
|
||||||
|
platformLocation.pathname = '';
|
||||||
|
return platformLocation;
|
||||||
|
}
|
|
@ -17,3 +17,8 @@ class SpyRouter extends SpyObject implements Router {
|
||||||
class SpyRouterOutlet extends SpyObject implements RouterOutlet {
|
class SpyRouterOutlet extends SpyObject implements RouterOutlet {
|
||||||
noSuchMethod(m) => super.noSuchMethod(m);
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SpyPlatformLocation extends SpyObject implements PlatformLocation {
|
||||||
|
String pathname;
|
||||||
|
noSuchMethod(m) => super.noSuchMethod(m);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {Router, RouterOutlet, Location} from 'angular2/router';
|
import {Router, RouterOutlet, Location, PlatformLocation} from 'angular2/router';
|
||||||
import {SpyObject, proxy} from 'angular2/testing_internal';
|
import {SpyObject, proxy} from 'angular2/testing_internal';
|
||||||
|
|
||||||
export class SpyRouter extends SpyObject {
|
export class SpyRouter extends SpyObject {
|
||||||
|
@ -11,4 +11,9 @@ export class SpyRouterOutlet extends SpyObject {
|
||||||
|
|
||||||
export class SpyLocation extends SpyObject {
|
export class SpyLocation extends SpyObject {
|
||||||
constructor() { super(Location); }
|
constructor() { super(Location); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class SpyPlatformLocation extends SpyObject {
|
||||||
|
pathname: string = null;
|
||||||
|
constructor() { super(SpyPlatformLocation); }
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue