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

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