• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!doctype html>
2<!--
3@license
4Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
5This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
6The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
7The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
8Code distributed by Google as part of the polymer project is also
9subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
10-->
11<html>
12<head>
13  <title>iron-ajax</title>
14
15  <script src="../../webcomponentsjs/webcomponents-lite.js"></script>
16  <script src="../../web-component-tester/browser.js"></script>
17
18  <link rel="import" href="../../polymer/polymer.html">
19  <link rel="import" href="../../promise-polyfill/promise-polyfill.html">
20  <link rel="import" href="../iron-ajax.html">
21</head>
22<body>
23  <test-fixture id="TrivialGet">
24    <template>
25      <iron-ajax url="/responds_to_get_with_json"></iron-ajax>
26    </template>
27  </test-fixture>
28  <test-fixture id="ParamsGet">
29    <template>
30      <iron-ajax url="/responds_to_get_with_json"
31                 params='{"a": "a"}'></iron-ajax>
32    </template>
33  </test-fixture>
34  <test-fixture id="AutoGet">
35    <template>
36      <iron-ajax auto url="/responds_to_get_with_json"></iron-ajax>
37    </template>
38  </test-fixture>
39  <test-fixture id="GetEcho">
40    <template>
41      <iron-ajax handle-as="json" url="/echoes_request_url"></iron-ajax>
42    </template>
43  </test-fixture>
44  <test-fixture id="TrivialPost">
45    <template>
46      <iron-ajax method="POST"
47                 url="/responds_to_post_with_json"></iron-ajax>
48    </template>
49  </test-fixture>
50  <test-fixture id="DebouncedGet">
51    <template>
52      <iron-ajax auto
53                 url="/responds_to_debounced_get_with_json"
54                 debounce-duration="150"></iron-ajax>
55    </template>
56  </test-fixture>
57  <test-fixture id='BlankUrl'>
58    <template>
59      <iron-ajax auto handle-as='text'></iron-ajax>
60    </template>
61  </test-fixture>
62  <test-fixture id="RealPost">
63    <template>
64      <iron-ajax method="POST" url="http://httpbin.org/post"></iron-ajax>
65    </template>
66  </test-fixture>
67  <test-fixture id="Delay">
68    <template>
69      <iron-ajax url="http://httpbin.org/delay/1"></iron-ajax>
70    </template>
71  </test-fixture>
72  <test-fixture id="Bubbles">
73    <template>
74      <iron-ajax url="http://httpbin.org/post" method="POST" bubbles></iron-ajax>
75      <iron-ajax url="/responds_to_get_with_502_error_json" bubbles></iron-ajax>
76    </template>
77  </test-fixture>
78  <script>
79    'use strict';
80    suite('<iron-ajax>', function() {
81      var responseHeaders = {
82        json: { 'Content-Type': 'application/json' },
83        plain: { 'Content-Type': 'text/plain' }
84      };
85      var ajax;
86      var request;
87      var server;
88
89      function timePasses(ms) {
90        return new Promise(function(resolve) {
91          window.setTimeout(function() {
92            resolve();
93          }, ms);
94        });
95      }
96
97      setup(function() {
98        server = sinon.fakeServer.create();
99        server.respondWith(
100          'GET',
101          /\/responds_to_get_with_json.*/,
102          [
103            200,
104            responseHeaders.json,
105            '{"success":true}'
106          ]
107        );
108
109        server.respondWith(
110          'POST',
111          '/responds_to_post_with_json',
112          [
113            200,
114            responseHeaders.json,
115            '{"post_success":true}'
116          ]
117        );
118
119        server.respondWith(
120          'GET',
121          '/responds_to_get_with_text',
122          [
123            200,
124            responseHeaders.plain,
125            'Hello World'
126          ]
127        );
128
129        server.respondWith(
130          'GET',
131          '/responds_to_debounced_get_with_json',
132          [
133            200,
134            responseHeaders.json,
135            '{"success": "true"}'
136          ]
137        );
138
139        server.respondWith(
140          'GET',
141          '/responds_to_get_with_502_error_json',
142          [
143            502,
144            responseHeaders.json,
145            '{"message": "an error has occurred"}'
146          ]
147        );
148
149        ajax = fixture('TrivialGet');
150      });
151
152      teardown(function() {
153        server.restore();
154      });
155
156      // Echo requests are responded to individually and on demand, unlike the
157      // others in this file which are responded to with server.respond(),
158      // which responds to all open requests.
159      // We don't use server.respondWith here because there's no way to use it
160      // and only respond to a subset of requests.
161      // This way we can test for delayed and out of order responses and
162      // distinquish them by their responses.
163      function respondToEchoRequest(request) {
164        request.respond(200, responseHeaders.json, JSON.stringify({
165          url: request.url
166        }));
167      }
168
169      suite('when making simple GET requests for JSON', function() {
170        test('has sane defaults that love you', function() {
171          request = ajax.generateRequest();
172
173          server.respond();
174
175          expect(request.response).to.be.ok;
176          expect(request.response).to.be.an('object');
177          expect(request.response.success).to.be.equal(true);
178        });
179
180        test('will be asynchronous by default', function() {
181          expect(ajax.toRequestOptions().async).to.be.eql(true);
182        });
183      });
184
185      suite('when setting custom headers', function() {
186        test('are present in the request headers', function() {
187          ajax.headers['custom-header'] = 'valid';
188          var options = ajax.toRequestOptions();
189
190          expect(options.headers).to.be.ok;
191          expect(options.headers['custom-header']).to.be.an('string');
192          expect(options.headers.hasOwnProperty('custom-header')).to.be.equal(
193              true);
194        });
195
196        test('non-objects in headers are not applied', function() {
197          ajax.headers = 'invalid';
198          var options = ajax.toRequestOptions();
199
200          expect(Object.keys(options.headers).length).to.be.equal(0);
201        });
202      });
203
204      suite('when url isn\'t set yet', function() {
205        test('we don\'t fire any automatic requests', function() {
206          expect(server.requests.length).to.be.equal(0);
207          ajax = fixture('BlankUrl');
208
209          return timePasses(1).then(function() {
210            // We don't make any requests.
211            expect(server.requests.length).to.be.equal(0);
212
213            // Explicitly asking for the request to fire works.
214            ajax.generateRequest();
215            expect(server.requests.length).to.be.equal(1);
216            server.requests = [];
217
218            // Explicitly setting url to '' works too.
219            ajax = fixture('BlankUrl');
220            ajax.url = '';
221            return timePasses(1);
222          }).then(function() {
223            expect(server.requests.length).to.be.equal(1);
224          });
225        });
226
227        test('requestUrl remains empty despite valid queryString', function() {
228          ajax = fixture('BlankUrl');
229          expect(ajax.url).to.be.equal(undefined);
230          expect(ajax.queryString).to.be.equal('');
231          expect(ajax.requestUrl).to.be.equal('');
232
233          ajax.params = {'a':'b', 'c':'d'};
234
235          expect(ajax.queryString).to.be.equal('a=b&c=d');
236          expect(ajax.requestUrl).to.be.equal('?a=b&c=d');
237        });
238
239        test('generateRequest works with empty URL and valid queryString', function() {
240          ajax = fixture('BlankUrl');
241          expect(ajax.url).to.be.equal(undefined);
242
243          ajax.generateRequest();
244          expect(server.requests[0].url).to.be.eql('');
245
246          ajax.params = {'a':'b', 'c':'d'};
247
248          ajax.generateRequest();
249          expect(server.requests[1].url).to.be.eql('?a=b&c=d');
250        });
251      });
252
253      suite('when properties are changed', function() {
254        test('generates simple-request elements that reflect the change', function() {
255          request = ajax.generateRequest();
256
257          expect(request.xhr.method).to.be.equal('GET');
258
259          ajax.method = 'POST';
260          ajax.url = '/responds_to_post_with_json';
261
262          request = ajax.generateRequest();
263
264          expect(request.xhr.method).to.be.equal('POST');
265        });
266      });
267
268      suite('when generating a request', function() {
269        test('yields an iron-request instance', function() {
270          var IronRequest = document.createElement('iron-request').constructor;
271
272          expect(ajax.generateRequest()).to.be.instanceOf(IronRequest);
273        });
274
275        test('correctly adds params to a URL that already has some', function() {
276          ajax.url += '?a=b';
277          ajax.params = {'c': 'd'};
278
279          expect(ajax.requestUrl).to.be.equal('/responds_to_get_with_json?a=b&c=d')
280        })
281
282        test('encodes params properly', function() {
283          ajax.params = {'a b,c': 'd e f'};
284
285          expect(ajax.queryString).to.be.equal('a%20b%2Cc=d%20e%20f');
286        });
287
288        test('encodes array params properly', function() {
289          ajax.params = {'a b': ['c','d e', 'f']};
290
291          expect(ajax.queryString).to.be.equal('a%20b=c&a%20b=d%20e&a%20b=f');
292        });
293
294        test('reflects the loading state in the `loading` property', function() {
295          var request = ajax.generateRequest();
296
297          expect(ajax.loading).to.be.equal(true);
298
299          server.respond();
300
301          return request.completes.then(function() {
302            return timePasses(1);
303          }).then(function() {
304            expect(ajax.loading).to.be.equal(false);
305          });
306        });
307
308        test('the `iron-ajax-presend` event gets fired', function() {
309          var spy = sinon.spy();
310          var windowSpy = sinon.spy();
311          ajax.addEventListener('iron-ajax-presend', spy);
312          window.addEventListener('iron-ajax-presend', windowSpy);
313
314          var request = ajax.generateRequest();
315
316          server.respond()
317
318          return request.completes.then(function() {
319            expect(spy).to.be.calledOnce;
320            expect(windowSpy).not.to.be.called;
321          });
322        });
323
324        test('the loading-changed event gets fired twice', function() {
325          var count = 0;
326          ajax.addEventListener('loading-changed', function() {
327            count++;
328          });
329
330          var request = ajax.generateRequest();
331
332          server.respond();
333
334          return request.completes.then(function() {
335            return timePasses(1);
336          }).then(function() {
337            expect(count).to.be.equal(2);
338          });
339        });
340      });
341
342      suite('when there are multiple requests', function() {
343        var requests;
344        var echoAjax;
345        var promiseAllComplete;
346
347        setup(function() {
348          echoAjax = fixture('GetEcho');
349          requests = [];
350
351          for (var i = 0; i < 3; ++i) {
352            echoAjax.params = {'order': i + 1};
353            requests.push(echoAjax.generateRequest());
354          }
355          var allPromises = requests.map(function(r){return r.completes});
356          promiseAllComplete = Promise.all(allPromises);
357        });
358
359        test('holds all requests in the `activeRequests` Array', function() {
360          expect(requests).to.deep.eql(echoAjax.activeRequests);
361        });
362
363        test('empties `activeRequests` when requests are completed', function() {
364          expect(echoAjax.activeRequests.length).to.be.equal(3);
365          for (var i = 0; i < 3; i++) {
366            respondToEchoRequest(server.requests[i]);
367          }
368          return promiseAllComplete.then(function() {
369            return timePasses(1);
370          }).then(function() {
371            expect(echoAjax.activeRequests.length).to.be.equal(0);
372          });
373        });
374
375        test('avoids race conditions with last response', function() {
376          expect(echoAjax.lastResponse).to.be.equal(undefined);
377
378          // Resolving the oldest request doesn't update lastResponse.
379          respondToEchoRequest(server.requests[0]);
380          return requests[0].completes.then(function() {
381            expect(echoAjax.lastResponse).to.be.equal(undefined);
382
383            // Resolving the most recent request does!
384            respondToEchoRequest(server.requests[2]);
385            return requests[2].completes;
386          }).then(function() {
387            expect(echoAjax.lastResponse).to.be.deep.eql(
388                {url: '/echoes_request_url?order=3'});
389
390
391            // Resolving an out of order stale request after does nothing!
392            respondToEchoRequest(server.requests[1]);
393            return requests[1].completes;
394          }).then(function() {
395            expect(echoAjax.lastResponse).to.be.deep.eql(
396                {url: '/echoes_request_url?order=3'});
397          });
398        });
399
400        test('`loading` is true while the last one is loading', function() {
401          expect(echoAjax.loading).to.be.equal(true);
402
403          respondToEchoRequest(server.requests[0]);
404          return requests[0].completes.then(function() {
405            // We're still loading because requests[2] is the most recently
406            // made request.
407            expect(echoAjax.loading).to.be.equal(true);
408
409            respondToEchoRequest(server.requests[2]);
410            return requests[2].completes;
411          }).then(function() {
412            // Now we're done loading.
413            expect(echoAjax.loading).to.be.eql(false);
414
415            // Resolving an out of order stale request after should have
416            // no effect.
417            respondToEchoRequest(server.requests[1]);
418            return requests[1].completes;
419          }).then(function() {
420            expect(echoAjax.loading).to.be.eql(false);
421          });
422        });
423      });
424
425      suite('when params are changed', function() {
426        test('generates a request that reflects the change', function() {
427          ajax = fixture('ParamsGet');
428          request = ajax.generateRequest();
429
430          expect(request.xhr.url).to.be.equal('/responds_to_get_with_json?a=a');
431
432          ajax.params = {b: 'b'};
433          request = ajax.generateRequest();
434
435          expect(request.xhr.url).to.be.equal('/responds_to_get_with_json?b=b');
436        });
437      });
438
439      suite('when `auto` is enabled', function() {
440        setup(function() {
441          ajax = fixture('AutoGet');
442        });
443
444        test('automatically generates new requests', function() {
445          return new Promise(function(resolve) {
446            ajax.addEventListener('request', function() {
447              resolve();
448            });
449          });
450        });
451
452        test('does not send requests if url is not a string', function() {
453          return new Promise(function(resolve, reject) {
454            ajax.addEventListener('request', function() {
455              reject('A request was generated but url is null!');
456            });
457
458            ajax.url = null;
459            ajax.handleAs = 'text';
460
461            Polymer.Base.async(function() {
462              resolve();
463            }, 1);
464          });
465        });
466
467        test('deduplicates multiple changes to a single request', function() {
468          return new Promise(function(resolve, reject) {
469            ajax.addEventListener('request', function() {
470              server.respond();
471            });
472
473            ajax.addEventListener('response', function() {
474              try {
475                expect(ajax.activeRequests.length).to.be.eql(1);
476                resolve()
477              } catch (e) {
478                reject(e);
479              }
480            });
481
482            ajax.handleas = 'text';
483            ajax.params = { foo: 'bar' };
484            ajax.headers = { 'X-Foo': 'Bar' };
485          });
486        });
487
488        test('automatically generates new request when a sub-property of params is changed', function(done) {
489          ajax.addEventListener('request', function() {
490            server.respond();
491          });
492
493          ajax.params = { foo: 'bar' };
494          ajax.addEventListener('response', function() {
495            ajax.addEventListener('request', function() {
496              done();
497            });
498
499            ajax.set('params.foo', 'xyz');
500          });
501        });
502      });
503
504      suite('the last response', function() {
505        setup(function() {
506          request = ajax.generateRequest();
507          server.respond();
508        });
509
510        test('is accessible as a readonly property', function() {
511          return request.completes.then(function(request) {
512            expect(ajax.lastResponse).to.be.equal(request.response);
513          });
514        });
515
516
517        test('updates with each new response', function() {
518          return request.completes.then(function(request) {
519
520            expect(request.response).to.be.an('object');
521            expect(ajax.lastResponse).to.be.equal(request.response);
522
523            ajax.handleAs = 'text';
524            request = ajax.generateRequest();
525            server.respond();
526
527            return request.completes;
528          }).then(function(request) {
529            expect(request.response).to.be.a('string');
530            expect(ajax.lastResponse).to.be.equal(request.response);
531          });
532        });
533      });
534
535      suite('when making POST requests', function() {
536        setup(function() {
537          ajax = fixture('TrivialPost');
538        });
539
540        test('POSTs the value of the `body` attribute', function() {
541          var requestBody = JSON.stringify({foo: 'bar'});
542
543          ajax.body = requestBody;
544          ajax.generateRequest();
545
546          expect(server.requests[0]).to.be.ok;
547          expect(server.requests[0].requestBody).to.be.equal(requestBody);
548        });
549
550        test('if `contentType` is set to form encode, the body is encoded',function() {
551          ajax.body = {foo: 'bar\nbip', 'biz bo': 'baz blar'};
552          ajax.contentType = 'application/x-www-form-urlencoded';
553          ajax.generateRequest();
554
555          expect(server.requests[0]).to.be.ok;
556          expect(server.requests[0].requestBody).to.be.equal(
557              'foo=bar%0D%0Abip&biz+bo=baz+blar');
558        });
559
560        test('if `contentType` is json, the body is json encoded', function() {
561          var requestObj = {foo: 'bar', baz: [1,2,3]}
562          ajax.body = requestObj;
563          ajax.contentType = 'application/json';
564          ajax.generateRequest();
565
566          expect(server.requests[0]).to.be.ok;
567          expect(server.requests[0].requestBody).to.be.equal(
568              JSON.stringify(requestObj));
569        });
570
571        suite('the examples in the documentation work', function() {
572          test('json content, body attribute is an object', function() {
573            ajax.setAttribute('body', '{"foo": "bar baz", "x": 1}');
574            ajax.contentType = 'application/json';
575            ajax.generateRequest();
576
577            expect(server.requests[0]).to.be.ok;
578            expect(server.requests[0].requestBody).to.be.equal(
579                '{"foo":"bar baz","x":1}');
580          });
581
582          test('form content, body attribute is an object', function() {
583            ajax.setAttribute('body', '{"foo": "bar baz", "x": 1}');
584            ajax.contentType = 'application/x-www-form-urlencoded';
585            ajax.generateRequest();
586
587            expect(server.requests[0]).to.be.ok;
588            expect(server.requests[0].requestBody).to.be.equal(
589                'foo=bar+baz&x=1');
590          });
591        });
592
593        suite('and `contentType` is explicitly set to form encode', function() {
594          test('we encode a custom object', function() {
595            function Foo(bar) { this.bar = bar };
596            var requestObj = new Foo('baz');
597            ajax.body = requestObj;
598            ajax.contentType = 'application/x-www-form-urlencoded';
599            ajax.generateRequest();
600
601            expect(server.requests[0]).to.be.ok;
602            expect(server.requests[0].requestBody).to.be.equal('bar=baz');
603          });
604        })
605
606        suite('and `contentType` isn\'t set', function() {
607          test('we don\'t try to encode an ArrayBuffer', function() {
608            var requestObj = new ArrayBuffer()
609            ajax.body = requestObj;
610            ajax.generateRequest();
611
612            expect(server.requests[0]).to.be.ok;
613            // We give the browser the ArrayBuffer directly, without trying
614            // to encode it.
615            expect(server.requests[0].requestBody).to.be.equal(requestObj);
616          });
617        })
618      });
619
620      suite('when debouncing requests', function() {
621        setup(function() {
622          ajax = fixture('DebouncedGet');
623        });
624
625        test('only requests a single resource', function() {
626          ajax._requestOptionsChanged();
627          expect(server.requests[0]).to.be.equal(undefined);
628          ajax._requestOptionsChanged();
629          return timePasses(200).then(function() {
630            expect(server.requests[0]).to.be.ok;
631          });
632        });
633      });
634
635      suite('when a response handler is bound', function() {
636        var responseHandler;
637
638        setup(function() {
639          responseHandler = sinon.spy();
640          ajax.addEventListener('response', responseHandler);
641        });
642
643        test('calls the handler after every response', function() {
644          ajax.generateRequest();
645          ajax.generateRequest();
646
647          server.respond();
648
649          return ajax.lastRequest.completes.then(function() {
650            expect(responseHandler.callCount).to.be.equal(2);
651          });
652        });
653      });
654
655      suite('when the response type is `json`', function() {
656        setup(function() {
657          server.restore();
658        });
659
660        test('finds the JSON on any platform', function() {
661          ajax.url = '../bower.json';
662          request = ajax.generateRequest();
663          return request.completes.then(function() {
664            expect(ajax.lastResponse).to.be.instanceOf(Object);
665          });
666        });
667      });
668
669      suite('when handleAs parameter is `text`', function() {
670
671        test('response type is string', function() {
672          ajax.url = '/responds_to_get_with_json';
673          ajax.handleAs = 'text';
674
675          request = ajax.generateRequest();
676          var promise = request.completes.then(function() {
677            expect(typeof(ajax.lastResponse)).to.be.equal('string');
678          });
679
680          expect(server.requests.length).to.be.equal(1);
681          expect(server.requests[0].requestHeaders['accept']).to.be.equal(
682            'text/plain');
683          server.respond();
684
685          return promise;
686        });
687
688      });
689
690      suite('when a request fails', function() {
691        test('we give an error with useful details', function() {
692          ajax.url = '/responds_to_get_with_502_error_json';
693          ajax.handleAs = 'json';
694          var eventFired = false;
695          ajax.addEventListener('error', function(event) {
696            expect(event.detail.request).to.be.ok;
697            expect(event.detail.error).to.be.ok;
698            eventFired = true;
699          });
700          var request = ajax.generateRequest();
701          var promise = request.completes.then(function() {
702            throw new Error('Expected the request to fail!');
703          }, function(error) {
704            expect(error).to.be.instanceof(Error);
705            expect(request.succeeded).to.be.eq(false);
706            return timePasses(100);
707          }).then(function() {
708            expect(eventFired).to.be.eq(true);
709            expect(ajax.lastError).to.not.be.eq(null);
710            expect(ajax.lastError.status).to.be.eq(502);
711            expect(ajax.lastError.statusText).to.be.eq("Bad Gateway");
712            expect(ajax.lastError.response).to.be.ok;
713          });
714
715          server.respond();
716
717          return promise;
718        });
719
720        test('with rejectWithRequest the promise chain contains the request and error', function() {
721          ajax.url = '/responds_to_get_with_502_error_json';
722          ajax.handleAs = 'json';
723          ajax.rejectWithRequest = true;
724
725          var request = ajax.generateRequest();
726          var promise = request.completes.then(function() {
727            throw new Error('Expected the request to fail!');
728          }, function(resp) {
729            expect(resp.error).to.be.instanceof(Error);
730            expect(resp.request).to.deep.equal(request);
731          });
732
733          server.respond();
734
735          return promise;
736        });
737
738        test('we give a useful error even when the domain doesn\'t resolve', function() {
739          ajax.url = 'http://nonexistant.example.com/';
740          server.restore();
741          var eventFired = false;
742          ajax.addEventListener('error', function(event) {
743            expect(event.detail.request).to.be.ok;
744            expect(event.detail.error).to.be.ok;
745            eventFired = true;
746          });
747          var request = ajax.generateRequest();
748          var promise = request.completes.then(function() {
749            throw new Error('Expected the request to fail!');
750          }, function(error) {
751            expect(request.succeeded).to.be.eq(false);
752            expect(error).to.not.be.eq(null);
753            return timePasses(100);
754          }).then(function() {
755            expect(eventFired).to.be.eq(true);
756            expect(ajax.lastError).to.not.be.eq(null);
757          });
758
759          server.respond();
760
761          return promise;
762        });
763      });
764
765      suite('when handleAs parameter is `json`', function() {
766
767        test('response type is string', function() {
768          ajax.url = '/responds_to_get_with_json';
769          ajax.handleAs = 'json';
770
771          request = ajax.generateRequest();
772          var promise = request.completes.then(function() {
773            expect(typeof(ajax.lastResponse)).to.be.equal('object');
774          });
775
776          expect(server.requests.length).to.be.equal(1);
777          expect(server.requests[0].requestHeaders['accept']).to.be.equal(
778            'application/json');
779
780          server.respond();
781
782          return promise;
783        });
784
785      });
786
787      suite('when making a POST over the wire', function() {
788        test('FormData is handled correctly', function() {
789          server.restore();
790          var requestBody = new FormData();
791          requestBody.append('a', 'foo');
792          requestBody.append('b', 'bar');
793
794          var ajax = fixture('RealPost');
795          ajax.body = requestBody;
796          return ajax.generateRequest().completes.then(function() {
797            expect(ajax.lastResponse.headers['Content-Type']).to.match(
798                /^multipart\/form-data; boundary=.*$/);
799
800            expect(ajax.lastResponse.form.a).to.be.equal('foo');
801            expect(ajax.lastResponse.form.b).to.be.equal('bar');
802          });
803        });
804
805        test('json is handled correctly', function() {
806          server.restore();
807          var ajax = fixture('RealPost');
808          ajax.body = JSON.stringify({a: 'foo', b: 'bar'});
809          ajax.contentType = 'application/json';
810          return ajax.generateRequest().completes.then(function() {
811            expect(ajax.lastResponse.headers['Content-Type']).to.match(
812                /^application\/json(;.*)?$/);
813            expect(ajax.lastResponse.json.a).to.be.equal('foo');
814            expect(ajax.lastResponse.json.b).to.be.equal('bar');
815          });
816        });
817
818        test('urlencoded data is handled correctly', function() {
819          server.restore();
820          var ajax = fixture('RealPost');
821          ajax.body = 'a=foo&b=bar';
822          return ajax.generateRequest().completes.then(function() {
823            expect(ajax.lastResponse.headers['Content-Type']).to.match(
824                /^application\/x-www-form-urlencoded(;.*)?$/);
825
826            expect(ajax.lastResponse.form.a).to.be.equal('foo');
827            expect(ajax.lastResponse.form.b).to.be.equal('bar');
828          });
829        });
830
831        test('xml is handled correctly', function() {
832          server.restore();
833          var ajax = fixture('RealPost');
834
835          var xmlDoc = document.implementation.createDocument(
836              null, "foo", null);
837          var node = xmlDoc.createElement("bar");
838          node.setAttribute("name" , "baz");
839          xmlDoc.documentElement.appendChild(node);
840          ajax.body = xmlDoc;
841          return ajax.generateRequest().completes.then(function() {
842            expect(ajax.lastResponse.headers['Content-Type']).to.match(
843                /^application\/xml(;.*)?$/);
844            expect(ajax.lastResponse.data).to.match(
845                /<foo\s*><bar\s+name="baz"\s*\/><\/foo\s*>/);
846          });
847        });
848      });
849
850      suite('when setting timeout', function() {
851        setup(function() {
852          server.restore();
853        });
854
855        test('it is present in the request xhr object', function() {
856          ajax.url = '/responds_to_get_with_json';
857          ajax.timeout = 5000; // 5 Seconds
858
859          request = ajax.generateRequest();
860          expect(request.xhr.timeout).to.be.equal(5000); // 5 Seconds
861        });
862
863        test('it fails once that timeout is reached', function() {
864          var ajax = fixture('Delay');
865          ajax.timeout = 1; // 1 Millisecond
866
867          request = ajax.generateRequest();
868          return request.completes.then(function() {
869            throw new Error('Expected the request to throw an error.');
870          }, function() {
871            expect(request.succeeded).to.be.equal(false);
872            expect(request.xhr.status).to.be.equal(0);
873            expect(request.timedOut).to.be.equal(true);
874            return timePasses(1);
875          }).then(function() {
876            expect(ajax.loading).to.be.equal(false);
877            expect(ajax.lastResponse).to.be.equal(null);
878            expect(ajax.lastError).to.not.be.equal(null);
879          });
880        });
881      });
882
883      suite('when using the bubbles attribute', function() {
884        test('the request and response events should bubble to window', function(done) {
885          server.restore();
886          var total = 0;
887          function incrementTotal() {
888            total++;
889            if (total === 5) {
890              done();
891            }
892          }
893          window.addEventListener('iron-ajax-presend', incrementTotal);
894          window.addEventListener('request', incrementTotal);
895          window.addEventListener('iron-ajax-request', incrementTotal);
896          window.addEventListener('response', incrementTotal);
897          window.addEventListener('iron-ajax-response', incrementTotal);
898          var ajax = fixture('Bubbles')[0];
899          ajax.generateRequest();
900          server.respond();
901        });
902
903        test('the request and error events should bubble to window', function(done) {
904          var total = 0;
905          function incrementTotal() {
906            total++;
907            if (total === 5) {
908              done();
909            }
910          }
911          window.addEventListener('iron-ajax-presend', incrementTotal);
912          window.addEventListener('request', incrementTotal);
913          window.addEventListener('iron-ajax-request', incrementTotal);
914          // NOTE(cdata): This needs to be capturing because Mocha + Firefox
915          // results in the error event being observed too early by the test
916          // runner and failing the test:
917          window.addEventListener('error', incrementTotal, true);
918          window.addEventListener('iron-ajax-error', incrementTotal);
919          var ajax = fixture('Bubbles')[1];
920          ajax.generateRequest();
921          server.respond();
922        });
923      });
924
925      suite('when handling the `iron-ajax-presend` event', function() {
926        setup(function() {
927          server.restore();
928        });
929
930        test('ability to cancel request', function() {
931          var requestSpy = sinon.spy();
932          var promiseSpy = sinon.spy();
933
934          ajax.addEventListener('iron-ajax-presend', function(e) {
935            e.preventDefault();
936          });
937          ajax.addEventListener('iron-ajax-request', requestSpy);
938
939          var request = ajax.generateRequest();
940
941          return request.completes.catch(function(request) {
942            expect(request.aborted).to.be.true;
943            promiseSpy();
944          }).then(function() {
945            expect(promiseSpy).to.be.calledOnce;
946            expect(requestSpy).not.to.be.called;
947          });
948        });
949
950        test('ability to modify the request options', function(done) {
951          ajax.addEventListener('iron-ajax-presend', function(e) {
952            e.detail.options.url += '/test';
953            e.detail.options.headers.authToken = 'a.b.c';
954          });
955          ajax.addEventListener('iron-ajax-request', function(e) {
956            expect(e.detail.options.url).to.equal('/responds_to_get_with_json/test');
957            expect(e.detail.options.headers.authToken).to.equal('a.b.c');
958            done();
959          });
960
961          ajax.generateRequest();
962        });
963      });
964    });
965  </script>
966
967</body>
968</html>
969