feat(aio): revise Docs page; docs version selector in sidenav
This commit is contained in:

committed by
Pete Bacon Darwin

parent
de25cfc0cb
commit
4be1966a21
@ -15,6 +15,12 @@
|
||||
<md-sidenav [ngClass]="{'collapsed': !isSideBySide }" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode">
|
||||
<aio-nav-menu *ngIf="!isSideBySide" class="top-menu" [nodes]="topMenuNodes" [currentNode]="currentNode"></aio-nav-menu>
|
||||
<aio-nav-menu [nodes]="sideNavNodes" [currentNode]="currentNode" ></aio-nav-menu>
|
||||
|
||||
<div class="doc-version" title="Angular docs version {{currentDocVersion?.title}}">
|
||||
<select (change)="onDocVersionChange($event.target.selectedIndex)">
|
||||
<option *ngFor="let version of docVersions" [value]="version.title">{{version.title}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</md-sidenav>
|
||||
|
||||
<section class="sidenav-content" [id]="pageId" role="content">
|
||||
|
@ -15,12 +15,13 @@ import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { GaService } from 'app/shared/ga.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
import { MockLogger } from 'testing/logger.service';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
import { MockLogger } from 'testing/logger.service';
|
||||
import { MockSearchService } from 'testing/search.service';
|
||||
import { MockSwUpdateNotificationsService } from 'testing/sw-update-notifications.service';
|
||||
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
|
||||
import { NavigationNode } from 'app/navigation/navigation.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { SwUpdateNotificationsService } from 'app/sw-updates/sw-update-notifications.service';
|
||||
|
||||
@ -194,6 +195,31 @@ describe('AppComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('SideNav version selector', () => {
|
||||
beforeEach(() => {
|
||||
component.onResize(1033); // side-by-side
|
||||
});
|
||||
|
||||
it('should pick first (current) version by default', () => {
|
||||
const versionSelector = sidenav.querySelector('select');
|
||||
expect(versionSelector.value).toEqual(TestHttp.docVersions[0].title);
|
||||
expect(versionSelector.selectedIndex).toEqual(0);
|
||||
});
|
||||
|
||||
// Older docs versions have an href
|
||||
it('should navigate when change to a version with an href', () => {
|
||||
component.onDocVersionChange(1);
|
||||
expect(locationService.go).toHaveBeenCalledWith(TestHttp.docVersions[1].url);
|
||||
});
|
||||
|
||||
// The current docs version should not have an href
|
||||
// This may change when we perfect our docs versioning approach
|
||||
it('should not navigate when change to a version without an href', () => {
|
||||
component.onDocVersionChange(0);
|
||||
expect(locationService.go).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('pageId', () => {
|
||||
|
||||
it('should set the id of the doc viewer container based on the current doc', () => {
|
||||
@ -437,6 +463,11 @@ class TestSearchService {
|
||||
class TestHttp {
|
||||
static versionFull = '4.0.0-local+sha.73808dd';
|
||||
|
||||
static docVersions: NavigationNode[] = [
|
||||
{ title: 'v4.0.0' },
|
||||
{ title: 'v2', url: 'https://v2.angular.io' }
|
||||
];
|
||||
|
||||
// tslint:disable:quotemark
|
||||
navJson = {
|
||||
"TopBar": [
|
||||
@ -472,6 +503,8 @@ class TestHttp {
|
||||
"tooltip": "Details of the Angular classes and values."
|
||||
}
|
||||
],
|
||||
"docVersions": TestHttp.docVersions,
|
||||
|
||||
"__versionInfo": {
|
||||
"raw": "4.0.0-rc.6",
|
||||
"major": 4,
|
||||
|
@ -33,6 +33,9 @@ export class AppComponent implements OnInit {
|
||||
private sideBySideWidth = 1032;
|
||||
sideNavNodes: NavigationNode[];
|
||||
topMenuNodes: NavigationNode[];
|
||||
|
||||
currentDocVersion: NavigationNode;
|
||||
docVersions: NavigationNode[];
|
||||
versionInfo: VersionInfo;
|
||||
|
||||
get homeImageUrl() {
|
||||
@ -95,9 +98,12 @@ export class AppComponent implements OnInit {
|
||||
});
|
||||
|
||||
this.navigationService.navigationViews.subscribe(views => {
|
||||
this.docVersions = views['docVersions'] || [];
|
||||
this.footerNodes = views['Footer'] || [];
|
||||
this.sideNavNodes = views['SideNav'] || [];
|
||||
this.topMenuNodes = views['TopBar'] || [];
|
||||
|
||||
this.currentDocVersion = this.docVersions[0];
|
||||
});
|
||||
|
||||
this.navigationService.versionInfo.subscribe( vi => this.versionInfo = vi );
|
||||
@ -116,6 +122,13 @@ export class AppComponent implements OnInit {
|
||||
this.isStarting = false;
|
||||
}
|
||||
|
||||
onDocVersionChange(versionIndex: number) {
|
||||
const version = this.docVersions[versionIndex];
|
||||
if (version.url) {
|
||||
this.locationService.go(version.url);
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener('window:resize', ['$event.target.innerWidth'])
|
||||
onResize(width) {
|
||||
this.isSideBySide = width > this.sideBySideWidth;
|
||||
|
@ -9,6 +9,8 @@ import { Logger } from 'app/shared/logger.service';
|
||||
describe('NavigationService', () => {
|
||||
|
||||
let injector: ReflectiveInjector;
|
||||
let backend: MockBackend;
|
||||
let navService: NavigationService;
|
||||
|
||||
function createResponse(body: any) {
|
||||
return new Response(new ResponseOptions({ body: JSON.stringify(body) }));
|
||||
@ -25,19 +27,16 @@ describe('NavigationService', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
backend = injector.get(ConnectionBackend);
|
||||
navService = injector.get(NavigationService);
|
||||
});
|
||||
|
||||
it('should be creatable', () => {
|
||||
const navService: NavigationService = injector.get(NavigationService);
|
||||
expect(navService).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('navigationViews', () => {
|
||||
let backend: MockBackend;
|
||||
let navService: NavigationService;
|
||||
|
||||
beforeEach(() => {
|
||||
backend = injector.get(ConnectionBackend);
|
||||
navService = injector.get(NavigationService);
|
||||
});
|
||||
|
||||
it('should make a single connection to the server', () => {
|
||||
expect(backend.connectionsArray.length).toEqual(1);
|
||||
@ -78,14 +77,12 @@ describe('NavigationService', () => {
|
||||
expect(views3).toBe(views1);
|
||||
});
|
||||
|
||||
|
||||
it('should do WHAT(?) if the request fails');
|
||||
});
|
||||
|
||||
describe('currentNode', () => {
|
||||
let currentNode: CurrentNode;
|
||||
let locationService: MockLocationService;
|
||||
let navService: NavigationService;
|
||||
|
||||
const topBarNodes: NavigationNode[] = [{ url: 'features', title: 'Features' }];
|
||||
const sideNavNodes: NavigationNode[] = [
|
||||
@ -105,14 +102,9 @@ describe('NavigationService', () => {
|
||||
__versionInfo: {}
|
||||
};
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
locationService = injector.get(LocationService);
|
||||
|
||||
navService = injector.get(NavigationService);
|
||||
navService.currentNode.subscribe(selected => currentNode = selected);
|
||||
|
||||
const backend = injector.get(ConnectionBackend);
|
||||
backend.connectionsArray[0].mockRespond(createResponse(navJson));
|
||||
});
|
||||
|
||||
@ -190,13 +182,10 @@ describe('NavigationService', () => {
|
||||
});
|
||||
|
||||
describe('versionInfo', () => {
|
||||
let navService: NavigationService, versionInfo: VersionInfo;
|
||||
let versionInfo: VersionInfo;
|
||||
|
||||
beforeEach(() => {
|
||||
navService = injector.get(NavigationService);
|
||||
navService.versionInfo.subscribe(info => versionInfo = info);
|
||||
|
||||
const backend = injector.get(ConnectionBackend);
|
||||
backend.connectionsArray[0].mockRespond(createResponse({
|
||||
__versionInfo: { raw: '4.0.0' }
|
||||
}));
|
||||
@ -206,4 +195,24 @@ describe('NavigationService', () => {
|
||||
expect(versionInfo).toEqual({ raw: '4.0.0' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('docVersions', () => {
|
||||
let actualDocVersions: NavigationNode[];
|
||||
let docVersions: NavigationNode[];
|
||||
|
||||
beforeEach(() => {
|
||||
actualDocVersions = [];
|
||||
docVersions = [
|
||||
{ title: 'v4.0.0' },
|
||||
{ title: 'v2', url: 'https://v2.angular.io' }
|
||||
];
|
||||
|
||||
navService.navigationViews.subscribe(views => actualDocVersions = views.docVersions);
|
||||
});
|
||||
|
||||
it('should extract the docVersions', () => {
|
||||
backend.connectionsArray[0].mockRespond(createResponse({ docVersions }));
|
||||
expect(actualDocVersions).toEqual(docVersions);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ import { Http } from '@angular/http';
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { AsyncSubject } from 'rxjs/AsyncSubject';
|
||||
import { combineLatest } from 'rxjs/observable/combineLatest';
|
||||
import 'rxjs/add/operator/map';
|
||||
import 'rxjs/add/operator/publishLast';
|
||||
import 'rxjs/add/operator/publishReplay';
|
||||
|
||||
@ -37,10 +38,11 @@ export class NavigationService {
|
||||
|
||||
constructor(private http: Http, private location: LocationService, private logger: Logger) {
|
||||
const navigationInfo = this.fetchNavigationInfo();
|
||||
this.navigationViews = this.getNavigationViews(navigationInfo);
|
||||
|
||||
this.currentNode = this.getCurrentNode(this.navigationViews);
|
||||
// The version information is packaged inside the navigation response to save us an extra request.
|
||||
this.versionInfo = this.getVersionInfo(navigationInfo);
|
||||
this.navigationViews = this.getNavigationViews(navigationInfo);
|
||||
this.currentNode = this.getCurrentNode(this.navigationViews);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,7 +71,13 @@ export class NavigationService {
|
||||
}
|
||||
|
||||
private getNavigationViews(navigationInfo: Observable<NavigationResponse>): Observable<NavigationViews> {
|
||||
const navigationViews = navigationInfo.map(response => unpluck(response, '__versionInfo')).publishReplay(1);
|
||||
const navigationViews = navigationInfo.map(response => {
|
||||
const views: NavigationViews = Object.assign({}, response);
|
||||
Object.keys(views).forEach(key => {
|
||||
if (key[0] === '_') { delete views[key]; }
|
||||
});
|
||||
return views;
|
||||
}).publishReplay(1);
|
||||
navigationViews.connect();
|
||||
return navigationViews;
|
||||
}
|
||||
@ -120,9 +128,3 @@ export class NavigationService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unpluck(obj: any, property: string) {
|
||||
const result = Object.assign({}, obj);
|
||||
delete result[property];
|
||||
return result;
|
||||
}
|
||||
|
@ -6,8 +6,9 @@ import { GaService } from 'app/shared/ga.service';
|
||||
import { LocationService } from './location.service';
|
||||
|
||||
describe('LocationService', () => {
|
||||
|
||||
let injector: ReflectiveInjector;
|
||||
let location: MockLocationStrategy;
|
||||
let service: LocationService;
|
||||
|
||||
beforeEach(() => {
|
||||
injector = ReflectiveInjector.resolveAndCreate([
|
||||
@ -17,19 +18,18 @@ describe('LocationService', () => {
|
||||
{ provide: LocationStrategy, useClass: MockLocationStrategy },
|
||||
{ provide: PlatformLocation, useClass: MockPlatformLocation }
|
||||
]);
|
||||
|
||||
location = injector.get(LocationStrategy);
|
||||
service = injector.get(LocationService);
|
||||
});
|
||||
|
||||
describe('currentUrl', () => {
|
||||
it('should emit the latest url at the time it is subscribed to', () => {
|
||||
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
|
||||
location.simulatePopState('/initial-url1');
|
||||
location.simulatePopState('/initial-url2');
|
||||
location.simulatePopState('/initial-url3');
|
||||
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('/next-url1');
|
||||
location.simulatePopState('/next-url2');
|
||||
location.simulatePopState('/next-url3');
|
||||
@ -40,9 +40,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should emit all location changes after it has been subscribed to', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('/initial-url1');
|
||||
location.simulatePopState('/initial-url2');
|
||||
location.simulatePopState('/initial-url3');
|
||||
@ -63,9 +60,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should pass only the latest and later urls to each subscriber', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('/initial-url1');
|
||||
location.simulatePopState('/initial-url2');
|
||||
location.simulatePopState('/initial-url3');
|
||||
@ -95,8 +89,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should strip leading and trailing slashes', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
const urls: string[] = [];
|
||||
|
||||
service.currentUrl.subscribe(u => urls.push(u));
|
||||
@ -117,8 +109,6 @@ describe('LocationService', () => {
|
||||
|
||||
describe('currentPath', () => {
|
||||
it('should strip leading and trailing slashes off the url', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
const paths: string[] = [];
|
||||
|
||||
service.currentPath.subscribe(p => paths.push(p));
|
||||
@ -137,8 +127,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should not strip other slashes off the url', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
const paths: string[] = [];
|
||||
|
||||
service.currentPath.subscribe(p => paths.push(p));
|
||||
@ -157,8 +145,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should strip the query off the url', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
let path: string;
|
||||
|
||||
service.currentPath.subscribe(p => path = p);
|
||||
@ -169,8 +155,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should strip the hash fragment off the url', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
const paths: string[] = [];
|
||||
|
||||
service.currentPath.subscribe(p => paths.push(p));
|
||||
@ -185,14 +169,10 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should emit the latest path at the time it is subscribed to', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
|
||||
location.simulatePopState('/initial/url1');
|
||||
location.simulatePopState('/initial/url2');
|
||||
location.simulatePopState('/initial/url3');
|
||||
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('/next/url1');
|
||||
location.simulatePopState('/next/url2');
|
||||
location.simulatePopState('/next/url3');
|
||||
@ -204,9 +184,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should emit all location changes after it has been subscribed to', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('/initial/url1');
|
||||
location.simulatePopState('/initial/url2');
|
||||
location.simulatePopState('/initial/url3');
|
||||
@ -227,9 +204,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should pass only the latest and later paths to each subscriber', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('/initial/url1');
|
||||
location.simulatePopState('/initial/url2');
|
||||
location.simulatePopState('/initial/url3');
|
||||
@ -260,27 +234,19 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
describe('go', () => {
|
||||
|
||||
it('should update the location', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
service.go('some-new-url');
|
||||
|
||||
expect(location.internalPath).toEqual('some-new-url');
|
||||
expect(location.path(true)).toEqual('some-new-url');
|
||||
});
|
||||
|
||||
it('should emit the new url', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
const urls = [];
|
||||
service.go('some-initial-url');
|
||||
|
||||
const urls = [];
|
||||
service.currentUrl.subscribe(url => urls.push(url));
|
||||
|
||||
service.go('some-new-url');
|
||||
|
||||
expect(urls).toEqual([
|
||||
'some-initial-url',
|
||||
'some-new-url'
|
||||
@ -288,8 +254,6 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should strip leading and trailing slashes', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
let url: string;
|
||||
|
||||
service.currentUrl.subscribe(u => url = u);
|
||||
@ -299,21 +263,48 @@ describe('LocationService', () => {
|
||||
expect(location.path(true)).toEqual('some/url');
|
||||
expect(url).toBe('some/url');
|
||||
});
|
||||
|
||||
it('should ignore undefined URL string', noUrlTest(undefined));
|
||||
it('should ignore null URL string', noUrlTest(null));
|
||||
it('should ignore empty URL string', noUrlTest(''));
|
||||
function noUrlTest(testUrl: string) {
|
||||
return function() {
|
||||
const initialUrl = 'some/url';
|
||||
const goExternalSpy = spyOn(service, 'goExternal');
|
||||
let url: string;
|
||||
|
||||
service.go(initialUrl);
|
||||
service.currentUrl.subscribe(u => url = u);
|
||||
|
||||
service.go(testUrl);
|
||||
expect(url).toEqual(initialUrl, 'should not have re-navigated locally');
|
||||
expect(goExternalSpy.wasCalled).toBeFalsy('should not have navigated externally');
|
||||
};
|
||||
}
|
||||
|
||||
it('should leave the site for external url that starts with "http"', () => {
|
||||
const goExternalSpy = spyOn(service, 'goExternal');
|
||||
const externalUrl = 'http://some/far/away/land';
|
||||
service.go(externalUrl);
|
||||
expect(goExternalSpy).toHaveBeenCalledWith(externalUrl);
|
||||
});
|
||||
|
||||
it('should not update currentUrl for external url that starts with "http"', () => {
|
||||
let localUrl: string;
|
||||
spyOn(service, 'goExternal');
|
||||
service.currentUrl.subscribe(url => localUrl = url);
|
||||
service.go('https://some/far/away/land');
|
||||
expect(localUrl).toBeFalsy('should not set local url');
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
it('should read the query from the current location.path', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('a/b/c?foo=bar&moo=car');
|
||||
expect(service.search()).toEqual({ foo: 'bar', moo: 'car' });
|
||||
});
|
||||
|
||||
it('should cope with an empty query', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('a/b/c');
|
||||
expect(service.search()).toEqual({ });
|
||||
|
||||
@ -328,25 +319,16 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should URL decode query values', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('a/b/c?query=a%26b%2Bc%20d');
|
||||
expect(service.search()).toEqual({ query: 'a&b+c d' });
|
||||
});
|
||||
|
||||
it('should URL decode query keys', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
location.simulatePopState('a/b/c?a%26b%2Bc%20d=value');
|
||||
expect(service.search()).toEqual({ 'a&b+c d': 'value' });
|
||||
});
|
||||
|
||||
it('should cope with a hash on the URL', () => {
|
||||
const location: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
spyOn(location, 'path').and.callThrough();
|
||||
service.search();
|
||||
expect(location.path).toHaveBeenCalledWith(false);
|
||||
@ -354,53 +336,47 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
describe('setSearch', () => {
|
||||
it('should call replaceState on PlatformLocation', () => {
|
||||
const location: MockPlatformLocation = injector.get(PlatformLocation);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
let platformLocation: MockPlatformLocation;
|
||||
|
||||
beforeEach(() => {
|
||||
platformLocation = injector.get(PlatformLocation);
|
||||
});
|
||||
|
||||
it('should call replaceState on PlatformLocation', () => {
|
||||
const params = {};
|
||||
service.setSearch('Some label', params);
|
||||
expect(location.replaceState).toHaveBeenCalledWith(jasmine.any(Object), 'Some label', 'a/b/c');
|
||||
expect(platformLocation.replaceState).toHaveBeenCalledWith(jasmine.any(Object), 'Some label', 'a/b/c');
|
||||
});
|
||||
|
||||
it('should convert the params to a query string', () => {
|
||||
const location: MockPlatformLocation = injector.get(PlatformLocation);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
const params = { foo: 'bar', moo: 'car' };
|
||||
service.setSearch('Some label', params);
|
||||
expect(location.replaceState).toHaveBeenCalledWith(jasmine.any(Object), 'Some label', jasmine.any(String));
|
||||
const [path, query] = location.replaceState.calls.mostRecent().args[2].split('?');
|
||||
expect(platformLocation.replaceState).toHaveBeenCalledWith(jasmine.any(Object), 'Some label', jasmine.any(String));
|
||||
const [path, query] = platformLocation.replaceState.calls.mostRecent().args[2].split('?');
|
||||
expect(path).toEqual('a/b/c');
|
||||
expect(query).toContain('foo=bar');
|
||||
expect(query).toContain('moo=car');
|
||||
});
|
||||
|
||||
it('should URL encode param values', () => {
|
||||
const location: MockPlatformLocation = injector.get(PlatformLocation);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
const params = { query: 'a&b+c d' };
|
||||
service.setSearch('', params);
|
||||
const [, query] = location.replaceState.calls.mostRecent().args[2].split('?');
|
||||
const [, query] = platformLocation.replaceState.calls.mostRecent().args[2].split('?');
|
||||
expect(query).toContain('query=a%26b%2Bc%20d');
|
||||
});
|
||||
|
||||
it('should URL encode param keys', () => {
|
||||
const location: MockPlatformLocation = injector.get(PlatformLocation);
|
||||
const service: LocationService = injector.get(LocationService);
|
||||
|
||||
const params = { 'a&b+c d': 'value' };
|
||||
service.setSearch('', params);
|
||||
const [, query] = location.replaceState.calls.mostRecent().args[2].split('?');
|
||||
const [, query] = platformLocation.replaceState.calls.mostRecent().args[2].split('?');
|
||||
expect(query).toContain('a%26b%2Bc%20d=value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleAnchorClick', () => {
|
||||
let service: LocationService, anchor: HTMLAnchorElement;
|
||||
let anchor: HTMLAnchorElement;
|
||||
|
||||
beforeEach(() => {
|
||||
service = injector.get(LocationService);
|
||||
anchor = document.createElement('a');
|
||||
});
|
||||
|
||||
@ -520,14 +496,10 @@ describe('LocationService', () => {
|
||||
describe('google analytics - GaService#locationChanged', () => {
|
||||
|
||||
let gaLocationChanged: jasmine.Spy;
|
||||
let location: Location;
|
||||
let service: LocationService;
|
||||
|
||||
beforeEach(() => {
|
||||
const gaService = injector.get(GaService);
|
||||
gaLocationChanged = gaService.locationChanged;
|
||||
location = injector.get(Location);
|
||||
service = injector.get(LocationService);
|
||||
});
|
||||
|
||||
it('should call locationChanged with initial URL', () => {
|
||||
@ -546,8 +518,7 @@ describe('LocationService', () => {
|
||||
});
|
||||
|
||||
it('should call locationChanged when window history changes', () => {
|
||||
const locationStrategy: MockLocationStrategy = injector.get(LocationStrategy);
|
||||
locationStrategy.simulatePopState('/next-url');
|
||||
location.simulatePopState('/next-url');
|
||||
|
||||
expect(gaLocationChanged.calls.count()).toBe(2, 'gaService.locationChanged');
|
||||
const args = gaLocationChanged.calls.argsFor(1);
|
||||
|
@ -36,9 +36,19 @@ export class LocationService {
|
||||
|
||||
// TODO?: ignore if url-without-hash-or-search matches current location?
|
||||
go(url: string) {
|
||||
if (!url) { return; }
|
||||
url = this.stripSlashes(url);
|
||||
this.location.go(url);
|
||||
this.urlSubject.next(url);
|
||||
if (/^http/.test(url)) {
|
||||
// Has http protocol so leave the site
|
||||
this.goExternal(url);
|
||||
} else {
|
||||
this.location.go(url);
|
||||
this.urlSubject.next(url);
|
||||
}
|
||||
}
|
||||
|
||||
goExternal(url: string) {
|
||||
location.assign(url);
|
||||
}
|
||||
|
||||
private stripSlashes(url: string) {
|
||||
|
Reference in New Issue
Block a user