diff --git a/karma.conf.js b/karma.conf.js index f1ff6b8..f8bff7c 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -20,6 +20,8 @@ module.exports = function(config) { 'bower_components/jquery-1/index.js', 'bower_components/jquery/dist/jquery.js', 'node_modules/es6-promise/dist/es6-promise.auto.js', + 'node_modules/abortcontroller-polyfill/dist/abortcontroller-polyfill-only.js', + 'node_modules/yetch/dist/yetch-ponyfill.js', 'pretender.js', 'test/**/*.js' ], diff --git a/package.json b/package.json index fa720ed..da567f2 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,10 @@ "sinon": "^3.2.1" }, "dependencies": { + "abortcontroller-polyfill": "^1.1.9", "fake-xml-http-request": "^2.0.0", - "route-recognizer": "^0.3.3" + "route-recognizer": "^0.3.3", + "yetch": "^0.0.1" }, "jspm": { "shim": { diff --git a/pretender.js b/pretender.js index 221c6d9..8d59961 100644 --- a/pretender.js +++ b/pretender.js @@ -13,6 +13,9 @@ var RouteRecognizer = appearsBrowserified ? getModuleDefault(require('route-reco var FakeXMLHttpRequest = appearsBrowserified ? getModuleDefault(require('fake-xml-http-request')) : self.FakeXMLHttpRequest; +// fetch related ponyfills +var Yetch = appearsBrowserified ? getModuleDefault(require('yetch/dist/yetch-polyfill')) : self.Yetch; + /** * parseURL - decompose a URL into its parts * @param {String} url a URL @@ -139,6 +142,14 @@ function Pretender(/* routeMap1, routeMap2, ..., options*/) { // the route map. self.XMLHttpRequest = interceptor(ctx); + // polyfill fetch when xhr is ready + // AbortController doesn't need restore + this._fetchProps = ['fetch', 'Headers', 'Request', 'Response']; + this._fetchProps.forEach(function(name) { + this['_native' + name] = self[name]; + self[name] = Yetch[name]; + }, this); + // 'start' the server this.running = true; @@ -471,6 +482,9 @@ Pretender.prototype = { }, shutdown: function shutdown() { self.XMLHttpRequest = this._nativeXMLHttpRequest; + this._fetchProps.forEach(function(name) { + self[name] = this['_native' + name]; + }, this); this.ctx.pretender = undefined; // 'stop' the server this.running = false; diff --git a/test/fetch_test.js b/test/fetch_test.js new file mode 100644 index 0000000..89d7b84 --- /dev/null +++ b/test/fetch_test.js @@ -0,0 +1,102 @@ +var describe = QUnit.module; +var it = QUnit.test; +var clock; + +describe('pretender invoking by fetch', function(config) { + config.beforeEach(function() { + this.pretender = new Pretender(); + }); + + config.afterEach(function() { + if (clock) { + clock.restore(); + } + this.pretender.shutdown(); + }); + + it('fetch triggers pretender', function(assert) { + var wasCalled; + + this.pretender.get('/some/path', function() { + wasCalled = true; + }); + + fetch('/some/path'); + assert.ok(wasCalled); + }); + + it('is resolved asynchronously', function(assert) { + assert.expect(2); + var done = assert.async(); + var val = 'unset'; + + this.pretender.get('/some/path', function(request) { + return [200, {}, '']; + }); + + fetch('/some/path').then(function() { + assert.equal(val, 'set'); + done(); + }); + + assert.equal(val, 'unset'); + val = 'set'; + }); + + it('can NOT be resolved synchronously', function(assert) { + assert.expect(1); + var val = 0; + + this.pretender.get( + '/some/path', + function(request) { + return [200, {}, '']; + }, + false + ); + + fetch('/some/path').then(function() { + // This won't be called + assert.equal(val, 0); + val++; + }); + assert.equal(val, 0); + }); + + it('has NO Abortable fetch', function(assert) { + assert.expect(1); + var done = assert.async(); + var wasCalled = false; + this.pretender.get( + '/downloads', + function(request) { + return [200, {}, 'FAIL']; + }, + 200 + ); + + var controller = new AbortController(); + var signal = controller.signal; + setTimeout(function() { + controller.abort(); + }, 10); + fetch('/downloads', { signal: signal }) + .then(function(data) { + assert.ok(data, 'AbortError was not rejected'); + done(); + }) + .catch(function(err) { + // it should execute to here but won't due to FakeXmlHttpRequest limitation + // + // ### why it's not working for fetch + // For `fake_xml_http_request` impl, the request is resolved once its state + // is changed to `DONE` so the `reject` is not cathed. + // So the senario happens in pretender is: + // 1. state chagne to `DONE`, trigger resolve request + // 2. abort, trigger reject + // 3. xhr.onerror, trigger reject + // The first resolve wins, error thus not rejected but an empty request is resolved. + done(); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 23240b4..f1a3ec8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,12 @@ # yarn lockfile v1 +"@sinonjs/formatio@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-2.0.0.tgz#84db7e9eb5531df18a8c5e0bfb6e449e55e654b2" + dependencies: + samsam "1.3.0" + "JSV@>= 4.0.x": version "4.0.2" resolved "https://registry.npmjs.org/JSV/-/JSV-4.0.2.tgz#d077f6825571f82132f9dffaed587b4029feff57" @@ -14,6 +20,10 @@ abbrev@1.0.x: version "1.0.9" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" +abortcontroller-polyfill@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.1.9.tgz#9fefe359fda2e9e0932dc85e6106453ac393b2da" + accepts@1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca" @@ -140,6 +150,10 @@ async@^2.0.1: dependencies: lodash "^4.14.0" +async@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/async/-/async-1.0.0.tgz#f8fc04ca3a13784ade9e1641af98578cfbd647a9" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -261,6 +275,21 @@ braces@^1.8.2: preserve "^0.2.0" repeat-element "^1.1.2" +build@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/build/-/build-0.1.4.tgz#707fe026ffceddcacbfdcdf356eafda64f151046" + dependencies: + cssmin "0.3.x" + jsmin "1.x" + jxLoader "*" + moo-server "*" + promised-io "*" + timespan "2.x" + uglify-js "1.x" + walker "1.x" + winston "*" + wrench "1.3.x" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -384,7 +413,7 @@ colors@0.6.x: version "0.6.2" resolved "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" -colors@1.0.3: +colors@1.0.3, colors@1.0.x: version "1.0.3" resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" @@ -499,6 +528,10 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" +cssmin@0.3.x: + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssmin/-/cssmin-0.3.2.tgz#ddce4c547b510ae0d594a8f1fbf8aaf8e2c5c00d" + cst@^0.4.3: version "0.4.10" resolved "https://registry.npmjs.org/cst/-/cst-0.4.10.tgz#9c05c825290a762f0a85c0aabb8c0fe035ae8516" @@ -839,9 +872,9 @@ eyes@0.1.x: version "0.1.8" resolved "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" -fake-xml-http-request@^1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/fake-xml-http-request/-/fake-xml-http-request-1.6.0.tgz#bd0ac79ae3e2660098282048a12c730a6f64d550" +fake-xml-http-request@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fake-xml-http-request/-/fake-xml-http-request-2.0.0.tgz#41a92f0ca539477700cb1dafd2df251d55dac8ff" fast-levenshtein@~2.0.4: version "2.0.6" @@ -1395,6 +1428,10 @@ js-reporters@1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/js-reporters/-/js-reporters-1.2.0.tgz#7cf2cb698196684790350d0c4ca07f4aed9ec17e" +js-yaml@0.3.x: + version "0.3.7" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-0.3.7.tgz#d739d8ee86461e54b354d6a7d7d1f2ad9a167f62" + js-yaml@3.6.1: version "3.6.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" @@ -1482,6 +1519,10 @@ jshint@^2.8.0: shelljs "0.3.x" strip-json-comments "1.0.x" +jsmin@1.x: + version "1.0.1" + resolved "https://registry.yarnpkg.com/jsmin/-/jsmin-1.0.1.tgz#e7bd0dcd6496c3bf4863235bf461a3d98aa3b98c" + json-schema@0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -1530,6 +1571,19 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.3.6" +just-extend@^1.1.27: + version "1.1.27" + resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-1.1.27.tgz#ec6e79410ff914e472652abfa0e603c03d60e905" + +jxLoader@*: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jxLoader/-/jxLoader-0.1.1.tgz#0134ea5144e533b594fc1ff25ff194e235c53ecd" + dependencies: + js-yaml "0.3.x" + moo-server "1.3.x" + promised-io "*" + walker "1.x" + karma-chrome-launcher@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-2.2.0.tgz#cf1b9d07136cc18fe239327d24654c3dbc368acf" @@ -1641,6 +1695,10 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +lodash.get@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" + lodash@3.7.x: version "3.7.0" resolved "https://registry.npmjs.org/lodash/-/lodash-3.7.0.tgz#3678bd8ab995057c07ade836ed2ef087da811d45" @@ -1664,9 +1722,9 @@ log4js@^0.6.31: readable-stream "~1.0.2" semver "~4.3.3" -lolex@^1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6" +lolex@^2.1.2, lolex@^2.3.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.6.0.tgz#cf9166f3c9dece3cdeb5d6b01fce50f14a1203e3" longest@^1.0.1: version "1.0.1" @@ -1683,6 +1741,12 @@ lru-cache@2.2.x: version "2.2.4" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d" +makeerror@1.0.x: + version "1.0.11" + resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" + dependencies: + tmpl "1.0.x" + map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -1774,6 +1838,10 @@ mkdirp@0.5.x, mkdirp@0.x.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1: dependencies: minimist "0.0.8" +moo-server@*, moo-server@1.3.x: + version "1.3.0" + resolved "https://registry.yarnpkg.com/moo-server/-/moo-server-1.3.0.tgz#5dc79569565a10d6efed5439491e69d2392e58f1" + ms@0.7.1: version "0.7.1" resolved "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" @@ -1810,6 +1878,16 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +nise@^1.0.1: + version "1.3.3" + resolved "https://registry.yarnpkg.com/nise/-/nise-1.3.3.tgz#c17a850066a8a1dfeb37f921da02441afc4a82ba" + dependencies: + "@sinonjs/formatio" "^2.0.0" + just-extend "^1.1.27" + lolex "^2.3.2" + path-to-regexp "^1.7.0" + text-encoding "^0.6.4" + node-pre-gyp@^0.6.36: version "0.6.36" resolved "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.36.tgz#db604112cb74e0d477554e9b505b17abddfab786" @@ -2098,6 +2176,10 @@ progress@~1.1.8: version "1.1.8" resolved "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" +promised-io@*: + version "0.3.5" + resolved "https://registry.yarnpkg.com/promised-io/-/promised-io-0.3.5.tgz#4ad217bb3658bcaae9946b17a8668ecd851e1356" + prompt@~0.2.14: version "0.2.14" resolved "https://registry.npmjs.org/prompt/-/prompt-0.2.14.tgz#57754f64f543fd7b0845707c818ece618f05ffdc" @@ -2417,6 +2499,10 @@ safe-buffer@^5.0.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" +samsam@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" + samsam@1.x, samsam@^1.1.3: version "1.2.1" resolved "https://registry.npmjs.org/samsam/-/samsam-1.2.1.tgz#edd39093a3184370cb859243b2bdf255e7d8ea67" @@ -2449,14 +2535,17 @@ signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -sinon@^2.3.8: - version "2.3.8" - resolved "https://registry.npmjs.org/sinon/-/sinon-2.3.8.tgz#31de06fed8fba3a671e576dd96d0a5863796f25c" +sinon@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-3.3.0.tgz#9132111b4bbe13c749c2848210864250165069b1" dependencies: + build "^0.1.4" diff "^3.1.0" formatio "1.2.0" - lolex "^1.6.0" + lodash.get "^4.4.2" + lolex "^2.1.2" native-promise-only "^0.8.1" + nise "^1.0.1" path-to-regexp "^1.7.0" samsam "^1.1.3" text-encoding "0.6.4" @@ -2657,7 +2746,7 @@ tar@^2.2.1: fstream "^1.0.2" inherits "2" -text-encoding@0.6.4: +text-encoding@0.6.4, text-encoding@^0.6.4: version "0.6.4" resolved "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" @@ -2665,12 +2754,20 @@ throttleit@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c" +timespan@2.x: + version "2.3.0" + resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929" + tmp@0.0.31, tmp@0.0.x: version "0.0.31" resolved "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" dependencies: os-tmpdir "~1.0.1" +tmpl@1.0.x: + version "1.0.4" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" + to-array@0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" @@ -2732,6 +2829,10 @@ typedarray@~0.0.5: version "0.0.6" resolved "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" +uglify-js@1.x: + version "1.3.5" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-1.3.5.tgz#4b5bfff9186effbaa888e4c9e94bd9fc4c94929d" + uglify-js@^2.6: version "2.8.29" resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" @@ -2838,6 +2939,12 @@ walk-sync@0.3.1: ensure-posix-path "^1.0.0" matcher-collection "^1.0.0" +walker@1.x: + version "1.0.7" + resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" + dependencies: + makeerror "1.0.x" + which@^1.1.1, which@^1.2.1, which@^1.2.12, which@~1.2.10, which@~1.2.2: version "1.2.14" resolved "https://registry.npmjs.org/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" @@ -2854,6 +2961,17 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" +winston@*: + version "2.4.2" + resolved "https://registry.yarnpkg.com/winston/-/winston-2.4.2.tgz#3ca01f763116fc48db61053b7544e750431f8db0" + dependencies: + async "~1.0.0" + colors "1.0.x" + cycle "1.0.x" + eyes "0.1.x" + isstream "0.1.x" + stack-trace "0.0.x" + winston@0.8.x: version "0.8.3" resolved "https://registry.npmjs.org/winston/-/winston-0.8.3.tgz#64b6abf4cd01adcaefd5009393b1d8e8bec19db0" @@ -2882,6 +3000,10 @@ wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" +wrench@1.3.x: + version "1.3.9" + resolved "https://registry.yarnpkg.com/wrench/-/wrench-1.3.9.tgz#6f13ec35145317eb292ca5f6531391b244111411" + ws@1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/ws/-/ws-1.1.2.tgz#8a244fa052401e08c9886cf44a85189e1fd4067f" @@ -2925,3 +3047,7 @@ yauzl@2.4.1: yeast@0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + +yetch@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/yetch/-/yetch-0.0.1.tgz#76f1729b2c2c667e23c3c2da90472a4897eca004"