Skip to content

Commit

Permalink
fix(ajax): ensure post sending values
Browse files Browse the repository at this point in the history
- refactors ajax to send posted data properly
- adds logic for serializing data to url-formdata
- creates custom XMLHttpRequest mock to enable better testing and integrate better with the library
- ensures proper content type setting
- corrects tests that were false positives because of jasmine-ajax
  • Loading branch information
benlesh committed Jan 13, 2016
1 parent 1100bdd commit 7aae0a3
Show file tree
Hide file tree
Showing 3 changed files with 249 additions and 76 deletions.
126 changes: 116 additions & 10 deletions spec/helpers/ajax-helper.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,120 @@
var jasmineCore = require('jasmine-core');
var root = require('../../dist/cjs/util/root').root;

// jasmine-ajax need this
global.getJasmineRequireObj = function () {
return jasmineCore;
var requests = [];
var recentRequest = null;

function MockXMLHttpRequest() {
this.previousRequest = recentRequest;
recentRequest = this;
requests.push(this);
this.requestHeaders = {};
this.responseType = '';
this.eventHandlers = [];
this.readyState = 0;
}

MockXMLHttpRequest.prototype = {
send: function (data) {
this.data = data;
},

open: function (method, url, async, user, password) {
this.method = method;
this.url = url;
this.async = async;
this.user = user;
this.password = password;
this.readyState = 1;
this.triggerEvent('readystatechange');
},

setRequestHeader: function (key, value) {
this.requestHeaders[key] = value;
},

addEventListener: function (name, handler) {
this.eventHandlers.push({ name: name, handler: handler });
},

removeEventListener: function (name, handler) {
for (var i = this.eventHandlers.length - 1; i--;) {
var eh = this.eventHandlers[i];
if (eh.name === name && eh.handler === handler) {
this.eventHandlers.splice(i, 1);
}
}
},

throwError: function (err) {
// TODO: something better with errors
this.triggerEvent('error');
},

respondWith: function (response) {
this.readyState = 4;
this.responseHeaders = {
'Content-Type': response.contentType || 'text/plain'
};
this.status = response.status || 200;
this.responseText = response.responseText;
if (!('response' in response)) {
switch (this.responseType) {
case 'json':
try {
this.response = JSON.parse(response.responseText);
} catch (err) {
throw new Error('unable to JSON.parse: \n' + response.responseText);
}
break;
case 'text':
this.response = response.responseText;
break;
default:
throw new Error('unhandled type "' + this.responseType + '"');
}
}
// TODO: pass better event to onload.
this.triggerEvent('load');
this.triggerEvent('readystatechange');
},

triggerEvent: function (name, eventObj) {
// TODO: create a better default event
e = eventObj || {};

if (this['on' + name]) {
this['on' + name](e);
}

this.eventHandlers.forEach(function (eh) {
if (eh.name === name) {
eh.handler.call(this, e);
}
});
}
};

MockXMLHttpRequest.mostRecent = function () {
return recentRequest;
};

// XMLHttpRequest in node
global.XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest;
MockXMLHttpRequest.allRequests = function () {
return requests;
};

var gXHR;
var rXHR;

global.setupMockXHR = function () {
gXHR = global.XMLHttpRequest;
rXHR = root.XMLHttpRequest;
global.XMLHttpRequest = MockXMLHttpRequest;
root.XMLHttpRequest = MockXMLHttpRequest;
};

var w = global.window;
global.window = global;
require.call(global, 'jasmine-ajax');
global.window = w;
global.teardownMockXHR = function () {
global.XMLHttpRequest = gXHR;
root.XMLHttpRequest = rXHR;
requests.length = 0;
recentRequest = null;
};
118 changes: 74 additions & 44 deletions spec/observables/dom/ajax-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ function noop() {

describe('Observable.ajax', function () {
beforeEach(function () {
jasmine.Ajax.install();
setupMockXHR();
});

afterEach(function () {
jasmine.Ajax.uninstall();
teardownMockXHR();
});

it('should set headers', function () {
Expand All @@ -26,7 +26,7 @@ describe('Observable.ajax', function () {
})
.subscribe();

var request = jasmine.Ajax.requests.mostRecent();
var request = XMLHttpRequest.mostRecent();

expect(request.url).toBe('/talk-to-me-goose');
expect(request.requestHeaders).toEqual({
Expand All @@ -52,15 +52,13 @@ describe('Observable.ajax', function () {
})
.subscribe(function(x) {
result = x;
}, function () {
throw 'should not have been called';
}, function () {
}, null, function () {
complete = true;
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');

jasmine.Ajax.requests.mostRecent().respondWith({
XMLHttpRequest.mostRecent().respondWith({
'status': 200,
'contentType': 'application/json',
'responseText': expected
Expand Down Expand Up @@ -90,9 +88,9 @@ describe('Observable.ajax', function () {
throw 'should not complete';
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');

jasmine.Ajax.requests.mostRecent().respondWith({
XMLHttpRequest.mostRecent().respondWith({
'status': 200,
'contentType': 'application/json',
'responseText': expected
Expand Down Expand Up @@ -135,15 +133,13 @@ describe('Observable.ajax', function () {
})
.subscribe(function(x) {
result = x;
}, function () {
throw 'should not have been called';
}, function () {
}, null, function () {
complete = true;
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');

jasmine.Ajax.requests.mostRecent().respondWith({
XMLHttpRequest.mostRecent().respondWith({
'status': 200,
'contentType': 'application/json',
'responseText': JSON.stringify(expected)
Expand All @@ -162,20 +158,20 @@ describe('Observable.ajax', function () {
url: '/flibbertyJibbet',
normalizeError: function (e, xhr, type) {
return xhr.response || xhr.responseText;
}
},
responseType: 'text'
})
.subscribe(function (x) {
console.log(x);
throw 'should not next';
}, function (x) {
error = x;
}, function () {
throw 'should not complete';
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');

jasmine.Ajax.requests.mostRecent().respondWith({
XMLHttpRequest.mostRecent().respondWith({
'status': 404,
'contentType': 'text/plain',
'responseText': 'Wee! I am text!'
Expand All @@ -195,20 +191,20 @@ describe('Observable.ajax', function () {
url: '/flibbertyJibbet',
normalizeError: function (e, xhr, type) {
return xhr.response || xhr.responseText;
}
},
responseType: 'text'
})
.subscribe(function (x) {
console.log(x);
throw 'should not next';
}, function (x) {
error = x;
}, function () {
throw 'should not complete';
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');

jasmine.Ajax.requests.mostRecent().respondWith({
XMLHttpRequest.mostRecent().respondWith({
'status': 300,
'contentType': 'text/plain',
'responseText': 'Wee! I am text!'
Expand All @@ -232,8 +228,8 @@ describe('Observable.ajax', function () {
throw 'should not have been called';
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
jasmine.Ajax.requests.mostRecent().respondWith({
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');
XMLHttpRequest.mostRecent().respondWith({
'status': 200,
'contentType': 'text/plain',
'responseText': expected
Expand All @@ -255,8 +251,8 @@ describe('Observable.ajax', function () {
throw 'should not have been called';
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
jasmine.Ajax.requests.mostRecent().respondWith({
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');
XMLHttpRequest.mostRecent().respondWith({
'status': 500,
'contentType': 'text/plain',
'responseText': expected
Expand All @@ -265,63 +261,97 @@ describe('Observable.ajax', function () {

describe('ajax.get', function () {
it('should succeed on 200', function () {
var expected = 'some response';
var expected = { foo: 'bar' };
var result;
var complete = false;

Rx.Observable
.ajax.get('/flibbertyJibbet')
.subscribe(function(x) {
result = x;
}, function () {
throw 'should not have been called';
}, function () {
}, null, function () {
complete = true;
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
var request = XMLHttpRequest.mostRecent();

jasmine.Ajax.requests.mostRecent().respondWith({
expect(request.url).toBe('/flibbertyJibbet');

request.respondWith({
'status': 200,
'contentType': 'application/json',
'responseText': expected
'responseText': JSON.stringify(expected)
});

expect(result).toBe(expected);
expect(result).toEqual(expected);
expect(complete).toBe(true);
});


it('should succeed on 200 with a resultSelector', function () {
var expected = 'hahahahaha';
var expected = { larf: 'hahahahaha' };
var result, innerResult;
var complete = false;

Rx.Observable
.ajax.get('/flibbertyJibbet', function (x) {
innerResult = x;
return x.response.toUpperCase();
return x.response.larf.toUpperCase();
})
.subscribe(function(x) {
result = x;
}, function () {
throw 'should not have been called';
}, function () {
}, null , function () {
complete = true;
});

expect(jasmine.Ajax.requests.mostRecent().url).toBe('/flibbertyJibbet');
expect(XMLHttpRequest.mostRecent().url).toBe('/flibbertyJibbet');

jasmine.Ajax.requests.mostRecent().respondWith({
XMLHttpRequest.mostRecent().respondWith({
'status': 200,
'contentType': 'application/json',
'responseText': expected
'responseText': JSON.stringify(expected)
});

expect(innerResult.xhr).toBeDefined();
expect(innerResult.response).toBe(expected);
expect(innerResult.response).toEqual({ larf: 'hahahahaha' });
expect(result).toBe('HAHAHAHAHA');
expect(complete).toBe(true);
});
});

describe('ajax.post', function () {
it('should succeed on 200', function () {
var expected = { foo: 'bar', hi: 'there you' };
var result;
var complete = false;

Rx.Observable
.ajax.post('/flibbertyJibbet', expected)
.subscribe(function(x) {
result = x;
}, null , function () {
complete = true;
});

var request = XMLHttpRequest.mostRecent();

expect(request.method).toBe('POST');
expect(request.url).toBe('/flibbertyJibbet');
expect(request.requestHeaders).toEqual({
'X-Requested-With': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
})

request.respondWith({
'status': 200,
'contentType': 'application/json',
'responseText': JSON.stringify(expected)
});

expect(request.data).toEqual('foo=bar&hi=there%20you');
expect(result.response).toEqual(expected);
expect(complete).toBe(true);
});
});
});

Loading

0 comments on commit 7aae0a3

Please sign in to comment.