diff --git a/README.md b/README.md index 1381289..0e8168a 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resol ### Compatibility -Chai as Promised is compatible with all promises following the [Promises/A+ specification][spec]. Notably, jQuery's so-called “promises” are not up to spec, and Chai as Promised will not work with them. In particular, Chai as Promised makes extensive use of the standard [transformation behavior][] of `then`, which jQuery does not support. +Chai as Promised is compatible with all promises following the [Promises/A+ specification][spec]. Notably, jQuery's promises were not up to spec before jQuery 3.0, and Chai as Promised will not work with them. In particular, Chai as Promised makes extensive use of the standard [transformation behavior][] of `then`, which jQuery<3.0 does not support. ### Working with Non-Promise–Friendly Test Runners diff --git a/lib/chai-as-promised.js b/lib/chai-as-promised.js index 103ede1..f994877 100644 --- a/lib/chai-as-promised.js +++ b/lib/chai-as-promised.js @@ -34,8 +34,11 @@ var Assertion = chai.Assertion; var assert = chai.assert; - function isJQueryPromise(thenable) { - return typeof thenable.always === "function" && + function isLegacyJQueryPromise(thenable) { + // jQuery promises are Promises/A+-compatible since 3.0.0. jQuery 3.0.0 is also the first version + // to define the catch method. + return typeof thenable.catch !== "function" && + typeof thenable.always === "function" && typeof thenable.done === "function" && typeof thenable.fail === "function" && typeof thenable.pipe === "function" && @@ -47,9 +50,10 @@ if (typeof assertion._obj.then !== "function") { throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable."); } - if (isJQueryPromise(assertion._obj)) { - throw new TypeError("Chai as Promised is incompatible with jQuery's thenables, sorry! Please use a " + - "Promises/A+ compatible library (see http://promisesaplus.com/)."); + if (isLegacyJQueryPromise(assertion._obj)) { + throw new TypeError("Chai as Promised is incompatible with thenables of jQuery<3.0.0, sorry! Please " + + "upgrade jQuery or use another Promises/A+ compatible library (see " + + "http://promisesaplus.com/)."); } } diff --git a/package.json b/package.json index c041b5b..4911f53 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "test": "npm run test-plugin && npm run test-intercompatibility", "test-plugin": "mocha", "test-intercompatibility": "mocha test-intercompatibility --opts test-intercompatibility/mocha.opts", + "test-browser-jquery": "coffee ./test/browser/runner.coffee jquery", "test-browser-q": "coffee ./test/browser/runner.coffee q", "test-browser-when": "coffee ./test/browser/runner.coffee when", "lint": "jshint ./lib", diff --git a/test/browser/libraries/jquery.coffee b/test/browser/libraries/jquery.coffee new file mode 100644 index 0000000..76a4449 --- /dev/null +++ b/test/browser/libraries/jquery.coffee @@ -0,0 +1,28 @@ +"use strict" + +exports.name = "jQuery" + +exports.uri = "https://code.jquery.com/jquery-git.js" + +exports.adapter = """ + global.fulfilledPromise = function (value) { + var deferred = jQuery.Deferred(); + deferred.resolve(value); + return deferred.promise(); + }; + global.rejectedPromise = function (reason) { + var deferred = jQuery.Deferred(); + deferred.reject(reason); + return deferred.promise(); + }; + global.defer = jQuery.Deferred; + global.waitAll = function (promises) { + // TODO uncomment & remove the alternative implementation when + // https://github.com/jquery/jquery/issues/2018 and + // https://github.com/jquery/jquery/issues/2546 get fixed. + // return jQuery.when.apply(null, promises); + var deferred = jQuery.Deferred(); + deferred.resolve(jQuery.when.apply(null, promises)); + return deferred.promise(); + }; +"""