diff --git a/packages/common/src/location/location.ts b/packages/common/src/location/location.ts index f603960ccd660..9d4f37fc18a3b 100644 --- a/packages/common/src/location/location.ts +++ b/packages/common/src/location/location.ts @@ -57,7 +57,7 @@ export class Location implements OnDestroy { /** @internal */ _subject: EventEmitter = new EventEmitter(); /** @internal */ - _baseHref: string; + _basePath: string; /** @internal */ _locationStrategy: LocationStrategy; /** @internal */ @@ -67,8 +67,8 @@ export class Location implements OnDestroy { constructor(locationStrategy: LocationStrategy) { this._locationStrategy = locationStrategy; - const browserBaseHref = this._locationStrategy.getBaseHref(); - this._baseHref = stripTrailingSlash(_stripIndexHtml(browserBaseHref)); + const baseHref = this._locationStrategy.getBaseHref(); + this._basePath = _stripOrigin(stripTrailingSlash(_stripIndexHtml(baseHref))); this._locationStrategy.onPopState((ev) => { this._subject.emit({ 'url': this.path(true), @@ -127,7 +127,7 @@ export class Location implements OnDestroy { * @returns The normalized URL string. */ normalize(url: string): string { - return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url))); + return Location.stripTrailingSlash(_stripBasePath(this._basePath, _stripIndexHtml(url))); } /** @@ -294,10 +294,18 @@ export function createLocation() { return new Location(ɵɵinject(LocationStrategy as any)); } -function _stripBaseHref(baseHref: string, url: string): string { - return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url; +function _stripBasePath(basePath: string, url: string): string { + return basePath && url.startsWith(basePath) ? url.substring(basePath.length) : url; } function _stripIndexHtml(url: string): string { return url.replace(/\/index.html$/, ''); } + +function _stripOrigin(baseHref: string): string { + if (/^(https?:)?\/\//.test(baseHref)) { + const [, pathname] = baseHref.split(/\/\/[^\/]+/); + return pathname; + } + return baseHref; +} diff --git a/packages/common/test/location/location_spec.ts b/packages/common/test/location/location_spec.ts index 6e74324fb455c..74cdca9f21a5f 100644 --- a/packages/common/test/location/location_spec.ts +++ b/packages/common/test/location/location_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {CommonModule, Location, LocationStrategy, PathLocationStrategy, PlatformLocation} from '@angular/common'; +import {APP_BASE_HREF, CommonModule, Location, LocationStrategy, PathLocationStrategy, PlatformLocation} from '@angular/common'; import {MockLocationStrategy, MockPlatformLocation} from '@angular/common/testing'; import {TestBed} from '@angular/core/testing'; @@ -55,7 +55,7 @@ describe('Location Class', () => { return new MockPlatformLocation(); } }, - {provide: Location, useClass: Location, deps: [LocationStrategy, PlatformLocation]}, + {provide: Location, useClass: Location, deps: [LocationStrategy]}, ] }); @@ -147,7 +147,7 @@ describe('Location Class', () => { return new MockPlatformLocation(); } }, - {provide: Location, useClass: Location, deps: [LocationStrategy, PlatformLocation]}, + {provide: Location, useClass: Location, deps: [LocationStrategy]}, ] }); @@ -215,4 +215,55 @@ describe('Location Class', () => { expect(notificationCount).toBe(1); }); }); + + describe('location.normalize(url) should return only route', () => { + const basePath = '/en'; + const route = '/go/to/there'; + const url = basePath + route; + const getBaseHref = (origin: string) => origin + basePath + '/'; + + it('in case APP_BASE_HREF starts with http:', () => { + const origin = 'http://example.com'; + const baseHref = getBaseHref(origin); + + TestBed.configureTestingModule({providers: [{provide: APP_BASE_HREF, useValue: baseHref}]}); + + const location = TestBed.inject(Location); + + expect(location.normalize(url)).toBe(route); + }); + + it('in case APP_BASE_HREF starts with https:', () => { + const origin = 'https://example.com'; + const baseHref = getBaseHref(origin); + + TestBed.configureTestingModule({providers: [{provide: APP_BASE_HREF, useValue: baseHref}]}); + + const location = TestBed.inject(Location); + + expect(location.normalize(url)).toBe(route); + }); + + it('in case APP_BASE_HREF starts with no protocol', () => { + const origin = '//example.com'; + const baseHref = getBaseHref(origin); + + TestBed.configureTestingModule({providers: [{provide: APP_BASE_HREF, useValue: baseHref}]}); + + const location = TestBed.inject(Location); + + expect(location.normalize(url)).toBe(route); + }); + + it('in case APP_BASE_HREF starts with no origin', () => { + const origin = ''; + const baseHref = getBaseHref(origin); + + TestBed.configureTestingModule({providers: [{provide: APP_BASE_HREF, useValue: baseHref}]}); + + const location = TestBed.inject(Location); + + expect(location.normalize(url)).toBe(route); + }); + }); }); diff --git a/packages/common/testing/src/location_mock.ts b/packages/common/testing/src/location_mock.ts index caf7184690956..0d12ddd8c5109 100644 --- a/packages/common/testing/src/location_mock.ts +++ b/packages/common/testing/src/location_mock.ts @@ -25,7 +25,7 @@ export class SpyLocation implements Location { /** @internal */ _subject: EventEmitter = new EventEmitter(); /** @internal */ - _baseHref: string = ''; + _basePath: string = ''; /** @internal */ _locationStrategy: LocationStrategy = null!; /** @internal */ @@ -43,7 +43,7 @@ export class SpyLocation implements Location { } setBaseHref(url: string) { - this._baseHref = url; + this._basePath = url; } path(): string { @@ -81,7 +81,7 @@ export class SpyLocation implements Location { if (url.length > 0 && !url.startsWith('/')) { url = '/' + url; } - return this._baseHref + url; + return this._basePath + url; } go(path: string, query: string = '', state: any = null) {