From 38addacda0466d6ac6464c2f4e2ca939ad0c8a1d Mon Sep 17 00:00:00 2001 From: Georgios Kalpakas Date: Fri, 11 Aug 2017 20:16:55 +0300 Subject: [PATCH] build(aio): switch from `@angular/http` to `@angular/common/http` ``` $ ls -l dist/*.js 14942 dist/0.b19e913fbdd6507d346b.chunk.js 1535 dist/inline.a1b446562b36eebb766d.bundle.js 524385 (+ 682) dist/main.19fec4390ff7837ee6ef.bundle.js 37402 dist/polyfills.9f7e0e53bce2a6c8326e.bundle.js 54001 dist/worker-basic.min.js 632265 (+ 682) total ``` --- aio/src/app/app.component.spec.ts | 14 +- aio/src/app/app.module.ts | 4 +- .../app/documents/document.service.spec.ts | 164 +++++++++--------- aio/src/app/documents/document.service.ts | 22 ++- .../embedded/api/api-list.component.spec.ts | 4 - aio/src/app/embedded/api/api.service.spec.ts | 60 +++---- aio/src/app/embedded/api/api.service.ts | 9 +- .../contributor/contributor.service.spec.ts | 123 +++++++------ .../contributor/contributor.service.ts | 8 +- .../resource/resource.service.spec.ts | 38 ++-- .../app/embedded/resource/resource.service.ts | 7 +- .../app/navigation/navigation.service.spec.ts | 64 ++++--- aio/src/app/navigation/navigation.service.ts | 7 +- .../shared/custom-md-icon-registry.spec.ts | 10 +- aio/src/app/shared/custom-md-icon-registry.ts | 23 ++- aio/src/app/shared/scroll-spy.service.spec.ts | 4 - aio/src/app/shared/toc.service.spec.ts | 4 - aio/src/test.ts | 8 +- aio/src/testing/nav-map-json-response.ts | 102 ----------- aio/yarn.lock | 2 +- 20 files changed, 279 insertions(+), 398 deletions(-) delete mode 100644 aio/src/testing/nav-map-json-response.ts diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index 6353be5f80..1765cd85e1 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -2,7 +2,7 @@ import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core'; import { async, inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { Title } from '@angular/platform-browser'; import { APP_BASE_HREF } from '@angular/common'; -import { Http } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; import { MdProgressBar, MdSidenav } from '@angular/material'; import { By } from '@angular/platform-browser'; @@ -650,7 +650,7 @@ describe('AppComponent', () => { describe('footer', () => { it('should have version number', () => { const versionEl: HTMLElement = fixture.debugElement.query(By.css('aio-footer')).nativeElement; - expect(versionEl.textContent).toContain(TestHttp.versionInfo.full); + expect(versionEl.textContent).toContain(TestHttpClient.versionInfo.full); }); }); @@ -1027,7 +1027,7 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') { providers: [ { provide: APP_BASE_HREF, useValue: '/' }, { provide: GaService, useClass: TestGaService }, - { provide: Http, useClass: TestHttp }, + { provide: HttpClient, useClass: TestHttpClient }, { provide: LocationService, useFactory: () => mockLocationService }, { provide: Logger, useClass: MockLogger }, { provide: SearchService, useClass: MockSearchService }, @@ -1049,7 +1049,7 @@ class TestSearchService { loadIndex = jasmine.createSpy('loadIndex'); } -class TestHttp { +class TestHttpClient { static versionInfo = { raw: '4.0.0-rc.6', @@ -1105,9 +1105,9 @@ class TestHttp { "tooltip": "Details of the Angular classes and values." } ], - "docVersions": TestHttp.docVersions, + "docVersions": TestHttpClient.docVersions, - "__versionInfo": TestHttp.versionInfo, + "__versionInfo": TestHttpClient.versionInfo, }; get(url: string) { @@ -1123,6 +1123,6 @@ class TestHttp { const contents = `${h1}

Some heading

`; data = { id, contents }; } - return of({ json: () => data }); + return of(data); } } diff --git a/aio/src/app/app.module.ts b/aio/src/app/app.module.ts index ca9545ab5b..2cc2c33aa6 100644 --- a/aio/src/app/app.module.ts +++ b/aio/src/app/app.module.ts @@ -1,6 +1,6 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; -import { HttpModule } from '@angular/http'; +import { HttpClientModule } from '@angular/common/http'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common'; @@ -75,7 +75,7 @@ export const svgIconProviders = [ imports: [ BrowserModule, EmbeddedModule, - HttpModule, + HttpClientModule, BrowserAnimationsModule, MdButtonModule, MdIconModule, diff --git a/aio/src/app/documents/document.service.spec.ts b/aio/src/app/documents/document.service.spec.ts index 7b9a660515..8f7116bcc7 100644 --- a/aio/src/app/documents/document.service.spec.ts +++ b/aio/src/app/documents/document.service.spec.ts @@ -1,11 +1,11 @@ -import { ReflectiveInjector } from '@angular/core'; -import { Http, ConnectionBackend, RequestOptions, BaseRequestOptions, Response, ResponseOptions } from '@angular/http'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Injector } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import { Observable } from 'rxjs/Observable'; import { Subscription } from 'rxjs/Subscription'; import { BehaviorSubject } from 'rxjs/BehaviorSubject'; -import { MockBackend } from '@angular/http/testing'; import { LocationService } from 'app/shared/location.service'; import { MockLocationService } from 'testing/location.service'; import { Logger } from 'app/shared/logger.service'; @@ -16,113 +16,123 @@ import { DocumentService, DocumentContents, const CONTENT_URL_PREFIX = 'generated/docs/'; -function createResponse(body: any) { - return new Response(new ResponseOptions({ body: JSON.stringify(body) })); -} - -function createInjector(initialUrl: string) { - return ReflectiveInjector.resolveAndCreate([ - DocumentService, - { provide: LocationService, useFactory: () => new MockLocationService(initialUrl) }, - { provide: ConnectionBackend, useClass: MockBackend }, - { provide: RequestOptions, useClass: BaseRequestOptions }, - { provide: Logger, useClass: MockLogger }, - Http, - ]); -} - -function getServices(initialUrl: string = '') { - const injector = createInjector(initialUrl); - return { - backend: injector.get(ConnectionBackend) as MockBackend, - locationService: injector.get(LocationService) as MockLocationService, - docService: injector.get(DocumentService) as DocumentService, - logger: injector.get(Logger) as MockLogger - }; -} - describe('DocumentService', () => { - it('should be creatable', () => { - const { docService } = getServices(); - expect(docService).toBeTruthy(); - }); + let httpMock: HttpTestingController; + + function createInjector(initialUrl: string) { + return TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + DocumentService, + { provide: LocationService, useFactory: () => new MockLocationService(initialUrl) }, + { provide: Logger, useClass: MockLogger }, + ] + }); + } + + function getServices(initialUrl: string = '') { + const injector = createInjector(initialUrl); + httpMock = injector.get(HttpTestingController) as HttpTestingController; + return { + locationService: injector.get(LocationService) as MockLocationService, + docService: injector.get(DocumentService) as DocumentService, + logger: injector.get(Logger) as MockLogger + }; + } + + afterEach(() => httpMock.verify()); describe('currentDocument', () => { it('should fetch a document for the initial location', () => { - const { docService, backend } = getServices('initial/doc'); - const connections = backend.connectionsArray; + const { docService } = getServices('initial/doc'); docService.currentDocument.subscribe(); - expect(connections.length).toEqual(1); - expect(connections[0].request.url).toEqual(CONTENT_URL_PREFIX + 'initial/doc.json'); - expect(backend.connectionsArray[0].request.url).toEqual(CONTENT_URL_PREFIX + 'initial/doc.json'); + httpMock.expectOne(CONTENT_URL_PREFIX + 'initial/doc.json'); }); it('should emit a document each time the location changes', () => { let latestDocument: DocumentContents; const doc0 = { contents: 'doc 0', id: 'initial/doc' }; - const doc1 = { contents: 'doc 1', id: 'new/doc' }; - const { docService, backend, locationService } = getServices('initial/doc'); - const connections = backend.connectionsArray; + const doc1 = { contents: 'doc 1', id: 'new/doc' }; + const { docService, locationService } = getServices('initial/doc'); docService.currentDocument.subscribe(doc => latestDocument = doc); expect(latestDocument).toBeUndefined(); - connections[0].mockRespond(createResponse(doc0)); + httpMock.expectOne({}).flush(doc0); expect(latestDocument).toEqual(doc0); locationService.go('new/doc'); - connections[1].mockRespond(createResponse(doc1)); + httpMock.expectOne({}).flush(doc1); expect(latestDocument).toEqual(doc1); }); it('should emit the not-found document if the document is not found on the server', () => { - const { docService, backend } = getServices('missing/doc'); - const connections = backend.connectionsArray; let currentDocument: DocumentContents; + const notFoundDoc = { id: FILE_NOT_FOUND_ID, contents: '

Page Not Found

' }; + const { docService } = getServices('missing/doc'); docService.currentDocument.subscribe(doc => currentDocument = doc); - connections[0].mockError(new Response(new ResponseOptions({ status: 404, statusText: 'NOT FOUND'})) as any); - expect(connections.length).toEqual(2); - expect(connections[1].request.url).toEqual(CONTENT_URL_PREFIX + 'file-not-found.json'); - const fileNotFoundDoc = { id: FILE_NOT_FOUND_ID, contents: '

Page Not Found

' }; - connections[1].mockRespond(createResponse(fileNotFoundDoc)); - expect(currentDocument).toEqual(fileNotFoundDoc); + // Initial request return 404. + httpMock.expectOne({}).flush(null, {status: 404, statusText: 'NOT FOUND'}); + + // Subsequent request for not-found document. + httpMock.expectOne(CONTENT_URL_PREFIX + 'file-not-found.json').flush(notFoundDoc); + + expect(currentDocument).toEqual(notFoundDoc); }); it('should emit a hard-coded not-found document if the not-found document is not found on the server', () => { let currentDocument: DocumentContents; - const notFoundDoc: DocumentContents = { contents: 'Document not found', id: FILE_NOT_FOUND_ID }; + const hardCodedNotFoundDoc = { contents: 'Document not found', id: FILE_NOT_FOUND_ID }; const nextDoc = { contents: 'Next Doc', id: 'new/doc' }; - const { docService, backend, locationService } = getServices(FILE_NOT_FOUND_ID); - const connections = backend.connectionsArray; + const { docService, locationService } = getServices(FILE_NOT_FOUND_ID); + docService.currentDocument.subscribe(doc => currentDocument = doc); - connections[0].mockError(new Response(new ResponseOptions({ status: 404, statusText: 'NOT FOUND'})) as any); - expect(connections.length).toEqual(1); - expect(currentDocument).toEqual(notFoundDoc); + httpMock.expectOne({}).flush(null, { status: 404, statusText: 'NOT FOUND'}); + expect(currentDocument).toEqual(hardCodedNotFoundDoc); // now check that we haven't killed the currentDocument observable sequence locationService.go('new/doc'); - connections[1].mockRespond(createResponse(nextDoc)); + httpMock.expectOne({}).flush(nextDoc); expect(currentDocument).toEqual(nextDoc); }); + it('should use a hard-coded error doc if the request fails (but not cache it)', () => { + let latestDocument: DocumentContents; + const doc1 = { contents: 'doc 1' }; + const doc2 = { contents: 'doc 2' }; + const { docService, locationService } = getServices('initial/doc'); + + docService.currentDocument.subscribe(doc => latestDocument = doc); + + httpMock.expectOne({}).flush(null, {status: 500, statusText: 'Server Error'}); + expect(latestDocument.id).toEqual(FETCHING_ERROR_ID); + + locationService.go('new/doc'); + httpMock.expectOne({}).flush(doc1); + expect(latestDocument).toEqual(jasmine.objectContaining(doc1)); + + locationService.go('initial/doc'); + httpMock.expectOne({}).flush(doc2); + expect(latestDocument).toEqual(jasmine.objectContaining(doc2)); + }); + it('should not crash the app if the response is invalid JSON', () => { let latestDocument: DocumentContents; - const { docService, backend, locationService} = getServices('initial/doc'); - const connections = backend.connectionsArray; + const doc1 = { contents: 'doc 1' }; + const { docService, locationService } = getServices('initial/doc'); docService.currentDocument.subscribe(doc => latestDocument = doc); - connections[0].mockRespond(new Response(new ResponseOptions({ body: 'this is invalid JSON' }))); + httpMock.expectOne({}).flush('this is invalid JSON'); expect(latestDocument.id).toEqual(FETCHING_ERROR_ID); - const doc1 = { contents: 'doc 1' }; locationService.go('new/doc'); - connections[1].mockRespond(createResponse(doc1)); + httpMock.expectOne({}).flush(doc1); expect(latestDocument).toEqual(jasmine.objectContaining(doc1)); }); @@ -132,37 +142,30 @@ describe('DocumentService', () => { const doc0 = { contents: 'doc 0' }; const doc1 = { contents: 'doc 1' }; - const { docService, backend, locationService} = getServices('url/0'); - const connections = backend.connectionsArray; + const { docService, locationService } = getServices('url/0'); subscription = docService.currentDocument.subscribe(doc => latestDocument = doc); - expect(connections.length).toEqual(1); - connections[0].mockRespond(createResponse(doc0)); + httpMock.expectOne({}).flush(doc0); expect(latestDocument).toEqual(jasmine.objectContaining(doc0)); subscription.unsubscribe(); - // modify the response so we can check that future subscriptions do not trigger another request - connections[0].response.next(createResponse({ contents: 'error 0' })); - subscription = docService.currentDocument.subscribe(doc => latestDocument = doc); locationService.go('url/1'); - expect(connections.length).toEqual(2); - connections[1].mockRespond(createResponse(doc1)); + httpMock.expectOne({}).flush(doc1); expect(latestDocument).toEqual(jasmine.objectContaining(doc1)); subscription.unsubscribe(); - // modify the response so we can check that future subscriptions do not trigger another request - connections[1].response.next(createResponse({ contents: 'error 1' })); - + // This should not trigger a new request. subscription = docService.currentDocument.subscribe(doc => latestDocument = doc); locationService.go('url/0'); - expect(connections.length).toEqual(2); + httpMock.expectNone({}); expect(latestDocument).toEqual(jasmine.objectContaining(doc0)); subscription.unsubscribe(); + // This should not trigger a new request. subscription = docService.currentDocument.subscribe(doc => latestDocument = doc); locationService.go('url/1'); - expect(connections.length).toEqual(2); + httpMock.expectNone({}); expect(latestDocument).toEqual(jasmine.objectContaining(doc1)); subscription.unsubscribe(); }); @@ -171,17 +174,18 @@ describe('DocumentService', () => { describe('computeMap', () => { it('should map the "empty" location to the correct document request', () => { let latestDocument: DocumentContents; - const { docService, backend } = getServices(); + const { docService } = getServices(); + docService.currentDocument.subscribe(doc => latestDocument = doc); - expect(backend.connectionsArray[0].request.url).toEqual(CONTENT_URL_PREFIX + 'index.json'); + httpMock.expectOne(CONTENT_URL_PREFIX + 'index.json'); }); it('should map the "folder" locations to the correct document request', () => { - const { docService, backend, locationService} = getServices('guide'); + const { docService } = getServices('guide'); docService.currentDocument.subscribe(); - expect(backend.connectionsArray[0].request.url).toEqual(CONTENT_URL_PREFIX + 'guide.json'); + httpMock.expectOne(CONTENT_URL_PREFIX + 'guide.json'); }); }); }); diff --git a/aio/src/app/documents/document.service.ts b/aio/src/app/documents/document.service.ts index c7617465f2..6ef931127f 100644 --- a/aio/src/app/documents/document.service.ts +++ b/aio/src/app/documents/document.service.ts @@ -1,11 +1,10 @@ import { Injectable } from '@angular/core'; -import { Http, Response } from '@angular/http'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { AsyncSubject } from 'rxjs/AsyncSubject'; import { of } from 'rxjs/observable/of'; import 'rxjs/add/operator/catch'; -import 'rxjs/add/operator/map'; import 'rxjs/add/operator/switchMap'; import { DocumentContents } from './document-contents'; @@ -41,7 +40,7 @@ export class DocumentService { constructor( private logger: Logger, - private http: Http, + private http: HttpClient, location: LocationService) { // Whenever the URL changes we try to get the appropriate doc this.currentDocument = location.currentPath.switchMap(path => this.getDocument(path)); @@ -58,15 +57,22 @@ export class DocumentService { private fetchDocument(id: string): Observable { const requestPath = `${DOC_CONTENT_URL_PREFIX}${id}.json`; + const subject = new AsyncSubject(); + this.logger.log('fetching document from', requestPath); - const subject = new AsyncSubject(); this.http - .get(requestPath) - .map(response => response.json()) - .catch((error: Response) => { + .get(requestPath, {responseType: 'json'}) + .do(data => { + if (!data || typeof data !== 'object') { + this.logger.log('received invalid data:', data); + throw Error('Invalid data'); + } + }) + .catch((error: HttpErrorResponse) => { return error.status === 404 ? this.getFileNotFoundDoc(id) : this.getErrorDoc(id, error); }) .subscribe(subject); + return subject.asObservable(); } @@ -83,7 +89,7 @@ export class DocumentService { } } - private getErrorDoc(id: string, error: Response): Observable { + private getErrorDoc(id: string, error: HttpErrorResponse): Observable { this.logger.error('Error fetching document', error); this.cache.delete(id); return Observable.of({ diff --git a/aio/src/app/embedded/api/api-list.component.spec.ts b/aio/src/app/embedded/api/api-list.component.spec.ts index 118906a231..c62618c85c 100644 --- a/aio/src/app/embedded/api/api-list.component.spec.ts +++ b/aio/src/app/embedded/api/api-list.component.spec.ts @@ -26,10 +26,6 @@ describe('ApiListComponent', () => { sections = getApiSections(); }); - it('should be creatable', () => { - expect(component).toBeDefined(); - }); - /** * Expectation Utility: Assert that filteredSections has the expected result for this test * @param itemTest - return true if the item passes the match test diff --git a/aio/src/app/embedded/api/api.service.spec.ts b/aio/src/app/embedded/api/api.service.spec.ts index c7bf9b060c..a8452c5469 100644 --- a/aio/src/app/embedded/api/api.service.spec.ts +++ b/aio/src/app/embedded/api/api.service.spec.ts @@ -1,6 +1,6 @@ -import { ReflectiveInjector } from '@angular/core'; -import { Http, ConnectionBackend, RequestOptions, BaseRequestOptions, Response, ResponseOptions } from '@angular/http'; -import { MockBackend, MockConnection } from '@angular/http/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Injector } from '@angular/core'; +import { TestBed, inject } from '@angular/core/testing'; import { Logger } from 'app/shared/logger.service'; @@ -8,35 +8,27 @@ import { ApiService } from './api.service'; describe('ApiService', () => { - let injector: ReflectiveInjector; + let injector: Injector; let service: ApiService; - let backend: MockBackend; - - function createResponse(body: any) { - return new Response(new ResponseOptions({ body: JSON.stringify(body) })); - } + let httpMock: HttpTestingController; beforeEach(() => { - injector = ReflectiveInjector.resolveAndCreate([ - ApiService, - { provide: ConnectionBackend, useClass: MockBackend }, - { provide: RequestOptions, useClass: BaseRequestOptions }, - Http, - { provide: Logger, useClass: TestLogger } - ]); - }); + injector = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + ApiService, + { provide: Logger, useClass: TestLogger } + ] + }); - beforeEach(() => { - backend = injector.get(ConnectionBackend); service = injector.get(ApiService); + httpMock = injector.get(HttpTestingController); }); - it('should be creatable', () => { - expect(service).toBeTruthy(); - }); + afterEach(() => httpMock.verify()); it('should not immediately connect to the server', () => { - expect(backend.connectionsArray.length).toEqual(0); + httpMock.expectNone({}); }); it('subscribers should be completed/unsubscribed when service destroyed', () => { @@ -50,9 +42,13 @@ describe('ApiService', () => { service.ngOnDestroy(); expect(completed).toBe(true); + + // Stop `httpMock.verify()` from complaining. + httpMock.expectOne({}); }); describe('#sections', () => { + it('first subscriber should fetch sections', () => { const data = [{name: 'a', title: 'A', items: []}, {name: 'b', title: 'B', items: []}]; @@ -60,7 +56,7 @@ describe('ApiService', () => { expect(sections).toEqual(data); }); - backend.connectionsArray[0].mockRespond(createResponse(data)); + httpMock.expectOne({}).flush(data); }); it('second subscriber should get previous sections and NOT trigger refetch', () => { @@ -77,27 +73,20 @@ describe('ApiService', () => { expect(sections).toEqual(data); }); - backend.connectionsArray[0].mockRespond(createResponse(data)); - - expect(backend.connectionsArray.length).toBe(1, 'server connections'); - expect(subscriptions).toBe(2, 'subscriptions'); + httpMock.expectOne({}).flush(data); }); - }); describe('#fetchSections', () => { it('should connect to the server w/ expected URL', () => { service.fetchSections(); - expect(backend.connectionsArray.length).toEqual(1); - expect(backend.connectionsArray[0].request.url).toEqual('generated/docs/api/api-list.json'); + httpMock.expectOne('generated/docs/api/api-list.json'); }); it('should refresh the #sections observable w/ new content on second call', () => { let call = 0; - let connection: MockConnection; - backend.connections.subscribe(c => connection = c); let data = [{name: 'a', title: 'A', items: []}, {name: 'b', title: 'B', items: []}]; @@ -107,12 +96,13 @@ describe('ApiService', () => { // (2) after refresh expect(sections).toEqual(data, 'call ' + call++); }); - connection.mockRespond(createResponse(data)); + + httpMock.expectOne({}).flush(data); // refresh/refetch data = [{name: 'c', title: 'C', items: []}]; service.fetchSections(); - connection.mockRespond(createResponse(data)); + httpMock.expectOne({}).flush(data); expect(call).toBe(2, 'should be called twice'); }); diff --git a/aio/src/app/embedded/api/api.service.ts b/aio/src/app/embedded/api/api.service.ts index 7c708d3265..32ee99baba 100644 --- a/aio/src/app/embedded/api/api.service.ts +++ b/aio/src/app/embedded/api/api.service.ts @@ -1,5 +1,5 @@ import { Injectable, OnDestroy } from '@angular/core'; -import { Http } from '@angular/http'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { ReplaySubject } from 'rxjs/ReplaySubject'; import { Subject } from 'rxjs/Subject'; @@ -54,7 +54,7 @@ export class ApiService implements OnDestroy { return this._sections; }; - constructor(private http: Http, private logger: Logger) { } + constructor(private http: HttpClient, private logger: Logger) { } ngOnDestroy() { this.onDestroy.next(); @@ -70,13 +70,12 @@ export class ApiService implements OnDestroy { fetchSections(src?: string) { // TODO: get URL by configuration? const url = this.apiBase + (src || this.apiListJsonDefault); - this.http.get(url) + this.http.get(url) .takeUntil(this.onDestroy) - .map(response => response.json()) .do(() => this.logger.log(`Got API sections from ${url}`)) .subscribe( sections => this.sectionsSubject.next(sections), - err => { + (err: HttpErrorResponse) => { // Todo: handle error this.logger.error(err); throw err; // rethrow for now. diff --git a/aio/src/app/embedded/contributor/contributor.service.spec.ts b/aio/src/app/embedded/contributor/contributor.service.spec.ts index 8911f5d991..02b575a812 100644 --- a/aio/src/app/embedded/contributor/contributor.service.spec.ts +++ b/aio/src/app/embedded/contributor/contributor.service.spec.ts @@ -1,39 +1,33 @@ -import { ReflectiveInjector } from '@angular/core'; -import { Http, ConnectionBackend, RequestOptions, BaseRequestOptions, Response, ResponseOptions } from '@angular/http'; -import { MockBackend } from '@angular/http/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Injector } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import { ContributorService } from './contributor.service'; import { Contributor, ContributorGroup } from './contributors.model'; describe('ContributorService', () => { - let injector: ReflectiveInjector; - let backend: MockBackend; + let injector: Injector; let contribService: ContributorService; - - function createResponse(body: any) { - return new Response(new ResponseOptions({ body: JSON.stringify(body) })); - } + let httpMock: HttpTestingController; beforeEach(() => { - injector = ReflectiveInjector.resolveAndCreate([ - ContributorService, - { provide: ConnectionBackend, useClass: MockBackend }, - { provide: RequestOptions, useClass: BaseRequestOptions }, - Http - ]); + injector = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + ContributorService + ] + }); - backend = injector.get(ConnectionBackend); contribService = injector.get(ContributorService); + httpMock = injector.get(HttpTestingController); }); - it('should be creatable', () => { - expect(contribService).toBeTruthy(); - }); + afterEach(() => httpMock.verify()); it('should make a single connection to the server', () => { - expect(backend.connectionsArray.length).toEqual(1); - expect(backend.connectionsArray[0].request.url).toEqual('generated/contributors.json'); + const req = httpMock.expectOne({}); + expect(req.request.url).toBe('generated/contributors.json'); }); describe('#contributors', () => { @@ -43,7 +37,7 @@ describe('ContributorService', () => { beforeEach(() => { testData = getTestContribs(); - backend.connectionsArray[0].mockRespond(createResponse(testData)); + httpMock.expectOne({}).flush(testData); contribService.contributors.subscribe(results => contribs = results); }); @@ -70,55 +64,54 @@ describe('ContributorService', () => { }); function getTestContribs() { - // tslint:disable:quotemark return { - "kapunahelewong": { - "name": "Kapunahele Wong", - "picture": "kapunahelewong.jpg", - "website": " https://github.com/kapunahelewong", - "twitter": "kapunahele", - "bio": "Kapunahele is a front-end developer and contributor to angular.io", - "group": "GDE" + kapunahelewong: { + name: 'Kapunahele Wong', + picture: 'kapunahelewong.jpg', + website: 'https://github.com/kapunahelewong', + twitter: 'kapunahele', + bio: 'Kapunahele is a front-end developer and contributor to angular.io', + group: 'GDE' }, - "misko": { - "name": "Miško Hevery", - "picture": "misko.jpg", - "twitter": "mhevery", - "website": "http://misko.hevery.com", - "bio": "Miško Hevery is the creator of AngularJS framework.", - "group": "Angular" + misko: { + name: 'Miško Hevery', + picture: 'misko.jpg', + twitter: 'mhevery', + website: 'http://misko.hevery.com', + bio: 'Miško Hevery is the creator of AngularJS framework.', + group: 'Angular' }, - "igor": { - "name": "Igor Minar", - "picture": "igor-minar.jpg", - "twitter": "IgorMinar", - "website": "https://google.com/+IgorMinar", - "bio": "Igor is a software engineer at Angular.", - "group": "Angular" + igor: { + name: 'Igor Minar', + picture: 'igor-minar.jpg', + twitter: 'IgorMinar', + website: 'https://google.com/+IgorMinar', + bio: 'Igor is a software engineer at Angular.', + group: 'Angular' }, - "kara": { - "name": "Kara Erickson", - "picture": "kara-erickson.jpg", - "twitter": "karaforthewin", - "website": "https://github.com/kara", - "bio": "Kara is a software engineer on the Angular team at Angular and a co-organizer of the Angular-SF Meetup. ", - "group": "Angular" + kara: { + name: 'Kara Erickson', + picture: 'kara-erickson.jpg', + twitter: 'karaforthewin', + website: 'https://github.com/kara', + bio: 'Kara is a software engineer on the Angular team at Angular and a co-organizer of the Angular-SF Meetup. ', + group: 'Angular' }, - "jeffcross": { - "name": "Jeff Cross", - "picture": "jeff-cross.jpg", - "twitter": "jeffbcross", - "website": "https://twitter.com/jeffbcross", - "bio": "Jeff was one of the earliest core team members on AngularJS.", - "group": "GDE" + jeffcross: { + name: 'Jeff Cross', + picture: 'jeff-cross.jpg', + twitter: 'jeffbcross', + website: 'https://twitter.com/jeffbcross', + bio: 'Jeff was one of the earliest core team members on AngularJS.', + group: 'GDE' }, - "naomi": { - "name": "Naomi Black", - "picture": "naomi.jpg", - "twitter": "naomitraveller", - "website": "http://google.com/+NaomiBlack", - "bio": "Naomi is Angular's TPM generalist and jack-of-all-trades.", - "group": "Angular" + naomi: { + name: 'Naomi Black', + picture: 'naomi.jpg', + twitter: 'naomitraveller', + website: 'http://google.com/+NaomiBlack', + bio: 'Naomi is Angular\'s TPM generalist and jack-of-all-trades.', + group: 'Angular' } }; } diff --git a/aio/src/app/embedded/contributor/contributor.service.ts b/aio/src/app/embedded/contributor/contributor.service.ts index 08cbb010ab..3ae09b353b 100644 --- a/aio/src/app/embedded/contributor/contributor.service.ts +++ b/aio/src/app/embedded/contributor/contributor.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Http } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; @@ -15,14 +15,12 @@ const knownGroups = ['Angular', 'GDE']; export class ContributorService { contributors: Observable; - constructor(private http: Http) { + constructor(private http: HttpClient) { this.contributors = this.getContributors(); } private getContributors() { - const contributors = this.http.get(contributorsPath) - .map(res => res.json()) - + const contributors = this.http.get<{[key: string]: Contributor}>(contributorsPath) // Create group map .map(contribs => { const contribMap = new Map(); diff --git a/aio/src/app/embedded/resource/resource.service.spec.ts b/aio/src/app/embedded/resource/resource.service.spec.ts index 273a8847da..e08e2ace26 100644 --- a/aio/src/app/embedded/resource/resource.service.spec.ts +++ b/aio/src/app/embedded/resource/resource.service.spec.ts @@ -1,39 +1,33 @@ -import { ReflectiveInjector } from '@angular/core'; -import { Http, ConnectionBackend, RequestOptions, BaseRequestOptions, Response, ResponseOptions } from '@angular/http'; -import { MockBackend } from '@angular/http/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Injector } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; import { ResourceService } from './resource.service'; import { Category, SubCategory, Resource } from './resource.model'; describe('ResourceService', () => { - let injector: ReflectiveInjector; - let backend: MockBackend; + let injector: Injector; let resourceService: ResourceService; - - function createResponse(body: any) { - return new Response(new ResponseOptions({ body: JSON.stringify(body) })); - } + let httpMock: HttpTestingController; beforeEach(() => { - injector = ReflectiveInjector.resolveAndCreate([ - ResourceService, - { provide: ConnectionBackend, useClass: MockBackend }, - { provide: RequestOptions, useClass: BaseRequestOptions }, - Http - ]); + injector = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + ResourceService + ] + }); - backend = injector.get(ConnectionBackend); resourceService = injector.get(ResourceService); + httpMock = injector.get(HttpTestingController); }); - it('should be creatable', () => { - expect(resourceService).toBeTruthy(); - }); + afterEach(() => httpMock.verify()); it('should make a single connection to the server', () => { - expect(backend.connectionsArray.length).toEqual(1); - expect(backend.connectionsArray[0].request.url).toEqual('generated/resources.json'); + const req = httpMock.expectOne({}); + expect(req.request.url).toBe('generated/resources.json'); }); describe('#categories', () => { @@ -43,7 +37,7 @@ describe('ResourceService', () => { beforeEach(() => { testData = getTestResources(); - backend.connectionsArray[0].mockRespond(createResponse(testData)); + httpMock.expectOne({}).flush(testData); resourceService.categories.subscribe(results => categories = results); }); diff --git a/aio/src/app/embedded/resource/resource.service.ts b/aio/src/app/embedded/resource/resource.service.ts index 3ce85a1b55..ff64c51e6f 100644 --- a/aio/src/app/embedded/resource/resource.service.ts +++ b/aio/src/app/embedded/resource/resource.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Http } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; @@ -14,14 +14,13 @@ const resourcesPath = CONTENT_URL_PREFIX + 'resources.json'; export class ResourceService { categories: Observable; - constructor(private http: Http) { + constructor(private http: HttpClient) { this.categories = this.getCategories(); } private getCategories(): Observable { - const categories = this.http.get(resourcesPath) - .map(res => res.json()) + const categories = this.http.get(resourcesPath) .map(data => mkCategories(data)) .publishLast(); diff --git a/aio/src/app/navigation/navigation.service.spec.ts b/aio/src/app/navigation/navigation.service.spec.ts index 09e1d2dcde..9a4e3dc7cd 100644 --- a/aio/src/app/navigation/navigation.service.spec.ts +++ b/aio/src/app/navigation/navigation.service.spec.ts @@ -1,44 +1,37 @@ -import { ReflectiveInjector } from '@angular/core'; -import { Http, ConnectionBackend, RequestOptions, BaseRequestOptions, Response, ResponseOptions } from '@angular/http'; -import { MockBackend } from '@angular/http/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { Injector } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; + import { CurrentNodes, NavigationService, NavigationViews, NavigationNode, VersionInfo } from 'app/navigation/navigation.service'; import { LocationService } from 'app/shared/location.service'; import { MockLocationService } from 'testing/location.service'; describe('NavigationService', () => { - let injector: ReflectiveInjector; - let backend: MockBackend; + let injector: Injector; let navService: NavigationService; - - function createResponse(body: any) { - return new Response(new ResponseOptions({ body: JSON.stringify(body) })); - } + let httpMock: HttpTestingController; beforeEach(() => { - injector = ReflectiveInjector.resolveAndCreate([ + injector = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ NavigationService, - { provide: LocationService, useFactory: () => new MockLocationService('a') }, - { provide: ConnectionBackend, useClass: MockBackend }, - { provide: RequestOptions, useClass: BaseRequestOptions }, - Http - ]); - }); + { provide: LocationService, useFactory: () => new MockLocationService('a') } + ] + }); - beforeEach(() => { - backend = injector.get(ConnectionBackend); navService = injector.get(NavigationService); + httpMock = injector.get(HttpTestingController); }); - it('should be creatable', () => { - expect(navService).toBeTruthy(); - }); + afterEach(() => httpMock.verify()); describe('navigationViews', () => { it('should make a single connection to the server', () => { - expect(backend.connectionsArray.length).toEqual(1); - expect(backend.connectionsArray[0].request.url).toEqual('generated/navigation.json'); + const req = httpMock.expectOne({}); + expect(req.request.url).toBe('generated/navigation.json'); }); it('should expose the server response', () => { @@ -46,7 +39,7 @@ describe('NavigationService', () => { navService.navigationViews.subscribe(views => viewsEvents.push(views)); expect(viewsEvents).toEqual([]); - backend.connectionsArray[0].mockRespond(createResponse({ TopBar: [ { url: 'a' }] })); + httpMock.expectOne({}).flush({ TopBar: [ { url: 'a' }] }); expect(viewsEvents).toEqual([{ TopBar: [ { url: 'a' }] }]); }); @@ -54,6 +47,9 @@ describe('NavigationService', () => { let completed = false; navService.navigationViews.subscribe(null, null, () => completed = true); expect(true).toBe(true, 'observable completed'); + + // Stop `$httpMock.verify()` from complaining. + httpMock.expectOne({}); }); it('should return the same object to all subscribers', () => { @@ -63,16 +59,16 @@ describe('NavigationService', () => { let views2: NavigationViews; navService.navigationViews.subscribe(views => views2 = views); - backend.connectionsArray[0].mockRespond(createResponse({ TopBar: [{ url: 'a' }] })); - - // modify the response so we can check that future subscriptions do not trigger another request - backend.connectionsArray[0].response.next(createResponse({ TopBar: [{ url: 'error 1' }] })); + httpMock.expectOne({}).flush({ TopBar: [{ url: 'a' }] }); let views3: NavigationViews; navService.navigationViews.subscribe(views => views3 = views); expect(views2).toBe(views1); expect(views3).toBe(views1); + + // Verfy that subsequent subscriptions did not trigger another request. + httpMock.expectNone({}); }); it('should do WHAT(?) if the request fails'); @@ -90,7 +86,7 @@ describe('NavigationService', () => { beforeEach(() => { navService.navigationViews.subscribe(views => view = views['sideNav']); - backend.connectionsArray[0].mockRespond(createResponse({sideNav})); + httpMock.expectOne({}).flush({sideNav}); }); it('should have the supplied tooltip', () => { @@ -135,9 +131,9 @@ describe('NavigationService', () => { }; beforeEach(() => { - locationService = injector.get(LocationService); + locationService = injector.get(LocationService) as any as MockLocationService; navService.currentNodes.subscribe(selected => currentNodes = selected); - backend.connectionsArray[0].mockRespond(createResponse(navJson)); + httpMock.expectOne({}).flush(navJson); }); it('should list the side navigation node that matches the current location, and all its ancestors', () => { @@ -231,9 +227,9 @@ describe('NavigationService', () => { beforeEach(() => { navService.versionInfo.subscribe(info => versionInfo = info); - backend.connectionsArray[0].mockRespond(createResponse({ + httpMock.expectOne({}).flush({ __versionInfo: expectedVersionInfo - })); + }); }); it('should extract the version info', () => { @@ -261,7 +257,7 @@ describe('NavigationService', () => { }); it('should extract the docVersions', () => { - backend.connectionsArray[0].mockRespond(createResponse({ docVersions })); + httpMock.expectOne({}).flush({ docVersions }); expect(actualDocVersions).toEqual(expectedDocVersions); }); }); diff --git a/aio/src/app/navigation/navigation.service.ts b/aio/src/app/navigation/navigation.service.ts index 3c665f4ef2..eddcbca91c 100644 --- a/aio/src/app/navigation/navigation.service.ts +++ b/aio/src/app/navigation/navigation.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { Http } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs/Observable'; import { AsyncSubject } from 'rxjs/AsyncSubject'; @@ -36,7 +36,7 @@ export class NavigationService { */ currentNodes: Observable; - constructor(private http: Http, private location: LocationService) { + constructor(private http: HttpClient, private location: LocationService) { const navigationInfo = this.fetchNavigationInfo(); this.navigationViews = this.getNavigationViews(navigationInfo); @@ -57,8 +57,7 @@ export class NavigationService { * We are not storing the subscription from connecting as we do not expect this service to be destroyed. */ private fetchNavigationInfo(): Observable { - const navigationInfo = this.http.get(navigationPath) - .map(res => res.json() as NavigationResponse) + const navigationInfo = this.http.get(navigationPath) .publishLast(); navigationInfo.connect(); return navigationInfo; diff --git a/aio/src/app/shared/custom-md-icon-registry.spec.ts b/aio/src/app/shared/custom-md-icon-registry.spec.ts index d7a1db03b0..30480555bd 100644 --- a/aio/src/app/shared/custom-md-icon-registry.spec.ts +++ b/aio/src/app/shared/custom-md-icon-registry.spec.ts @@ -12,7 +12,7 @@ describe('CustomMdIconRegistry', () => { ]; const registry = new CustomMdIconRegistry(mockHttp, mockSanitizer, svgIcons); let svgElement: SVGElement; - registry.getNamedSvgIcon('test_icon', null).subscribe(el => svgElement = el as SVGElement); + registry.getNamedSvgIcon('test_icon').subscribe(el => svgElement = el); expect(svgElement).toEqual(createSvg(svgSrc)); }); @@ -27,8 +27,12 @@ describe('CustomMdIconRegistry', () => { spyOn(MdIconRegistry.prototype, 'getNamedSvgIcon'); const registry = new CustomMdIconRegistry(mockHttp, mockSanitizer, svgIcons); - registry.getNamedSvgIcon('other_icon', null); - expect(MdIconRegistry.prototype.getNamedSvgIcon).toHaveBeenCalledWith('other_icon', null); + + registry.getNamedSvgIcon('other_icon'); + expect(MdIconRegistry.prototype.getNamedSvgIcon).toHaveBeenCalledWith('other_icon', undefined); + + registry.getNamedSvgIcon('other_icon', 'foo'); + expect(MdIconRegistry.prototype.getNamedSvgIcon).toHaveBeenCalledWith('other_icon', 'foo'); }); }); diff --git a/aio/src/app/shared/custom-md-icon-registry.ts b/aio/src/app/shared/custom-md-icon-registry.ts index 77b05d9169..e2a901d464 100644 --- a/aio/src/app/shared/custom-md-icon-registry.ts +++ b/aio/src/app/shared/custom-md-icon-registry.ts @@ -1,7 +1,7 @@ import { InjectionToken, Inject, Injectable } from '@angular/core'; import { of } from 'rxjs/observable/of'; import { MdIconRegistry } from '@angular/material'; -import { Http } from '@angular/http'; +import { HttpClient } from '@angular/common/http'; import { DomSanitizer } from '@angular/platform-browser'; /** @@ -27,6 +27,19 @@ interface SvgIconMap { [iconName: string]: SVGElement; } +// +// @angular/material's `MdIconRegitry` currently (v2.0.0-beta.8) requires an instance of `Http` +// (from @angular/http). It is only used to [get some text content][1], so we can create a wrapper +// around `HttpClient` and pretend it is `Http`. +// [1]: https://github.com/angular/material2/blob/2.0.0-beta.8/src/lib/icon/icon-registry.ts#L465-L466 +// +function createFakeHttp(http: HttpClient): any { + return { + get: (url: string) => http.get(url, {responseType: 'text'}) + .map(data => ({text: () => data})) + }; +} + /** * A custom replacement for Angular Material's `MdIconRegistry`, which allows * us to provide preloaded icon SVG sources. @@ -35,14 +48,14 @@ interface SvgIconMap { export class CustomMdIconRegistry extends MdIconRegistry { private preloadedSvgElements: SvgIconMap = {}; - constructor(http: Http, sanitizer: DomSanitizer, @Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) { - super(http, sanitizer); + constructor(http: HttpClient, sanitizer: DomSanitizer, @Inject(SVG_ICONS) svgIcons: SvgIconInfo[]) { + super(createFakeHttp(http), sanitizer); this.loadSvgElements(svgIcons); } - getNamedSvgIcon(iconName, namespace) { + getNamedSvgIcon(iconName: string, namespace?: string) { if (this.preloadedSvgElements[iconName]) { - return of(this.preloadedSvgElements[iconName].cloneNode(true)); + return of(this.preloadedSvgElements[iconName].cloneNode(true) as SVGElement); } return super.getNamedSvgIcon(iconName, namespace); } diff --git a/aio/src/app/shared/scroll-spy.service.spec.ts b/aio/src/app/shared/scroll-spy.service.spec.ts index 7c7702fa3f..c08f0d0e52 100644 --- a/aio/src/app/shared/scroll-spy.service.spec.ts +++ b/aio/src/app/shared/scroll-spy.service.spec.ts @@ -160,10 +160,6 @@ describe('ScrollSpyService', () => { }); - it('should be creatable', () => { - expect(scrollSpyService).toBeTruthy(); - }); - describe('#spyOn()', () => { let getSpiedElemGroups: () => ScrollSpiedElementGroup[]; diff --git a/aio/src/app/shared/toc.service.spec.ts b/aio/src/app/shared/toc.service.spec.ts index a40385f4e1..85ca45480b 100644 --- a/aio/src/app/shared/toc.service.spec.ts +++ b/aio/src/app/shared/toc.service.spec.ts @@ -31,10 +31,6 @@ describe('TocService', () => { tocService.tocList.subscribe(tocList => lastTocList = tocList); }); - it('should be creatable', () => { - expect(tocService).toBeTruthy(); - }); - describe('tocList', () => { it('should emit the latest value to new subscribers', () => { const expectedValue1 = tocItem('Heading A'); diff --git a/aio/src/test.ts b/aio/src/test.ts index 722acc0395..949eddf8d7 100644 --- a/aio/src/test.ts +++ b/aio/src/test.ts @@ -14,19 +14,19 @@ import { } from '@angular/platform-browser-dynamic/testing'; // List vendors here to increase test rebuild performance. +import '@angular/animations'; import '@angular/common'; import '@angular/common/testing'; +import '@angular/common/http'; +import '@angular/common/http/testing'; import '@angular/core/'; import '@angular/core/testing'; +import '@angular/material'; import '@angular/platform-browser'; import '@angular/platform-browser/testing'; import '@angular/platform-browser/animations'; import '@angular/platform-browser-dynamic'; import '@angular/platform-browser-dynamic/testing'; -import '@angular/http'; -import '@angular/http/testing'; -import '@angular/animations'; -import '@angular/material'; import '@angular/service-worker'; import 'rxjs'; // tslint:disable-line diff --git a/aio/src/testing/nav-map-json-response.ts b/aio/src/testing/nav-map-json-response.ts deleted file mode 100644 index a55acb6b50..0000000000 --- a/aio/src/testing/nav-map-json-response.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { Response } from '@angular/http'; - -// tslint:disable:quotemark -export function getTestNavMapResponse(): Response { - - const navMapJson = { "nodes": [ - { - "docId": "guide/quickstart", - "navTitle": "Quickstart", - "tooltip": "A quick look at an Angular app." - }, - - { - "docId": "guide/cli-quickstart", - "navTitle": "CLI Quickstart", - "tooltip": "A quick look at an Angular app built with the Angular CLI.", - "hide": true // <----- SHOULD BE FILTERED OUT - }, - - { - "navTitle": "Tutorial", - "children": [ - { - "docId": " tutorial/", - "navTitle": "Introduction", - "tooltip": "Introduction to the Tour of Heroes tutorial" - }, - { - "docId": "tutorial/toh-pt1", - "navTitle": "The Hero Editor", - "tooltip": "Build a simple hero editor." - } - ] - }, - - { - "navTitle": "Getting started", - "tooltip": "A gentle introduction to Angular", - "children": [ - { - "docId": "guide/docs-overview", - "navTitle": "Overview", - "tooltip": "How to read and use this documentation." - }, - { - "docId": "guide/setup", - "navTitle": "Setup", - "tooltip": "Install the Angular QuickStart seed for faster, more efficient development on your machine." - } - ] - }, - - { - "navTitle": "Core", - "tooltip": "Learn the core capabilities of Angular", - "children": [ - { - "docId": "guide/NgModule", - "navTitle": "Angular Modules (NgModule)", - "tooltip": "Define application modules with @NgModule." - }, - { - "docId": "guide/directives", - "navTitle": "Directives", - "tooltip": "Learn how directives modify the layout and behavior of elements.", - "children": [ - { - "docId": "guide/attribute-directives", - "navTitle": "Attribute directives", - "tooltip": "Attribute directives attach behavior to elements." - }, - { - "docId": "guide/structural-directives", - "navTitle": "Structural directives", - "tooltip": "Structural directives manipulate the layout of the page." - } - ] - } - ] - }, - { - "navTitle": "Empty Heading", - "children": [ ] - }, - { - "navTitle": "External", - "children": [ - { - "url": "https://gitter.im/angular/angular", - "navTitle": "Gitter", - "tooltip": "Chat about Angular with other birds of a feather" - } - ] - } - ]}; - - // tslint:enable:quotemark - return { - status: 200, - json: () => navMapJson - } as Response; -} diff --git a/aio/yarn.lock b/aio/yarn.lock index 9f6de42c1f..6efafbcf82 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -168,7 +168,7 @@ base64-js "^1.1.2" jshashes "^1.0.5" -"@angular/tsc-wrapped@^5.0.0-beta.3": +"@angular/tsc-wrapped@5.0.0-beta.3": version "5.0.0-beta.3" resolved "https://registry.yarnpkg.com/@angular/tsc-wrapped/-/tsc-wrapped-5.0.0-beta.3.tgz#d71c607b02eb6fe7091b908ef2ec97180ec52618" dependencies: