2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
2016-08-25 03:50:16 -04:00
|
|
|
import {Injectable} from '@angular/core';
|
2016-08-30 21:07:40 -04:00
|
|
|
import {Connection, ConnectionBackend, ReadyState, Request, Response} from '@angular/http';
|
2018-02-27 17:06:06 -05:00
|
|
|
import {ReplaySubject, Subject} from 'rxjs';
|
|
|
|
import {take} from 'rxjs/operators';
|
2015-04-29 02:07:55 -04:00
|
|
|
|
2016-08-10 18:55:18 -04:00
|
|
|
|
2015-04-29 02:07:55 -04:00
|
|
|
/**
|
2015-06-09 18:18:57 -04:00
|
|
|
*
|
2015-06-24 03:27:07 -04:00
|
|
|
* Mock Connection to represent a {@link Connection} for tests.
|
2015-06-09 18:18:57 -04:00
|
|
|
*
|
2018-09-20 10:01:01 -04:00
|
|
|
* @usageNotes
|
|
|
|
* ### Example of `mockRespond()`
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* var connection;
|
|
|
|
* backend.connections.subscribe(c => connection = c);
|
|
|
|
* http.request('data.json').subscribe(res => console.log(res.text()));
|
|
|
|
* connection.mockRespond(new Response(new ResponseOptions({ body: 'fake response' }))); //logs
|
|
|
|
* 'fake response'
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* ### Example of `mockError()`
|
|
|
|
*
|
|
|
|
* ```
|
|
|
|
* var connection;
|
|
|
|
* backend.connections.subscribe(c => connection = c);
|
|
|
|
* http.request('data.json').subscribe(res => res, err => console.log(err)));
|
|
|
|
* connection.mockError(new Error('error'));
|
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 16:09:01 -04:00
|
|
|
* @deprecated see https://angular.io/guide/http
|
2018-10-19 12:47:13 -04:00
|
|
|
* @publicApi
|
2016-06-27 15:27:23 -04:00
|
|
|
*/
|
2015-08-26 14:41:41 -04:00
|
|
|
export class MockConnection implements Connection {
|
2018-04-08 17:19:25 -04:00
|
|
|
// TODO: Name `readyState` should change to be more generic, and states could be made to be more
|
2016-08-17 12:24:44 -04:00
|
|
|
// descriptive than ResourceLoader states.
|
2015-04-29 02:07:55 -04:00
|
|
|
/**
|
2015-06-09 18:18:57 -04:00
|
|
|
* Describes the state of the connection, based on `XMLHttpRequest.readyState`, but with
|
|
|
|
* additional states. For example, state 5 indicates an aborted connection.
|
|
|
|
*/
|
2015-12-03 16:44:14 -05:00
|
|
|
readyState: ReadyState;
|
2015-04-29 02:07:55 -04:00
|
|
|
|
|
|
|
/**
|
2015-06-09 18:18:57 -04:00
|
|
|
* {@link Request} instance used to create the connection.
|
|
|
|
*/
|
2015-04-29 02:07:55 -04:00
|
|
|
request: Request;
|
2015-06-09 18:18:57 -04:00
|
|
|
|
|
|
|
/**
|
2015-06-24 03:27:07 -04:00
|
|
|
* {@link EventEmitter} of {@link Response}. Can be subscribed to in order to be notified when a
|
|
|
|
* response is available.
|
2015-06-09 18:18:57 -04:00
|
|
|
*/
|
2016-02-01 20:05:50 -05:00
|
|
|
response: ReplaySubject<Response>;
|
2015-04-29 02:07:55 -04:00
|
|
|
|
|
|
|
constructor(req: Request) {
|
2018-02-27 17:06:06 -05:00
|
|
|
this.response = <any>new ReplaySubject(1).pipe(take(1));
|
2015-12-03 16:44:14 -05:00
|
|
|
this.readyState = ReadyState.Open;
|
2015-04-29 02:07:55 -04:00
|
|
|
this.request = req;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-06-09 18:18:57 -04:00
|
|
|
* Sends a mock response to the connection. This response is the value that is emitted to the
|
2015-06-24 03:27:07 -04:00
|
|
|
* {@link EventEmitter} returned by {@link Http}.
|
2015-06-09 18:18:57 -04:00
|
|
|
*
|
|
|
|
*/
|
2015-04-29 02:07:55 -04:00
|
|
|
mockRespond(res: Response) {
|
2015-12-03 16:44:14 -05:00
|
|
|
if (this.readyState === ReadyState.Done || this.readyState === ReadyState.Cancelled) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error('Connection has already been resolved');
|
2015-04-29 02:07:55 -04:00
|
|
|
}
|
2015-12-03 16:44:14 -05:00
|
|
|
this.readyState = ReadyState.Done;
|
2015-10-01 19:04:20 -04:00
|
|
|
this.response.next(res);
|
|
|
|
this.response.complete();
|
2015-04-29 02:07:55 -04:00
|
|
|
}
|
|
|
|
|
2015-06-09 18:18:57 -04:00
|
|
|
/**
|
|
|
|
* Not yet implemented!
|
|
|
|
*
|
|
|
|
* Sends the provided {@link Response} to the `downloadObserver` of the `Request`
|
|
|
|
* associated with this connection.
|
|
|
|
*/
|
2015-04-29 02:07:55 -04:00
|
|
|
mockDownload(res: Response) {
|
2015-06-09 18:18:57 -04:00
|
|
|
// this.request.downloadObserver.onNext(res);
|
|
|
|
// if (res.bytesLoaded === res.totalBytes) {
|
|
|
|
// this.request.downloadObserver.onCompleted();
|
|
|
|
// }
|
2015-04-29 02:07:55 -04:00
|
|
|
}
|
|
|
|
|
2015-06-09 18:18:57 -04:00
|
|
|
// TODO(jeffbcross): consider using Response type
|
|
|
|
/**
|
2015-06-24 03:27:07 -04:00
|
|
|
* Emits the provided error object as an error to the {@link Response} {@link EventEmitter}
|
|
|
|
* returned
|
2015-06-09 18:18:57 -04:00
|
|
|
* from {@link Http}.
|
2016-06-02 20:30:40 -04:00
|
|
|
*
|
2015-06-09 18:18:57 -04:00
|
|
|
*/
|
2015-07-07 23:03:00 -04:00
|
|
|
mockError(err?: Error) {
|
2016-08-17 12:24:44 -04:00
|
|
|
// Matches ResourceLoader semantics
|
2015-12-03 16:44:14 -05:00
|
|
|
this.readyState = ReadyState.Done;
|
2015-10-01 19:04:20 -04:00
|
|
|
this.response.error(err);
|
2015-04-29 02:07:55 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-09 18:18:57 -04:00
|
|
|
/**
|
|
|
|
* A mock backend for testing the {@link Http} service.
|
|
|
|
*
|
2015-10-11 01:11:13 -04:00
|
|
|
* This class can be injected in tests, and should be used to override providers
|
2015-06-09 18:18:57 -04:00
|
|
|
* to other backends, such as {@link XHRBackend}.
|
|
|
|
*
|
2018-09-20 10:01:01 -04:00
|
|
|
* @usageNotes
|
2015-10-19 10:37:32 -04:00
|
|
|
* ### Example
|
2015-06-09 18:18:57 -04:00
|
|
|
*
|
|
|
|
* ```
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
* import {Injectable, Injector} from '@angular/core';
|
2016-12-29 12:39:00 -05:00
|
|
|
* import {async, fakeAsync, tick} from '@angular/core/testing';
|
|
|
|
* import {BaseRequestOptions, ConnectionBackend, Http, RequestOptions} from '@angular/http';
|
|
|
|
* import {Response, ResponseOptions} from '@angular/http';
|
|
|
|
* import {MockBackend, MockConnection} from '@angular/http/testing';
|
|
|
|
*
|
|
|
|
* const HERO_ONE = 'HeroNrOne';
|
|
|
|
* const HERO_TWO = 'WillBeAlwaysTheSecond';
|
|
|
|
*
|
|
|
|
* @Injectable()
|
|
|
|
* class HeroService {
|
|
|
|
* constructor(private http: Http) {}
|
|
|
|
*
|
|
|
|
* getHeroes(): Promise<String[]> {
|
|
|
|
* return this.http.get('myservices.de/api/heroes')
|
|
|
|
* .toPromise()
|
|
|
|
* .then(response => response.json().data)
|
|
|
|
* .catch(e => this.handleError(e));
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* private handleError(error: any): Promise<any> {
|
|
|
|
* console.error('An error occurred', error);
|
|
|
|
* return Promise.reject(error.message || error);
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* describe('MockBackend HeroService Example', () => {
|
|
|
|
* beforeEach(() => {
|
perf: switch angular to use StaticInjector instead of ReflectiveInjector
This change allows ReflectiveInjector to be tree shaken resulting
in not needed Reflect polyfil and smaller bundles.
Code savings for HelloWorld using Closure:
Reflective: bundle.js: 105,864(34,190 gzip)
Static: bundle.js: 154,889(33,555 gzip)
645( 2%)
BREAKING CHANGE:
`platformXXXX()` no longer accepts providers which depend on reflection.
Specifically the method signature when from `Provider[]` to
`StaticProvider[]`.
Example:
Before:
```
[
MyClass,
{provide: ClassA, useClass: SubClassA}
]
```
After:
```
[
{provide: MyClass, deps: [Dep1,...]},
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
]
```
NOTE: This only applies to platform creation and providers for the JIT
compiler. It does not apply to `@Compotent` or `@NgModule` provides
declarations.
Benchpress note: Previously Benchpress also supported reflective
provides, which now require static providers.
DEPRECATION:
- `ReflectiveInjector` is now deprecated as it will be remove. Use
`Injector.create` as a replacement.
closes #18496
2017-08-03 15:33:29 -04:00
|
|
|
* this.injector = Injector.create([
|
2016-12-29 12:39:00 -05:00
|
|
|
* {provide: ConnectionBackend, useClass: MockBackend},
|
|
|
|
* {provide: RequestOptions, useClass: BaseRequestOptions},
|
|
|
|
* Http,
|
|
|
|
* HeroService,
|
|
|
|
* ]);
|
|
|
|
* this.heroService = this.injector.get(HeroService);
|
|
|
|
* this.backend = this.injector.get(ConnectionBackend) as MockBackend;
|
|
|
|
* this.backend.connections.subscribe((connection: any) => this.lastConnection = connection);
|
2015-06-09 18:18:57 -04:00
|
|
|
* });
|
2016-12-29 12:39:00 -05:00
|
|
|
*
|
|
|
|
* it('getHeroes() should query current service url', () => {
|
|
|
|
* this.heroService.getHeroes();
|
|
|
|
* expect(this.lastConnection).toBeDefined('no http service connection at all?');
|
|
|
|
* expect(this.lastConnection.request.url).toMatch(/api\/heroes$/, 'url invalid');
|
|
|
|
* });
|
|
|
|
*
|
|
|
|
* it('getHeroes() should return some heroes', fakeAsync(() => {
|
|
|
|
* let result: String[];
|
|
|
|
* this.heroService.getHeroes().then((heroes: String[]) => result = heroes);
|
|
|
|
* this.lastConnection.mockRespond(new Response(new ResponseOptions({
|
|
|
|
* body: JSON.stringify({data: [HERO_ONE, HERO_TWO]}),
|
|
|
|
* })));
|
|
|
|
* tick();
|
|
|
|
* expect(result.length).toEqual(2, 'should contain given amount of heroes');
|
|
|
|
* expect(result[0]).toEqual(HERO_ONE, ' HERO_ONE should be the first hero');
|
|
|
|
* expect(result[1]).toEqual(HERO_TWO, ' HERO_TWO should be the second hero');
|
|
|
|
* }));
|
|
|
|
*
|
|
|
|
* it('getHeroes() while server is down', fakeAsync(() => {
|
|
|
|
* let result: String[];
|
|
|
|
* let catchedError: any;
|
|
|
|
* this.heroService.getHeroes()
|
|
|
|
* .then((heroes: String[]) => result = heroes)
|
|
|
|
* .catch((error: any) => catchedError = error);
|
2018-08-04 12:05:46 -04:00
|
|
|
* this.lastConnection.mockError(new Response(new ResponseOptions({
|
2016-12-29 12:39:00 -05:00
|
|
|
* status: 404,
|
|
|
|
* statusText: 'URL not Found',
|
|
|
|
* })));
|
|
|
|
* tick();
|
|
|
|
* expect(result).toBeUndefined();
|
|
|
|
* expect(catchedError).toBeDefined();
|
|
|
|
* }));
|
|
|
|
* });
|
2015-06-09 18:18:57 -04:00
|
|
|
* ```
|
|
|
|
*
|
2018-06-01 16:09:01 -04:00
|
|
|
* @deprecated see https://angular.io/guide/http
|
2018-10-19 12:47:13 -04:00
|
|
|
* @publicApi
|
2016-06-27 15:27:23 -04:00
|
|
|
*/
|
2015-04-29 02:07:55 -04:00
|
|
|
@Injectable()
|
2015-08-26 14:41:41 -04:00
|
|
|
export class MockBackend implements ConnectionBackend {
|
2015-06-09 18:18:57 -04:00
|
|
|
/**
|
2015-06-24 03:27:07 -04:00
|
|
|
* {@link EventEmitter}
|
2015-06-09 18:18:57 -04:00
|
|
|
* of {@link MockConnection} instances that have been created by this backend. Can be subscribed
|
|
|
|
* to in order to respond to connections.
|
|
|
|
*
|
|
|
|
* This property only exists in the mock implementation, not in real Backends.
|
|
|
|
*/
|
2015-10-01 19:04:20 -04:00
|
|
|
connections: any; //<MockConnection>
|
2015-06-09 18:18:57 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* An array representation of `connections`. This array will be updated with each connection that
|
|
|
|
* is created by this backend.
|
|
|
|
*
|
|
|
|
* This property only exists in the mock implementation, not in real Backends.
|
|
|
|
*/
|
2015-08-28 14:29:19 -04:00
|
|
|
connectionsArray: MockConnection[];
|
2015-06-09 18:18:57 -04:00
|
|
|
/**
|
2015-06-24 03:27:07 -04:00
|
|
|
* {@link EventEmitter} of {@link MockConnection} instances that haven't yet been resolved (i.e.
|
|
|
|
* with a `readyState`
|
2015-06-09 18:18:57 -04:00
|
|
|
* less than 4). Used internally to verify that no connections are pending via the
|
|
|
|
* `verifyNoPendingRequests` method.
|
|
|
|
*
|
|
|
|
* This property only exists in the mock implementation, not in real Backends.
|
|
|
|
*/
|
2015-10-01 19:04:20 -04:00
|
|
|
pendingConnections: any; // Subject<MockConnection>
|
2015-04-29 02:07:55 -04:00
|
|
|
constructor() {
|
|
|
|
this.connectionsArray = [];
|
2015-10-01 19:04:20 -04:00
|
|
|
this.connections = new Subject();
|
2016-06-08 19:38:52 -04:00
|
|
|
this.connections.subscribe(
|
|
|
|
(connection: MockConnection) => this.connectionsArray.push(connection));
|
2015-10-01 19:04:20 -04:00
|
|
|
this.pendingConnections = new Subject();
|
2015-04-29 02:07:55 -04:00
|
|
|
}
|
|
|
|
|
2015-06-09 18:18:57 -04:00
|
|
|
/**
|
|
|
|
* Checks all connections, and raises an exception if any connection has not received a response.
|
|
|
|
*
|
|
|
|
* This method only exists in the mock implementation, not in real Backends.
|
|
|
|
*/
|
2015-04-29 02:07:55 -04:00
|
|
|
verifyNoPendingRequests() {
|
|
|
|
let pending = 0;
|
2016-02-01 20:05:50 -05:00
|
|
|
this.pendingConnections.subscribe((c: MockConnection) => pending++);
|
2016-08-25 03:50:16 -04:00
|
|
|
if (pending > 0) throw new Error(`${pending} pending connections to be resolved`);
|
2015-04-29 02:07:55 -04:00
|
|
|
}
|
|
|
|
|
2015-06-09 18:18:57 -04:00
|
|
|
/**
|
|
|
|
* Can be used in conjunction with `verifyNoPendingRequests` to resolve any not-yet-resolve
|
|
|
|
* connections, if it's expected that there are connections that have not yet received a response.
|
|
|
|
*
|
|
|
|
* This method only exists in the mock implementation, not in real Backends.
|
|
|
|
*/
|
2016-02-01 20:05:50 -05:00
|
|
|
resolveAllConnections() { this.connections.subscribe((c: MockConnection) => c.readyState = 4); }
|
2015-04-29 02:07:55 -04:00
|
|
|
|
2015-06-09 18:18:57 -04:00
|
|
|
/**
|
|
|
|
* Creates a new {@link MockConnection}. This is equivalent to calling `new
|
|
|
|
* MockConnection()`, except that it also will emit the new `Connection` to the `connections`
|
2015-06-24 03:27:07 -04:00
|
|
|
* emitter of this `MockBackend` instance. This method will usually only be used by tests
|
2015-06-09 18:18:57 -04:00
|
|
|
* against the framework itself, not by end-users.
|
|
|
|
*/
|
2016-02-01 20:05:50 -05:00
|
|
|
createConnection(req: Request): MockConnection {
|
2016-08-30 21:07:40 -04:00
|
|
|
if (!req || !(req instanceof Request)) {
|
2016-08-25 03:50:16 -04:00
|
|
|
throw new Error(`createConnection requires an instance of Request, got ${req}`);
|
2015-04-29 02:07:55 -04:00
|
|
|
}
|
2016-11-12 08:08:58 -05:00
|
|
|
const connection = new MockConnection(req);
|
2015-10-01 19:04:20 -04:00
|
|
|
this.connections.next(connection);
|
2015-04-29 02:07:55 -04:00
|
|
|
return connection;
|
|
|
|
}
|
|
|
|
}
|