OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2015 The Chromium Authors. All rights reserved. | |
John Williams
2015/04/02 23:57:06
The main file for this CL. Needs more docs, I kno
Jamie
2015/04/03 01:45:57
No (c) needed
Jamie
2015/04/03 01:45:57
Doesn't sinon already support mocking out XMLHttpR
John Williams
2015/04/04 01:40:26
Because it's a test file, or because we're phasing
John Williams
2015/04/04 01:40:26
Done.
Jamie
2015/04/06 19:24:03
It's not needed for any new files (for the past ye
| |
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 | |
7 * A mock version of remoting.Xhr. | |
8 */ | |
9 | |
10 (function() { | |
11 'use strict'; | |
12 | |
13 /** | |
14 * @constructor | |
15 * @param {remoting.Xhr.Params} params | |
Jamie
2015/04/03 01:45:57
Would it make sense to declare this as @extends {X
John Williams
2015/04/04 01:40:26
Doing that causes the compiler to check for privat
Jamie
2015/04/06 19:24:03
Acknowledged.
| |
16 */ | |
17 remoting.MockXhr = function(params) { | |
18 origXhr['checkParams_'](params); | |
19 | |
20 /** @const {remoting.Xhr.Params} */ | |
21 this.params = normalizeParams(params); | |
22 | |
23 | |
Jamie
2015/04/03 01:45:57
Duplicate blank line.
John Williams
2015/04/04 01:40:26
Done.
| |
24 /** @private {base.Deferred<!remoting.Xhr.Response>} */ | |
25 this.deferred_ = null; | |
26 | |
27 /** @type {remoting.Xhr.Response} */ | |
28 this.response_ = null; | |
29 | |
30 /** @type {boolean} */ | |
31 this.aborted_ = false; | |
32 }; | |
33 | |
34 /** | |
35 * @param {remoting.Xhr.Params} params | |
36 * @return {remoting.Xhr.Params} The argument with all missing fields | |
37 * filled in with default values. | |
38 */ | |
39 var normalizeParams = function(params) { | |
40 return { | |
41 method: params.method, | |
42 url: params.url, | |
43 urlParams: typeof params.urlParams == 'object' ? | |
44 base.copyWithoutNullFields(params.urlParams) : | |
45 params.urlParams, | |
46 textContent: params.textContent, | |
47 jsonContent: params.jsonContent, | |
48 formContent: params.formContent || | |
49 base.copyWithoutNullFields(params.formContent), | |
Jamie
2015/04/03 01:45:57
You're only calling copyWithoutNullFields if its p
John Williams
2015/04/04 01:40:26
Having formContent === undefined is a special case
Jamie
2015/04/06 19:24:03
That's not what this does, I don't think. If param
John Williams
2015/04/06 23:46:13
I changed it to be more explicit about undefined.
| |
50 headers: base.copyWithoutNullFields(params.headers), | |
51 withCredentials: !!params.withCredentials, | |
Jamie
2015/04/03 01:45:57
Prefer Boolean(x) to !!x for clarity.
John Williams
2015/04/04 01:40:26
Done.
| |
52 oauthToken: params.oauthToken == null : null params.oauthToken, | |
Jamie
2015/04/03 01:45:57
This doesn't look right.
John Williams
2015/04/04 01:40:26
Yeah, I was in a hurry to get this CL out before I
| |
53 useIdentity: !!params.useIdentity, | |
54 acceptJson: !!params.acceptJson | |
55 }; | |
56 }; | |
57 | |
58 ////////////////////////////////////////////////////////////////////////////// | |
59 // | |
60 // Mock implementation. | |
61 // | |
Jamie
2015/04/03 01:45:57
I don't think we have a big block comment like thi
| |
62 | |
63 /** | |
64 * @return {void} | |
65 */ | |
66 remoting.MockXhr.prototype.abort = function() { | |
67 this.aborted_ = true; | |
68 }; | |
69 | |
70 /** | |
71 * @return {!Promise<!remoting.Xhr.Response>} | |
72 */ | |
73 remoting.MockXhr.prototype.start = function() { | |
74 runMatchingHandler(this); | |
75 if (!this.deferred_) { | |
76 this.deferred_ = new base.Deferred(); | |
77 this.maybeRespond_(); | |
78 } | |
79 return this.deferred_.promise(); | |
80 }; | |
81 | |
82 ////////////////////////////////////////////////////////////////////////////// | |
83 // | |
84 // Mock-specific methods. | |
85 // | |
86 | |
87 /** | |
88 * @param {number} status | |
Jamie
2015/04/03 01:45:57
I think this (and subsequent) methods should be @p
John Williams
2015/04/04 01:40:26
They need to be called by user code (see the unit
| |
89 */ | |
90 remoting.MockXhr.prototype.setEmptyResponse = function(status) { | |
91 this.setResponse_(new remoting.Xhr.Response( | |
92 status, | |
93 'mock status text from setEmptyResponse', | |
94 null, | |
95 '', | |
96 false)); | |
97 }; | |
98 | |
99 /** | |
100 * @param {number} status | |
101 * @param {string} body | |
102 */ | |
103 remoting.MockXhr.prototype.setTextResponse = function(status, body) { | |
104 this.setResponse_(new remoting.Xhr.Response( | |
105 status, | |
106 'mock status text from setTextResponse', | |
107 null, | |
108 body || '', | |
109 false)); | |
110 }; | |
111 | |
112 /** | |
113 * @param {number} status | |
114 * @param {*} body | |
115 */ | |
116 remoting.MockXhr.prototype.setJsonResponse = function(status, body) { | |
117 if (!this.params.acceptJson) { | |
118 throw new Error('client does not want JSON response'); | |
119 } | |
120 this.setResponse_(new remoting.Xhr.Response( | |
121 status, | |
122 'mock status text from setJsonResponse', | |
123 null, | |
124 JSON.stringify(body), | |
125 true)); | |
126 }; | |
127 | |
128 /** | |
129 * @param {!remoting.Xhr.Response} response | |
130 */ | |
131 remoting.MockXhr.prototype.setResponse_ = function(response) { | |
Jamie
2015/04/03 01:45:57
I think this (and some other) methods should be @p
John Williams
2015/04/04 01:40:26
Done.
| |
132 base.debug.assert(this.response_ == null); | |
133 this.response_ = response; | |
134 this.maybeRespond_(); | |
135 }; | |
136 | |
137 remoting.MockXhr.prototype.maybeRespond_ = function() { | |
138 if (this.deferred_ && this.response_ && !this.aborted_) { | |
139 this.deferred_.resolve(this.response_); | |
140 } | |
141 }; | |
142 | |
143 /** @type {*} */ | |
Jamie
2015/04/03 01:45:57
Can you be more specific about the type of this?
John Williams
2015/04/04 01:40:26
It's a pretty ugly type, but OK.
Jamie
2015/04/06 19:24:03
Yuck; that is horrible. I was thinking it would be
John Williams
2015/04/06 23:46:13
Done.
| |
144 var origXhr = null; | |
145 | |
146 /** | |
147 * @type {!Array<remoting.MockXhr.UrlHandler>} | |
148 */ | |
149 var handlers = []; | |
150 | |
151 /** | |
152 * Registers a handler for a given method and URL. The |urlPattern| | |
153 * argument may either be a string, which must equal a URL to match | |
154 * it, or a RegExp, which is matched against URLs with the |test| | |
Jamie
2015/04/03 01:45:57
I don't think "with the test method" adds any valu
John Williams
2015/04/04 01:40:26
Hmm. You're right. I'm used to there being two met
| |
155 * method. | |
156 * | |
157 * Matching handlers are run when a FakeXhr's |start| method is called. | |
158 * | |
159 * @param {?string} method The HTTP method to respond to, or null to | |
160 * response to any method. | |
Jamie
2015/04/03 01:45:57
s/response/respond/
John Williams
2015/04/04 01:40:26
Done.
| |
161 * @param {?string|!RegExp} urlPattern The URL or pattern to respond | |
162 * to, or null to match any URL. | |
163 * @param {function(!remoting.MockXhr):void} proc The function to call | |
Jamie
2015/04/03 01:45:57
|callback| would be a more conventional name for t
John Williams
2015/04/04 01:40:26
Done.
| |
164 * when a matching XHR is started. | |
165 * @param {boolean=} opt_reuse If true, the response can be used for | |
166 * multiple requests. | |
167 */ | |
168 remoting.MockXhr.setResponseFor = function( | |
169 method, urlPattern, proc, opt_reuse) { | |
170 handlers.push({ | |
171 method: method, | |
172 urlPattern: urlPattern, | |
173 proc: proc, | |
174 reuse: !!opt_reuse | |
175 }); | |
176 }; | |
177 | |
178 /** | |
179 * Calls |setResponseFor| to add a 200 response with text content. | |
Jamie
2015/04/03 01:45:57
s/200/204/
s/with text content/(no content)/
I wo
John Williams
2015/04/04 01:40:26
Done.
I said "calls |setResponseFor|" because |se
Jamie
2015/04/06 19:24:03
Either copy them, or add a "see setResponseFor" co
John Williams
2015/04/06 23:46:13
Done.
| |
180 * | |
181 * @param {?string} method | |
182 * @param {?string|!RegExp} urlPattern | |
183 * @param {boolean=} opt_reuse | |
184 */ | |
185 remoting.MockXhr.setEmptyResponseFor = function(method, urlPattern, opt_reuse) { | |
186 remoting.MockXhr.setResponseFor( | |
187 method, urlPattern, function(/** remoting.MockXhr */ xhr) { | |
188 xhr.setEmptyResponse(204); | |
189 }, opt_reuse); | |
190 }; | |
191 | |
192 /** | |
193 * Calls |setResponseFor| to add a 200 response with text content. | |
194 * | |
195 * @param {?string} method | |
196 * @param {?string|!RegExp} urlPattern | |
197 * @param {string} content | |
198 * @param {boolean=} opt_reuse | |
199 */ | |
200 remoting.MockXhr.setTextResponseFor = function( | |
201 method, urlPattern, content, opt_reuse) { | |
202 remoting.MockXhr.setResponseFor( | |
203 method, urlPattern, function(/** remoting.MockXhr */ xhr) { | |
204 xhr.setTextResponse(200, content); | |
205 }, opt_reuse); | |
206 }; | |
207 | |
208 /** | |
209 * Calls |setResponseFor| to add a 200 response with JSON content. | |
210 * | |
211 * @param {?string} method | |
212 * @param {?string|!RegExp} urlPattern | |
213 * @param {*} content | |
214 * @param {boolean=} opt_reuse | |
215 */ | |
216 remoting.MockXhr.setJsonResponseFor = function( | |
217 method, urlPattern, content, opt_reuse) { | |
218 remoting.MockXhr.setResponseFor( | |
219 method, urlPattern, function(/** remoting.MockXhr */ xhr) { | |
220 xhr.setJsonResponse(200, content); | |
221 }, opt_reuse); | |
222 }; | |
223 | |
224 /** | |
225 * Runs the most first handler for a given method and URL. | |
226 * @param {!remoting.MockXhr} xhr | |
227 */ | |
228 var runMatchingHandler = function(xhr) { | |
229 for (var i = 0; i < handlers.length; i++) { | |
230 var handler = handlers[i]; | |
231 if (handler.method == null || handler.method != xhr.params.method) { | |
232 continue; | |
233 } | |
234 if (handler.urlPattern == null) { | |
235 // Let the handler run. | |
236 } else if (typeof handler.urlPattern == 'string') { | |
237 if (xhr.params.url != handler.urlPattern) { | |
238 continue; | |
239 } | |
240 } else { | |
241 var regexp = /** @type {RegExp} */ (handler.urlPattern); | |
242 if (!regexp.test(xhr.params.url)) { | |
243 continue; | |
244 } | |
245 } | |
246 if (!handler.reuse) { | |
247 handlers.splice(i, 1); | |
248 } | |
249 handler.proc(xhr); | |
250 return; | |
251 }; | |
252 throw new Error( | |
253 'No handler registered for ' + xhr.params.method + | |
254 ' to '+ xhr.params.url); | |
255 }; | |
256 | |
257 remoting.MockXhr.activate = function() { | |
258 base.debug.assert( | |
259 origXhr == null, | |
260 'Xhr mocking already active'); | |
261 origXhr = remoting.Xhr; | |
262 remoting.MockXhr.Response = remoting.Xhr.Response; | |
263 remoting['Xhr'] = remoting.MockXhr; | |
264 }; | |
265 | |
266 remoting.MockXhr.restore = function() { | |
267 base.debug.assert( | |
268 origXhr != null, | |
269 'Xhr mocking not active'); | |
270 remoting['Xhr'] = origXhr; | |
271 origXhr = null; | |
272 handlers = []; | |
273 }; | |
274 | |
275 })(); | |
276 | |
277 // Can't put put typedefs inside a function :-( | |
278 /** | |
279 * @typedef {{ | |
280 * method:?string, | |
281 * urlPattern:(?string|RegExp), | |
282 * proc:function(!remoting.MockXhr):void, | |
283 * reuse:boolean | |
284 * }} | |
285 */ | |
286 remoting.MockXhr.UrlHandler; | |
OLD | NEW |