Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1215)

Side by Side Diff: remoting/webapp/crd/js/mock_xhr.js

Issue 1055313002: Added mock_xhr module w/ unit tests. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: for submit Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview A mock version of remoting.Xhr. Compared to
7 * sinon.useMockXhr, this allows unit tests to be written at a higher
8 * level, and it eliminates a fair amount of boilerplate involved in
9 * making the sinon mocks work with asynchronous calls
10 * (cf. gcd_client_unittest.js vs. gcd_client_with_mock_xhr.js for an
11 * example).
12 */
13
14 (function() {
15 'use strict';
16
17 /**
18 * @constructor
19 * @param {remoting.Xhr.Params} params
20 */
21 remoting.MockXhr = function(params) {
22 origXhr['checkParams_'](params);
23
24 /** @const {remoting.Xhr.Params} */
25 this.params = normalizeParams(params);
26
27 /** @private {base.Deferred<!remoting.Xhr.Response>} */
28 this.deferred_ = null;
29
30 /** @type {remoting.Xhr.Response} */
31 this.response_ = null;
32
33 /** @type {boolean} */
34 this.aborted_ = false;
35 };
36
37 /**
38 * Converts constuctor parameters to a normalized form that hides
39 * details of how the constructor is called.
40 * @param {remoting.Xhr.Params} params
41 * @return {remoting.Xhr.Params} The argument with all missing fields
42 * filled in with default values.
43 */
44 var normalizeParams = function(params) {
45 return {
46 method: params.method,
47 url: params.url,
48 urlParams: typeof params.urlParams == 'object' ?
49 base.copyWithoutNullFields(params.urlParams) :
50 params.urlParams,
51 textContent: params.textContent,
52 jsonContent: params.jsonContent,
53 formContent: params.formContent === undefined ? undefined :
54 base.copyWithoutNullFields(params.formContent),
55 headers: base.copyWithoutNullFields(params.headers),
56 withCredentials: Boolean(params.withCredentials),
57 oauthToken: params.oauthToken,
58 useIdentity: Boolean(params.useIdentity),
59 acceptJson: Boolean(params.acceptJson)
60 };
61 };
62
63 /**
64 * Psuedo-override from remoting.Xhr.
65 * @return {void}
66 */
67 remoting.MockXhr.prototype.abort = function() {
68 this.aborted_ = true;
69 };
70
71 /**
72 * Psuedo-override from remoting.Xhr.
73 * @return {!Promise<!remoting.Xhr.Response>}
74 */
75 remoting.MockXhr.prototype.start = function() {
76 runMatchingHandler(this);
77 if (!this.deferred_) {
78 this.deferred_ = new base.Deferred();
79 this.maybeRespond_();
80 }
81 return this.deferred_.promise();
82 };
83
84 /**
85 * Tells this object to send an empty response to the current or next
86 * request.
87 * @param {number} status The HTTP status code to respond with.
88 */
89 remoting.MockXhr.prototype.setEmptyResponse = function(status) {
90 this.setResponse_(new remoting.Xhr.Response(
91 status,
92 'mock status text from setEmptyResponse',
93 null,
94 '',
95 false));
96 };
97
98 /**
99 * Tells this object to send a text/plain response to the current or
100 * next request.
101 * @param {number} status The HTTP status code to respond with.
102 * @param {string} body The content to respond with.
103 */
104 remoting.MockXhr.prototype.setTextResponse = function(status, body) {
105 this.setResponse_(new remoting.Xhr.Response(
106 status,
107 'mock status text from setTextResponse',
108 null,
109 body || '',
110 false));
111 };
112
113 /**
114 * Tells this object to send an application/json response to the
115 * current or next request.
116 * @param {number} status The HTTP status code to respond with.
117 * @param {*} body The content to respond with.
118 */
119 remoting.MockXhr.prototype.setJsonResponse = function(status, body) {
120 if (!this.params.acceptJson) {
121 throw new Error('client does not want JSON response');
122 }
123 this.setResponse_(new remoting.Xhr.Response(
124 status,
125 'mock status text from setJsonResponse',
126 null,
127 JSON.stringify(body),
128 true));
129 };
130
131 /**
132 * Sets the response to be used for the current or next request.
133 * @param {!remoting.Xhr.Response} response
134 * @private
135 */
136 remoting.MockXhr.prototype.setResponse_ = function(response) {
137 base.debug.assert(this.response_ == null);
138 this.response_ = response;
139 this.maybeRespond_();
140 };
141
142 /**
143 * Sends a response if one is available.
144 * @private
145 */
146 remoting.MockXhr.prototype.maybeRespond_ = function() {
147 if (this.deferred_ && this.response_ && !this.aborted_) {
148 this.deferred_.resolve(this.response_);
149 }
150 };
151
152 /**
153 * The original value of the remoting.Xhr constructor. The JSDoc type
154 * is that of the remoting.Xhr constructor function.
155 * @type {?function(this: remoting.Xhr, remoting.Xhr.Params):void}
156 */
157 var origXhr = null;
158
159 /**
160 * @type {!Array<remoting.MockXhr.UrlHandler>}
161 */
162 var handlers = [];
163
164 /**
165 * Registers a handler for a given method and URL. The |urlPattern|
166 * argument may either be a string, which must equal a URL to match
167 * it, or a RegExp.
168 *
169 * Matching handlers are run when a FakeXhr's |start| method is
170 * called. The handler should generally call one of
171 * |set{Test,Json,Empty}Response|
172 *
173 * @param {?string} method The HTTP method to respond to, or null to
174 * respond to any method.
175 * @param {?string|!RegExp} urlPattern The URL or pattern to respond
176 * to, or null to match any URL.
177 * @param {function(!remoting.MockXhr):void} callback The handler
178 * function to call when a matching XHR is started.
179 * @param {boolean=} opt_reuse If true, the response can be used for
180 * multiple requests.
181 */
182 remoting.MockXhr.setResponseFor = function(
183 method, urlPattern, callback, opt_reuse) {
184 handlers.push({
185 method: method,
186 urlPattern: urlPattern,
187 callback: callback,
188 reuse: !!opt_reuse
189 });
190 };
191
192 /**
193 * Installs a 204 (no content) response. See |setResponseFor| for
194 * more details on how the parameters work.
195 *
196 * @param {?string} method
197 * @param {?string|!RegExp} urlPattern
198 * @param {boolean=} opt_reuse
199 */
200 remoting.MockXhr.setEmptyResponseFor = function(method, urlPattern, opt_reuse) {
201 remoting.MockXhr.setResponseFor(
202 method, urlPattern, function(/** remoting.MockXhr */ xhr) {
203 xhr.setEmptyResponse(204);
204 }, opt_reuse);
205 };
206
207 /**
208 * Installs a 200 response with text content. See |setResponseFor|
209 * for more details on how the parameters work.
210 *
211 * @param {?string} method
212 * @param {?string|!RegExp} urlPattern
213 * @param {string} content
214 * @param {boolean=} opt_reuse
215 */
216 remoting.MockXhr.setTextResponseFor = function(
217 method, urlPattern, content, opt_reuse) {
218 remoting.MockXhr.setResponseFor(
219 method, urlPattern, function(/** remoting.MockXhr */ xhr) {
220 xhr.setTextResponse(200, content);
221 }, opt_reuse);
222 };
223
224 /**
225 * Installs a 200 response with JSON content. See |setResponseFor|
226 * for more details on how the parameters work.
227 *
228 * @param {?string} method
229 * @param {?string|!RegExp} urlPattern
230 * @param {*} content
231 * @param {boolean=} opt_reuse
232 */
233 remoting.MockXhr.setJsonResponseFor = function(
234 method, urlPattern, content, opt_reuse) {
235 remoting.MockXhr.setResponseFor(
236 method, urlPattern, function(/** remoting.MockXhr */ xhr) {
237 xhr.setJsonResponse(200, content);
238 }, opt_reuse);
239 };
240
241 /**
242 * Runs the most first handler for a given method and URL.
243 * @param {!remoting.MockXhr} xhr
244 */
245 var runMatchingHandler = function(xhr) {
246 for (var i = 0; i < handlers.length; i++) {
247 var handler = handlers[i];
248 if (handler.method == null || handler.method != xhr.params.method) {
249 continue;
250 }
251 if (handler.urlPattern == null) {
252 // Let the handler run.
253 } else if (typeof handler.urlPattern == 'string') {
254 if (xhr.params.url != handler.urlPattern) {
255 continue;
256 }
257 } else {
258 var regexp = /** @type {RegExp} */ (handler.urlPattern);
259 if (!regexp.test(xhr.params.url)) {
260 continue;
261 }
262 }
263 if (!handler.reuse) {
264 handlers.splice(i, 1);
265 }
266 handler.callback(xhr);
267 return;
268 };
269 throw new Error(
270 'No handler registered for ' + xhr.params.method +
271 ' to '+ xhr.params.url);
272 };
273
274 /**
275 * Activates this mock.
276 */
277 remoting.MockXhr.activate = function() {
278 base.debug.assert(
279 origXhr == null,
280 'Xhr mocking already active');
281 origXhr = remoting.Xhr;
282 remoting.MockXhr.Response = remoting.Xhr.Response;
283 remoting['Xhr'] = remoting.MockXhr;
284 };
285
286 /**
287 * Restores the original definiton of |remoting.Xhr|.
288 */
289 remoting.MockXhr.restore = function() {
290 base.debug.assert(
291 origXhr != null,
292 'Xhr mocking not active');
293 remoting['Xhr'] = origXhr;
294 origXhr = null;
295 handlers = [];
296 };
297
298 })();
299
300 // Can't put put typedefs inside a function :-(
301 /**
302 * @typedef {{
303 * method:?string,
304 * urlPattern:(?string|RegExp),
305 * callback:function(!remoting.MockXhr):void,
306 * reuse:boolean
307 * }}
308 */
309 remoting.MockXhr.UrlHandler;
OLDNEW
« no previous file with comments | « remoting/webapp/crd/js/gcd_client_with_mock_xhr_unittest.js ('k') | remoting/webapp/crd/js/mock_xhr_unittest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698