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
```
This commit is contained in:
Georgios Kalpakas 2017-08-11 20:16:55 +03:00 committed by Hans
parent 3efd4a15d6
commit 38addacda0
20 changed files with 279 additions and 398 deletions

View File

@ -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}<h2 id="#somewhere">Some heading</h2>`;
data = { id, contents };
}
return of({ json: () => data });
return of(data);
}
}

View File

@ -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,

View File

@ -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) }));
}
describe('DocumentService', () => {
let httpMock: HttpTestingController;
function createInjector(initialUrl: string) {
return ReflectiveInjector.resolveAndCreate([
return TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
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);
httpMock = injector.get(HttpTestingController) as HttpTestingController;
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();
});
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 { 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: '<h1>Page Not Found</h1>' };
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: '<h1>Page Not Found</h1>' };
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');
});
});
});

View File

@ -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<DocumentContents> {
const requestPath = `${DOC_CONTENT_URL_PREFIX}${id}.json`;
const subject = new AsyncSubject<DocumentContents>();
this.logger.log('fetching document from', requestPath);
const subject = new AsyncSubject();
this.http
.get(requestPath)
.map(response => response.json())
.catch((error: Response) => {
.get<DocumentContents>(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<DocumentContents> {
private getErrorDoc(id: string, error: HttpErrorResponse): Observable<DocumentContents> {
this.logger.error('Error fetching document', error);
this.cache.delete(id);
return Observable.of({

View File

@ -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

View File

@ -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([
injector = TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
ApiService,
{ provide: ConnectionBackend, useClass: MockBackend },
{ provide: RequestOptions, useClass: BaseRequestOptions },
Http,
{ 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');
});

View File

@ -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<ApiSection[]>(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.

View File

@ -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'
}
};
}

View File

@ -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<ContributorGroup[]>;
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<string, Contributor[]>();

View File

@ -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);
});

View File

@ -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<Category[]>;
constructor(private http: Http) {
constructor(private http: HttpClient) {
this.categories = this.getCategories();
}
private getCategories(): Observable<Category[]> {
const categories = this.http.get(resourcesPath)
.map(res => res.json())
const categories = this.http.get<any>(resourcesPath)
.map(data => mkCategories(data))
.publishLast();

View File

@ -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);
});
});

View File

@ -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<CurrentNodes>;
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<NavigationResponse> {
const navigationInfo = this.http.get(navigationPath)
.map(res => res.json() as NavigationResponse)
const navigationInfo = this.http.get<NavigationResponse>(navigationPath)
.publishLast();
navigationInfo.connect();
return navigationInfo;

View File

@ -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');
});
});

View File

@ -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;
}
// <hack-alert>
// @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
// </hack-alert>
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);
}

View File

@ -160,10 +160,6 @@ describe('ScrollSpyService', () => {
});
it('should be creatable', () => {
expect(scrollSpyService).toBeTruthy();
});
describe('#spyOn()', () => {
let getSpiedElemGroups: () => ScrollSpiedElementGroup[];

View File

@ -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');

View File

@ -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

View File

@ -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;
}

View File

@ -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: