Skip to content
This repository has been archived by the owner on Mar 13, 2018. It is now read-only.

Commit

Permalink
Merge pull request #14 from rictic/master
Browse files Browse the repository at this point in the history
Fix race condition with response attribute. Fixes #12.
  • Loading branch information
John Messerly committed Sep 18, 2014
2 parents 0898920 + 89e5ec2 commit 5e0d9f8
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 26 deletions.
69 changes: 44 additions & 25 deletions core-ajax.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
params='{"alt":"json", "q":"chrome"}'
handleAs="json"
on-core-response="{{handleResponse}}"></core-ajax>
With `auto` set to `true`, the element performs a request whenever
its `url` or `params` properties are changed.
Expand All @@ -32,19 +32,19 @@
@homepage github.io
-->
<link rel="import" href="core-xhr.html">
<polymer-element name="core-ajax" hidden attributes="url handleAs auto params response method headers body contentType withCredentials">
<polymer-element name="core-ajax" hidden attributes="url handleAs auto params response error method headers body contentType withCredentials">
<script>

Polymer('core-ajax', {
/**
* Fired when a response is received.
*
*
* @event core-response
*/

/**
* Fired when an error is received.
*
*
* @event core-error
*/

Expand All @@ -56,7 +56,7 @@

/**
* The URL target of the request.
*
*
* @attribute url
* @type string
* @default ''
Expand All @@ -66,21 +66,21 @@
/**
* Specifies what data to store in the `response` property, and
* to deliver as `event.response` in `response` events.
*
*
* One of:
*
*
* `text`: uses `XHR.responseText`.
*
*
* `xml`: uses `XHR.responseXML`.
*
*
* `json`: uses `XHR.responseText` parsed as JSON.
*
* `arraybuffer`: uses `XHR.response`.
*
* `blob`: uses `XHR.response`.
*
* `document`: uses `XHR.response`.
*
*
* @attribute handleAs
* @type string
* @default 'text'
Expand All @@ -98,22 +98,33 @@

/**
* Parameters to send to the specified URL, as JSON.
*
*
* @attribute params
* @type string (JSON)
* @default ''
*/
params: '',

/**
* Returns the response object.
* The response for the most recently made request, or null if it hasn't
* completed yet or the request resulted in error.
*
* @attribute response
* @type Object
* @default null
*/
response: null,

/**
* The error for the most recently made request, or null if it hasn't
* completed yet or the request resulted in success.
*
* @attribute error
* @type Object
* @default null
*/
error: null,

/**
* The HTTP method to use such as 'GET', 'POST', 'PUT', or 'DELETE'.
* Default is 'GET'.
Expand All @@ -129,13 +140,13 @@
*
* Example:
*
* <core-ajax
* <core-ajax
* auto
* url="http://somesite.com"
* headers='{"X-Requested-With": "XMLHttpRequest"}'
* handleAs="json"
* on-core-response="{{handleResponse}}"></core-ajax>
*
*
* @attribute headers
* @type Object
* @default null
Expand All @@ -150,7 +161,7 @@
* <core-ajax method="POST" auto url="http://somesite.com"
* body='{"foo":1, "bar":2}'>
* </core-ajax>
*
*
* @attribute body
* @type Object
* @default null
Expand All @@ -168,26 +179,26 @@

/**
* Set the withCredentials flag on the request.
*
*
* @attribute withCredentials
* @type boolean
* @default false
*/
withCredentials: false,

/**
* Additional properties to send to core-xhr.
*
* Can be set to an object containing default properties
* to send as arguments to the `core-xhr.request()` method
* which implements the low-level communication.
*
*
* @property xhrArgs
* @type Object
* @default null
*/
xhrArgs: null,

ready: function() {
this.xhr = document.createElement('core-xhr');
},
Expand All @@ -196,7 +207,7 @@
if (this.isSuccess(xhr)) {
this.processResponse(xhr);
} else {
this.error(xhr);
this.processError(xhr);
}
this.complete(xhr);
},
Expand All @@ -208,12 +219,17 @@

processResponse: function(xhr) {
var response = this.evalResponse(xhr);
this.response = response;
if (xhr === this.activeRequest) {
this.response = response;
}
this.fire('core-response', {response: response, xhr: xhr});
},

error: function(xhr) {
processError: function(xhr) {
var response = xhr.status + ': ' + xhr.responseText;
if (xhr === this.activeRequest) {
this.error = response;
}
this.fire('core-error', {response: response, xhr: xhr});
},

Expand Down Expand Up @@ -277,8 +293,8 @@
this.autoGo();
},

// TODO(sorvell): multiple side-effects could call autoGo
// during one micro-task, use a job to have only one action
// TODO(sorvell): multiple side-effects could call autoGo
// during one micro-task, use a job to have only one action
// occur
autoGo: function() {
if (this.auto) {
Expand Down Expand Up @@ -317,7 +333,10 @@
args.callback = this.receive.bind(this);
args.url = this.url;
args.method = this.method;
return args.url && this.xhr.request(args);

this.response = this.error = null;
this.activeRequest = args.url && this.xhr.request(args);
return this.activeRequest;
}

});
Expand Down
109 changes: 109 additions & 0 deletions tests/html/core-ajax-response-and-error.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<!doctype html>
<!--
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>
<title>core-ajax-response-and-error</title>

<script src="../../../platform/platform.js"></script>
<script src="../../../polymer-test-tools/chai/chai.js"></script>
<script src="../../../polymer-test-tools/htmltest.js"></script>

<link rel="import" href="../../core-ajax.html">

</head>
<body>

<polymer-element name="race-condition">
<template>
<style>section {margin-top: 20px;}</style>
<core-ajax
id='ajax'
url="http://httpbin.org/delay/{{delay}}"
handleas="json" auto
response="{{response}}"
on-core-response="{{handleResponse}}"></core-ajax>
<div>Response url: {{response.url}}</div>
<div>Result: {{testResult}}</div>

<section>
<div>Requests</div>
<ul>
<template repeat='{{request in requests}}'>
<li>
{{request.delay}} second delay, Status: {{request.statusText}}
</li>
</template>
</ul>
</section>
</template>
<script>
Polymer({
delay: 1,
response: null,
testResult: 'pending...',
passed: false,
requests: [],
observe: {
'$.ajax.activeRequest': 'requestChanged'
},
domReady: function() {
setTimeout(function() {
if (this.response != null) {
console.error('HTTP request returned too quick!')
chai.assert.fail(
'', '', 'Indeterminate, initial request returned too quick');
this.testResult = 'indeterminate';
return;
}
this.delay = 2;
}.bind(this), 100);
// This will fail the test if it neither passes nor fails in time.
this.finalTimeout = setTimeout(function() {
chai.assert.fail('', '', 'Test timed out.');
}, 7000);
},
responseChanged: function() {
if (this.response.url != this.$.ajax.url) {
this.testResult = 'FAIL';
chai.assert.fail(this.$.ajax.url, this.response.url,
'Race condition in response attribute');
return;
}
this.passed = true;
},
passedChanged: function() {
if (this.passed && this.testResult == 'pending...') {
this.testResult = 'PASS';
clearTimeout(this.finalTimeout);
done();
}
},
requestChanged: function(o, n) {
this.requests.push({
'statusText': 'pending',
xhr: n,
delay: this.delay
});
},
handleResponse: function(resp) {
var xhr = resp.detail.xhr;
for (var i = 0; i < this.requests.length; i++) {
if (this.requests[i].xhr === xhr) {
this.requests[i].statusText = xhr.statusText;
}
}
},
});
</script>
</polymer-element>

<race-condition></race-condition>
</body>
</html>
3 changes: 2 additions & 1 deletion tests/js/htmltests.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
htmlSuite('core-ajax', function() {
htmlTest('html/core-ajax.html');
});
htmlTest('html/core-ajax-response-and-error.html');
});

0 comments on commit 5e0d9f8

Please sign in to comment.