@@ -2,31 +2,40 @@ import {Injectable} from 'angular2/di';
22import { Request } from 'angular2/src/http/static_request' ;
33import { Response } from 'angular2/src/http/static_response' ;
44import { ReadyStates } from 'angular2/src/http/enums' ;
5+ import { Connection , ConnectionBackend } from 'angular2/src/http/interfaces' ;
56import * as Rx from 'rx' ;
67
78/**
8- * Connection represents a request and response for an underlying transport, like XHR or mock.
9- * The mock implementation contains helper methods to respond to connections within tests.
10- * API subject to change and expand.
9+ *
10+ * Connection class used by MockBackend
11+ *
12+ * This class is typically not instantiated directly, but instances can be retrieved by subscribing
13+ *to the `connections` Observable of
14+ * {@link MockBackend} in order to mock responses to requests.
15+ *
1116 **/
12- export class Connection {
17+ export class MockConnection implements Connection {
18+ // TODO Name `readyState` should change to be more generic, and states could be made to be more
19+ // descriptive than XHR states.
1320 /**
14- * Observer to call on download progress, if provided in config.
15- **/
16- downloadObserver : Rx . Observer < Response > ;
21+ * Describes the state of the connection, based on `XMLHttpRequest.readyState`, but with
22+ * additional states. For example, state 5 indicates an aborted connection.
23+ */
24+ readyState : ReadyStates ;
1725
1826 /**
19- * TODO
20- * Name `readyState` should change to be more generic, and states could be made to be more
21- * descriptive than XHR states.
22- **/
23-
24- readyState : ReadyStates ;
27+ * {@link Request } instance used to create the connection.
28+ */
2529 request : Request ;
30+
31+ /**
32+ * [RxJS
33+ * Observable](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md)
34+ * of {@link Response}. Can be subscribed to in order to be notified when a response is available.
35+ */
2636 response : Rx . Subject < Response > ;
2737
2838 constructor ( req : Request ) {
29- // State
3039 if ( Rx . hasOwnProperty ( 'default' ) ) {
3140 this . response = new ( ( < any > Rx ) . default . Rx . Subject ) ( ) ;
3241 } else {
@@ -38,15 +47,29 @@ export class Connection {
3847 this . dispose = this . dispose . bind ( this ) ;
3948 }
4049
50+ /**
51+ * Changes the `readyState` of the connection to a custom state of 5 (cancelled).
52+ */
4153 dispose ( ) {
4254 if ( this . readyState !== ReadyStates . DONE ) {
4355 this . readyState = ReadyStates . CANCELLED ;
4456 }
4557 }
4658
4759 /**
48- * Called after a connection has been established.
49- **/
60+ * Sends a mock response to the connection. This response is the value that is emitted to the
61+ * `Observable` returned by {@link Http}.
62+ *
63+ * #Example
64+ *
65+ * ```
66+ * var connection;
67+ * backend.connections.subscribe(c => connection = c);
68+ * http.request('data.json').subscribe(res => console.log(res.text()));
69+ * connection.mockRespond(new Response('fake response')); //logs 'fake response'
70+ * ```
71+ *
72+ */
5073 mockRespond ( res : Response ) {
5174 if ( this . readyState >= ReadyStates . DONE ) {
5275 throw new Error ( 'Connection has already been resolved' ) ;
@@ -56,13 +79,24 @@ export class Connection {
5679 this . response . onCompleted ( ) ;
5780 }
5881
82+ /**
83+ * Not yet implemented!
84+ *
85+ * Sends the provided {@link Response} to the `downloadObserver` of the `Request`
86+ * associated with this connection.
87+ */
5988 mockDownload ( res : Response ) {
60- this . downloadObserver . onNext ( res ) ;
61- if ( res . bytesLoaded === res . totalBytes ) {
62- this . downloadObserver . onCompleted ( ) ;
63- }
89+ // this.request .downloadObserver.onNext(res);
90+ // if (res.bytesLoaded === res.totalBytes) {
91+ // this.request .downloadObserver.onCompleted();
92+ // }
6493 }
6594
95+ // TODO(jeffbcross): consider using Response type
96+ /**
97+ * Emits the provided error object as an error to the {@link Response} observable returned
98+ * from {@link Http}.
99+ */
66100 mockError ( err ?) {
67101 // Matches XHR semantics
68102 this . readyState = ReadyStates . DONE ;
@@ -71,35 +105,135 @@ export class Connection {
71105 }
72106}
73107
108+ /**
109+ * A mock backend for testing the {@link Http} service.
110+ *
111+ * This class can be injected in tests, and should be used to override bindings
112+ * to other backends, such as {@link XHRBackend}.
113+ *
114+ * #Example
115+ *
116+ * ```
117+ * import {MockBackend, DefaultOptions, Http} from 'angular2/http';
118+ * it('should get some data', inject([AsyncTestCompleter], (async) => {
119+ * var connection;
120+ * var injector = Injector.resolveAndCreate([
121+ * MockBackend,
122+ * bind(Http).toFactory((backend, defaultOptions) => {
123+ * return new Http(backend, defaultOptions)
124+ * }, [MockBackend, DefaultOptions])]);
125+ * var http = injector.get(Http);
126+ * var backend = injector.get(MockBackend);
127+ * //Assign any newly-created connection to local variable
128+ * backend.connections.subscribe(c => connection = c);
129+ * http.request('data.json').subscribe((res) => {
130+ * expect(res.text()).toBe('awesome');
131+ * async.done();
132+ * });
133+ * connection.mockRespond(new Response('awesome'));
134+ * }));
135+ * ```
136+ *
137+ * This method only exists in the mock implementation, not in real Backends.
138+ **/
74139@Injectable ( )
75- export class MockBackend {
76- connections : Rx . Subject < Connection > ;
77- connectionsArray : Array < Connection > ;
78- pendingConnections : Rx . Observable < Connection > ;
140+ export class MockBackend implements ConnectionBackend {
141+ /**
142+ * [RxJS
143+ * Subject](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/subject.md)
144+ * of {@link MockConnection} instances that have been created by this backend. Can be subscribed
145+ * to in order to respond to connections.
146+ *
147+ * #Example
148+ *
149+ * ```
150+ * import {MockBackend, Http, BaseRequestOptions} from 'angular2/http';
151+ * import {Injector} from 'angular2/di';
152+ *
153+ * it('should get a response', () => {
154+ * var connection; //this will be set when a new connection is emitted from the backend.
155+ * var text; //this will be set from mock response
156+ * var injector = Injector.resolveAndCreate([
157+ * MockBackend,
158+ * bind(Http).toFactory(backend, options) {
159+ * return new Http(backend, options);
160+ * }, [MockBackend, BaseRequestOptions]]);
161+ * var backend = injector.get(MockBackend);
162+ * var http = injector.get(Http);
163+ * backend.connections.subscribe(c => connection = c);
164+ * http.request('something.json').subscribe(res => {
165+ * text = res.text();
166+ * });
167+ * connection.mockRespond(new Response('Something'));
168+ * expect(text).toBe('Something');
169+ * });
170+ * ```
171+ *
172+ * This property only exists in the mock implementation, not in real Backends.
173+ */
174+ connections : Rx . Subject < MockConnection > ;
175+
176+ /**
177+ * An array representation of `connections`. This array will be updated with each connection that
178+ * is created by this backend.
179+ *
180+ * This property only exists in the mock implementation, not in real Backends.
181+ */
182+ connectionsArray : Array < MockConnection > ;
183+ /**
184+ * [Observable](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md)
185+ * of {@link MockConnection} instances that haven't yet been resolved (i.e. with a `readyState`
186+ * less than 4). Used internally to verify that no connections are pending via the
187+ * `verifyNoPendingRequests` method.
188+ *
189+ * This property only exists in the mock implementation, not in real Backends.
190+ */
191+ pendingConnections : Rx . Observable < MockConnection > ;
79192 constructor ( ) {
193+ var Observable ;
80194 this . connectionsArray = [ ] ;
81195 if ( Rx . hasOwnProperty ( 'default' ) ) {
82196 this . connections = new ( < any > Rx ) . default . Rx . Subject ( ) ;
197+ Observable = ( < any > Rx ) . default . Rx . Observable ;
83198 } else {
84- this . connections = new Rx . Subject < Connection > ( ) ;
199+ this . connections = new Rx . Subject < MockConnection > ( ) ;
200+ Observable = Rx . Observable ;
85201 }
86202 this . connections . subscribe ( connection => this . connectionsArray . push ( connection ) ) ;
87- this . pendingConnections = this . connections . filter ( ( c ) => c . readyState < ReadyStates . DONE ) ;
203+ this . pendingConnections =
204+ Observable . fromArray ( this . connectionsArray ) . filter ( ( c ) => c . readyState < ReadyStates . DONE ) ;
88205 }
89206
207+ /**
208+ * Checks all connections, and raises an exception if any connection has not received a response.
209+ *
210+ * This method only exists in the mock implementation, not in real Backends.
211+ */
90212 verifyNoPendingRequests ( ) {
91213 let pending = 0 ;
92214 this . pendingConnections . subscribe ( ( c ) => pending ++ ) ;
93215 if ( pending > 0 ) throw new Error ( `${ pending } pending connections to be resolved` ) ;
94216 }
95217
218+ /**
219+ * Can be used in conjunction with `verifyNoPendingRequests` to resolve any not-yet-resolve
220+ * connections, if it's expected that there are connections that have not yet received a response.
221+ *
222+ * This method only exists in the mock implementation, not in real Backends.
223+ */
96224 resolveAllConnections ( ) { this . connections . subscribe ( ( c ) => c . readyState = 4 ) ; }
97225
226+ /**
227+ * Creates a new {@link MockConnection}. This is equivalent to calling `new
228+ * MockConnection()`, except that it also will emit the new `Connection` to the `connections`
229+ * observable of this `MockBackend` instance. This method will usually only be used by tests
230+ * against the framework itself, not by end-users.
231+ */
98232 createConnection ( req : Request ) {
99233 if ( ! req || ! ( req instanceof Request ) ) {
100234 throw new Error ( `createConnection requires an instance of Request, got ${ req } ` ) ;
101235 }
102- let connection = new Connection ( req ) ;
236+ let connection = new MockConnection ( req ) ;
103237 this . connections . onNext ( connection ) ;
104238 return connection ;
105239 }
0 commit comments