Sinon version: v1.12.2
Environment: OSX 10.10.1
Example URL: N/A
Description
I'm attempting to write unit tests for jQuery ajax requests in a JSDom environment, but Sinon's fake XHR is not intercepting the requests. The exact same suite does work in the browser.
I know the jQueryXHR + JSDom combo can cause some folks trouble, but I'm fairly certain that Ive got them configured correctly. I've set the global.window and global.XMLHttpRequest object from JSDom so that jQuery can make XHR requests, and I've verified that this works. The issue comes in when I try to fake the requests with Sinon: they aren't intercepted.
It comes down to sinonXhr.supportsXHR being calculated as false. This follows from this line, where it looks for XMLHttpRequest on the local variable global. That local variable is passed in here. In Node, this is an empty object, while global is where it should be looking at for the xml http request.
It seems unlikely to me that this would go unnoticed for so long, so maybe I'm configuring things oddly. Has anyone had success with jQuery+JSDom+Sinon's FakeXHRs?
Interestingly enough, git reverting this commit solves the problem. If this is, in fact, a regression, then perhaps there's a way to fix #508 and #511 in some other way.
Update: Even after the git revert, it still passes in the browser.
Update
That PR didn't quite fix my tests. Still working on a fix!
If, by some rare chance you read this and are experiencing the same issue, simply update your package.json:
"sinon": "git://github.com/jmeas/sinon.js.git#ajax-node"
That branch implements #659. I'll keep it around as long as this repo doesn't have the fix for the problem.
My working setup for a new redux project:
Sinon: 1.17.3
jQuery: 2.2.2
jsdom: 8.3.0
import 'babel-polyfill';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';
import jsdom from 'jsdom';
global.jsdom = jsdom.jsdom;
global.document = global.jsdom('<!doctype html><html><body></body></html>');
global.window = global.document.defaultView;
global.XMLHttpRequest = global.window.XMLHttpRequest;
chai.use(chaiImmutable);
global.expect = chai.expect;
global.assert = chai.assert;
global.sinon = require('sinon');
global.sinon.useFakeXMLHttpRequest();
global.window.XMLHttpRequest = global.XMLHttpRequest;
global.$ = require('jquery')(global.window);
I bumped into this exact issue - the key was loading sinon after the globals have been set up (ie. XMLHttpRequest) so that XHR support check works as expected.
That means you won't be able to use ES6 import at the top of the file, as sinon will be loaded before the globals - so resorting to CommonJS require as in the example above.
global.XMLHttpRequest = global.window.XMLHttpRequest;
// ...
global.sinon = require('sinon');
@alistairjcbrown : have you tried Sinon 2? That version works with module bundlers and I think it should prove less bothersome.
I faced the same problem in https://github.com/yiisoft/yii2/pull/13160.
@fatso83 I tried the latest version (2.0.0-pre.4), the error persists. Used @alistairjcbrown solution as a workaround. Thanks for him for discovering this.
I use mocha-jsdom wrapper. In my case I predefined sinon variable without assigning value to it at the very top of the test file:
var sinon;
Then I imported sinon to it after registering jsdom and testable code:
jsdom({src: fs.readFileSync('vendor/bower/jquery/dist/jquery.js', 'utf-8')});
before(function () {
$ = window.$;
registerTestableCode();
sinon = require('sinon');
});
Then in specific test suite where fake server is needed I wrote:
describe('refresh', function () {
var server;
beforeEach(function () {
server = sinon.fakeServer.create();
window.XMLHttpRequest = global.XMLHttpRequest; // This line is important
});
afterEach(function () {
server.restore();
});
// Testing code
});
This works fine, however for convenience and speed on dev server I included only one file in tests suite. Tests were passing, when I added the rest of the files back, the same error appeared:
TypeError: Cannot read property 'respond' of undefined
coming from line:
server.requests[0].respond(200, {"Content-Type": "application/json"}, JSON.stringify(response));
So I had to import sinon in similar fashion in other files even there are no AJAX related code at all and file with AJAX is tested before the other ones.
Looking for a better fix.
Most helpful comment
My working setup for a new redux project:
Sinon: 1.17.3
jQuery: 2.2.2
jsdom: 8.3.0