Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / webapp / base / js / xhr_unittest.js
blob1fb7856f1193b34ed5e7b5f7757ad857d75e968d
1 // Copyright 2014 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.
5 /**
6  * @fileoverview
7  */
9 (function() {
11 'use strict';
13 /** @type {sinon.FakeXhrCtrl} */
14 var fakeXhrCtrl;
16 /** @type {sinon.FakeXhr} */
17 var fakeXhr;
19 QUnit.module('xhr', {
20   beforeEach: function() {
21     fakeXhr = null;
22     fakeXhrCtrl = sinon.useFakeXMLHttpRequest();
23     fakeXhrCtrl.onCreate =
24         function(/** sinon.FakeXhr */ xhr) {
25           fakeXhr = xhr;
26         };
27     remoting.identity = new remoting.Identity();
28     chromeMocks.identity.mock$setToken('my_token');
29   },
30   afterEach: function() {
31     remoting.identity = null;
32     fakeXhrCtrl.restore();
33   }
34 });
36 QUnit.test('urlencodeParamHash', function(assert) {
37   assert.equal(
38       remoting.Xhr.urlencodeParamHash({}),
39       '');
40   assert.equal(
41       remoting.Xhr.urlencodeParamHash({'key': 'value'}),
42       'key=value');
43   assert.equal(
44       remoting.Xhr.urlencodeParamHash({'key /?=&': 'value /?=&'}),
45       'key%20%2F%3F%3D%26=value%20%2F%3F%3D%26');
46   assert.equal(
47       remoting.Xhr.urlencodeParamHash({'k1': 'v1', 'k2': 'v2'}),
48       'k1=v1&k2=v2');
49 });
52 // Test for what happens when the parameters are specified
53 // incorrectly.
56 QUnit.test('invalid *content parameters', function(assert) {
57   assert.throws(function() {
58     new remoting.Xhr({
59       method: 'POST',
60       url: 'http://foo.com',
61       jsonContent: {},
62       formContent: {}
63     });
64   });
66   assert.throws(function() {
67     new remoting.Xhr({
68       method: 'POST',
69       url: 'http://foo.com',
70       textContent: '',
71       formContent: {}
72     });
73   });
75   assert.throws(function() {
76     new remoting.Xhr({
77       method: 'POST',
78       url: 'http://foo.com',
79       textContent: '',
80       jsonContent: {}
81     });
82   });
83 });
86 QUnit.test('invalid URL parameters', function(assert) {
87   assert.throws(function() {
88     new remoting.Xhr({
89       method: 'POST',
90       url: 'http://foo.com?',
91       urlParams: {}
92     });
93   });
95   assert.throws(function() {
96     new remoting.Xhr({
97       method: 'POST',
98       url: 'http://foo.com#',
99       urlParams: {}
100     });
101   });
104 QUnit.test('invalid auth parameters', function(assert) {
105   assert.throws(function() {
106     new remoting.Xhr({
107       method: 'POST',
108       url: 'http://foo.com',
109       useIdentity: false,
110       oauthToken: '',
111       headers: {
112         'Authorization': ''
113       }
114     });
115   });
117   assert.throws(function() {
118     new remoting.Xhr({
119       method: 'POST',
120       url: 'http://foo.com',
121       useIdentity: true,
122       headers: {
123         'Authorization': ''
124       }
125     });
126   });
128   assert.throws(function() {
129     new remoting.Xhr({
130       method: 'POST',
131       url: 'http://foo.com',
132       useIdentity: true,
133       oauthToken: '',
134       headers: {}
135     });
136   });
139 QUnit.test('invalid auth parameters', function(assert) {
140   assert.throws(function() {
141     new remoting.Xhr({
142       method: 'POST',
143       url: 'http://foo.com',
144       useIdentity: false,
145       oauthToken: '',
146       headers: {
147         'Authorization': ''
148       }
149     });
150   });
152   assert.throws(function() {
153     new remoting.Xhr({
154       method: 'POST',
155       url: 'http://foo.com',
156       useIdentity: true,
157       headers: {
158         'Authorization': ''
159       }
160     });
161   });
163   assert.throws(function() {
164     new remoting.Xhr({
165       method: 'POST',
166       url: 'http://foo.com',
167       useIdentity: true,
168       oauthToken: '',
169       headers: {}
170     });
171   });
175 QUnit.test('unexpected parameters', function(assert) {
176   assert.throws(function() {
177     new remoting.Xhr({
178       method: 'POST',
179       url: 'http://foo.com',
180       xyzzy: 'not a real parameter'
181     });
182   });
186 // The typical case.
189 QUnit.test('successful GET', function(assert) {
190   var promise = new remoting.Xhr({
191     method: 'GET',
192     url: 'http://foo.com'
193   }).start().then(function(response) {
194     assert.ok(!response.isError());
195     assert.equal(response.status, 200);
196     assert.equal(response.getText(), 'body');
197   });
198   assert.equal(fakeXhr.method, 'GET');
199   assert.equal(fakeXhr.url, 'http://foo.com');
200   assert.equal(fakeXhr.withCredentials, false);
201   assert.equal(fakeXhr.requestBody, null);
202   assert.ok(!('Content-type' in fakeXhr.requestHeaders));
203   fakeXhr.respond(200, {}, 'body');
204   return promise;
208 // Tests for the effect of acceptJson.
211 QUnit.test('acceptJson required', function(assert) {
212   var promise = new remoting.Xhr({
213     method: 'GET',
214     url: 'http://foo.com'
215   }).start().then(function(response) {
216     assert.throws(response.getJson);
217   });
218   fakeXhr.respond(200, {}, '{}');
219   return promise;
222 QUnit.test('JSON response', function(assert) {
223   var responseJson = {
224       'myJsonData': [true]
225   };
226   var responseText = JSON.stringify(responseJson);
227   var promise = new remoting.Xhr({
228     method: 'GET',
229     url: 'http://foo.com',
230     acceptJson: true
231   }).start().then(function(response) {
232     // Calling getText is still OK even when a JSON response is
233     // requested.
234     assert.equal(
235         response.getText(),
236         responseText);
237     // Check that getJson works as advertised.
238     assert.deepEqual(
239         response.getJson(),
240         responseJson);
241     // Calling getJson multiple times doesn't re-parse the response.
242     assert.strictEqual(
243         response.getJson(),
244         response.getJson());
245   });
246   fakeXhr.respond(200, {}, responseText);
247   return promise;
251 // Tests for various parameters that modify the HTTP request.
254 QUnit.test('GET with param string', function(assert) {
255   new remoting.Xhr({
256     method: 'GET',
257     url: 'http://foo.com',
258     urlParams: 'the_param_string'
259   }).start();
260   assert.equal(fakeXhr.url, 'http://foo.com?the_param_string');
263 QUnit.test('GET with param object', function(assert) {
264   new remoting.Xhr({
265     method: 'GET',
266     url: 'http://foo.com',
267     urlParams: {'a': 'b', 'c': 'd'}
268   }).start();
269   assert.equal(fakeXhr.url, 'http://foo.com?a=b&c=d');
272 QUnit.test('GET with headers', function(assert) {
273   new remoting.Xhr({
274     method: 'GET',
275     url: 'http://foo.com',
276     headers: {'Header1': 'headerValue1', 'Header2': 'headerValue2'}
277   }).start();
278   assert.equal(
279       fakeXhr.requestHeaders['Header1'],
280       'headerValue1');
281   assert.equal(
282       fakeXhr.requestHeaders['Header2'],
283       'headerValue2');
287 QUnit.test('GET with credentials', function(assert) {
288   new remoting.Xhr({
289     method: 'GET',
290     url: 'http://foo.com',
291     withCredentials: true
292   }).start();
293   assert.equal(fakeXhr.withCredentials, true);
297 // Checking that typical POST requests work.
300 QUnit.test('POST with text content', function(assert) {
301   var done = assert.async();
303   var promise = new remoting.Xhr({
304     method: 'POST',
305     url: 'http://foo.com',
306     textContent: 'the_content_string'
307   }).start().then(function(response) {
308     assert.equal(response.status, 200);
309     assert.equal(response.getText(), 'body');
310     done();
311   });
312   assert.equal(fakeXhr.method, 'POST');
313   assert.equal(fakeXhr.url, 'http://foo.com');
314   assert.equal(fakeXhr.withCredentials, false);
315   assert.equal(fakeXhr.requestBody, 'the_content_string');
316   assert.equal(fakeXhr.requestHeaders['Content-type'],
317                'text/plain; charset=UTF-8');
318   fakeXhr.respond(200, {}, 'body');
319   return promise;
322 QUnit.test('POST with form content', function(assert) {
323   new remoting.Xhr({
324     method: 'POST',
325     url: 'http://foo.com',
326     formContent: {'a': 'b', 'c': 'd'}
327   }).start();
328   assert.equal(fakeXhr.requestBody, 'a=b&c=d');
329   assert.equal(
330       fakeXhr.requestHeaders['Content-type'],
331       'application/x-www-form-urlencoded; charset=UTF-8');
335 // Tests for authentication-related options.
338 QUnit.test('GET with auth token', function(assert) {
339   new remoting.Xhr({
340     method: 'GET',
341     url: 'http://foo.com',
342     oauthToken: 'my_token'
343   }).start();
344   assert.equal(fakeXhr.requestHeaders['Authorization'],
345               'Bearer my_token');
348 QUnit.test('GET with useIdentity', function(assert) {
349   var xhr = new remoting.Xhr({
350     method: 'GET',
351     url: 'http://foo.com',
352     useIdentity: true
353   });
355   xhr.start();
356   var done = assert.async();
357   fakeXhr.addEventListener('loadstart', function() {
358     assert.equal(fakeXhr.requestHeaders['Authorization'],
359                  'Bearer my_token');
360     done();
361   });
365 // Error responses.
367 QUnit.test('GET with error response', function(assert) {
368   var promise = new remoting.Xhr({
369     method: 'GET',
370     url: 'http://foo.com'
371   }).start().then(function(response) {
372     assert.ok(response.isError());
373     assert.equal(response.status, 500);
374     assert.equal(response.getText(), 'body');
375   });
376   fakeXhr.respond(500, {}, 'body');
377   return promise;
380 QUnit.test('204 is not an error', function(assert) {
381   var promise = new remoting.Xhr({
382     method: 'GET',
383     url: 'http://foo.com'
384   }).start().then(function(response) {
385     assert.ok(!response.isError());
386     assert.equal(response.status, 204);
387     assert.equal(response.getText(), '');
388   });
389   fakeXhr.respond(204, {}, null);
390   return promise;
393 QUnit.test('GET with non-HTTP response', function(assert) {
394   var promise = new remoting.Xhr({
395     method: 'GET',
396     url: 'http://foo.com'
397   }).start().then(function(response) {
398     assert.ok(response.isError());
399   });
400   fakeXhr.respond(0, {}, null);
401   return promise;
404 QUnit.module('AutoRetryXhr', {
405   beforeEach: function() {
406     fakeXhr = null;
407     fakeXhrCtrl = sinon.useFakeXMLHttpRequest();
408     fakeXhrCtrl.onCreate = function(/** sinon.FakeXhr */ xhr) {
409       fakeXhr = xhr;
410     };
411   },
412   afterEach: function() {
413     fakeXhrCtrl.restore();
414   }
418  * A class allows you to specify a sequence of canned responses, to be returned
419  * in order in response to XHRs.
421  * @param {Array<Array>} cannedResponses
422  * @constructor
423  */
424 var CannedXhrResponder = function(cannedResponses) {
425   /** @private */
426   this.mockXhr_ = sinon.useFakeXMLHttpRequest();
427   this.cannedResponses_ = cannedResponses;
428   this.mockXhr_.onCreate = this.onCreate_.bind(this);
431 /** @private */
432 CannedXhrResponder.prototype.onCreate_ = function(/** sinon.FakeXhr */ xhr) {
433   var that = this;
434   var response = that.cannedResponses_.shift();
435   Promise.resolve().then(function() {
436     xhr.respond.apply(xhr, response);
437   });
440 CannedXhrResponder.prototype.dispose = function() {
441   this.mockXhr_.restore();
444 QUnit.test('retries on status code equals 0',
445   /**
446    * 'this' is not defined for jscompile, so it can't figure out the type of
447    * this.clock.
448    * @suppress {reportUnknownTypes|checkVars|checkTypes}
449    */
450   function(assert) {
451     this.clock.restore();
452     fakeXhrCtrl.restore();
453     var responder = new CannedXhrResponder([
454       [0, {}, null],
455       [0, {}, null],
456       [200, {}, 'body']
457     ]);
458     var promise = new remoting.AutoRetryXhr({
459       method: 'GET',
460       url: 'http://foo.com'
461     }).start().then(function(response) {
462       assert.ok(!response.isError());
463     }).catch(function(){
464       throw new Error('Expect retry to succeed.');
465     });
466     return promise;
469 QUnit.test('respects opt_maxRetryAttempts',
470   /**
471    * 'this' is not defined for jscompile, so it can't figure out the type of
472    * this.clock.
473    * @suppress {reportUnknownTypes|checkVars|checkTypes}
474    */
475   function(assert) {
476     this.clock.restore();
477     fakeXhrCtrl.restore();
478     var responder = new CannedXhrResponder([
479       [0, {}, null],
480       [0, {}, null],
481       [0, {}, null],
482       [200, {}, 'body']
483     ]);
484     var promise = new remoting.AutoRetryXhr({
485       method: 'GET',
486       url: 'http://foo.com'
487     }, 2).start().then(function(response) {
488       throw new Error('Expect retry to fail.');
489     }).catch(function(){
490       assert.ok(true);
491     });
492     return promise;
495 QUnit.test('does not retry when offline', function(assert) {
496   var isOnlineStub = sinon.stub(base, 'isOnline');
497   isOnlineStub.returns(false);
499   var promise = new remoting.AutoRetryXhr({
500     method: 'GET',
501     url: 'http://foo.com'
502   }).start().then(function(response) {
503     assert.ok(false, 'Expect failure');
504   }).catch(function(/** remoting.Error */ error) {
505     assert.equal(error.getTag(), remoting.Error.Tag.NETWORK_FAILURE);
506   });
507   isOnlineStub.restore();
508   return promise;
511 QUnit.test('resolves with successful responses', function(assert) {
512   var promise = new remoting.AutoRetryXhr({
513     method: 'GET',
514     url: 'http://foo.com'
515   }).start().then(function(response) {
516     assert.ok(!response.isError());
517     assert.equal(response.getText(), 'body');
518   });
519   fakeXhr.respond(200, {}, 'body');
520   return promise;
523 QUnit.test('rejects with failed responses', function(assert) {
524   var promise = new remoting.AutoRetryXhr({
525     method: 'GET',
526     url: 'http://foo.com'
527   }).start().then(function(response) {
528     assert.ok(response.isError());
529     assert.equal(response.getText(), 'failure');
530   });
531   fakeXhr.respond(500, {}, 'failure');
532   return promise;
535 })();