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

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: 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 === undefined ? undefined :
50 base.copyWithoutNullFields(params.formContent),
51 headers: base.copyWithoutNullFields(params.headers),
52 withCredentials: Boolean(params.withCredentials),
53 oauthToken: params.oauthToken,
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 /**
149 * The original value of the remoting.MockXhr constructor.
Jamie 2015/04/07 00:27:09 s/MockXhr/Xhr/ (I think). Also, please add someth
150 * @type {?function(this: remoting.Xhr, remoting.Xhr.Params):void}
151 */
152 var origXhr = null;
153
154 /**
155 * @type {!Array<remoting.MockXhr.UrlHandler>}
156 */
157 var handlers = [];
158
159 /**
160 * Registers a handler for a given method and URL. The |urlPattern|
161 * argument may either be a string, which must equal a URL to match
162 * it, or a RegExp.
163 *
164 * Matching handlers are run when a FakeXhr's |start| method is
165 * called. The handler should generally call one on
Jamie 2015/04/07 00:27:09 s/on/of/
166 * |set{Test,Json,Empty}Response|
167 *
168 * @param {?string} method The HTTP method to respond to, or null to
169 * respond to any method.
170 * @param {?string|!RegExp} urlPattern The URL or pattern to respond
171 * to, or null to match any URL.
172 * @param {function(!remoting.MockXhr):void} callback The handler
173 * function to call when a matching XHR is started.
174 * @param {boolean=} opt_reuse If true, the response can be used for
175 * multiple requests.
176 */
177 remoting.MockXhr.setResponseFor = function(
178 method, urlPattern, callback, opt_reuse) {
179 handlers.push({
180 method: method,
181 urlPattern: urlPattern,
182 callback: callback,
183 reuse: !!opt_reuse
184 });
185 };
186
187 /**
188 * Installs a 204 (no content) response. See |setResponseFor| for
189 * more details on how the parameters work.
190 *
191 * @param {?string} method
192 * @param {?string|!RegExp} urlPattern
193 * @param {boolean=} opt_reuse
194 */
195 remoting.MockXhr.setEmptyResponseFor = function(method, urlPattern, opt_reuse) {
196 remoting.MockXhr.setResponseFor(
197 method, urlPattern, function(/** remoting.MockXhr */ xhr) {
198 xhr.setEmptyResponse(204);
199 }, opt_reuse);
200 };
201
202 /**
203 * Installs a 200 response with text content. See |setResponseFor|
204 * for more details on how the parameters work.
205 *
206 * @param {?string} method
207 * @param {?string|!RegExp} urlPattern
208 * @param {string} content
209 * @param {boolean=} opt_reuse
210 */
211 remoting.MockXhr.setTextResponseFor = function(
212 method, urlPattern, content, opt_reuse) {
213 remoting.MockXhr.setResponseFor(
214 method, urlPattern, function(/** remoting.MockXhr */ xhr) {
215 xhr.setTextResponse(200, content);
216 }, opt_reuse);
217 };
218
219 /**
220 * Installs a 200 response with JSON content. See |setResponseFor|
221 * for more details on how the parameters work.
222 *
223 * @param {?string} method
224 * @param {?string|!RegExp} urlPattern
225 * @param {*} content
226 * @param {boolean=} opt_reuse
227 */
228 remoting.MockXhr.setJsonResponseFor = function(
229 method, urlPattern, content, opt_reuse) {
230 remoting.MockXhr.setResponseFor(
231 method, urlPattern, function(/** remoting.MockXhr */ xhr) {
232 xhr.setJsonResponse(200, content);
233 }, opt_reuse);
234 };
235
236 /**
237 * Runs the most first handler for a given method and URL.
238 * @param {!remoting.MockXhr} xhr
239 */
240 var runMatchingHandler = function(xhr) {
241 for (var i = 0; i < handlers.length; i++) {
242 var handler = handlers[i];
243 if (handler.method == null || handler.method != xhr.params.method) {
244 continue;
245 }
246 if (handler.urlPattern == null) {
247 // Let the handler run.
248 } else if (typeof handler.urlPattern == 'string') {
249 if (xhr.params.url != handler.urlPattern) {
250 continue;
251 }
252 } else {
253 var regexp = /** @type {RegExp} */ (handler.urlPattern);
254 if (!regexp.test(xhr.params.url)) {
255 continue;
256 }
257 }
258 if (!handler.reuse) {
259 handlers.splice(i, 1);
260 }
261 handler.callback(xhr);
262 return;
263 };
264 throw new Error(
265 'No handler registered for ' + xhr.params.method +
266 ' to '+ xhr.params.url);
267 };
268
269 /**
270 * Activates this mock.
271 */
272 remoting.MockXhr.activate = function() {
273 base.debug.assert(
274 origXhr == null,
275 'Xhr mocking already active');
276 origXhr = remoting.Xhr;
277 remoting.MockXhr.Response = remoting.Xhr.Response;
278 remoting['Xhr'] = remoting.MockXhr;
279 };
280
281 /**
282 * Restores the original definiton of |remoting.Xhr|.
283 */
284 remoting.MockXhr.restore = function() {
285 base.debug.assert(
286 origXhr != null,
287 'Xhr mocking not active');
288 remoting['Xhr'] = origXhr;
289 origXhr = null;
290 handlers = [];
291 };
292
293 })();
294
295 // Can't put put typedefs inside a function :-(
296 /**
297 * @typedef {{
298 * method:?string,
299 * urlPattern:(?string|RegExp),
300 * callback:function(!remoting.MockXhr):void,
301 * reuse:boolean
302 * }}
303 */
304 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