Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fetch support #230

Merged
merged 7 commits into from
Jul 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
# Pretender Changelog

## 2.1
* [230](https://github.com/pretenderjs/pretender/pull/230) Support `fetch`

## 2.0
* **Breaking change**: updated [fake-xml-http-request](https://github.com/pretenderjs/FakeXMLHttpRequest) to 2.0 (dropping support for end-of-life node versions)
* Improved webpack compatiblity through using module defaults [216](https://github.com/pretenderjs/pretender/pull/216)
* Added TypeScript type information [223](https://github.com/pretenderjs/pretender/pull/223)

## 1.4.1
* [188](https://github.com/pretenderjs/pretender/pull/178) Console warn if a second pretender instance is started

Expand All @@ -12,7 +17,7 @@
* [171](https://github.com/pretenderjs/pretender/pull/171) Fix url behavior in IE 11
* [177](https://github.com/pretenderjs/pretender/pull/177) Allow handlers to return a Promise

## 1.3.0
## 1.3.0
* [168](https://github.com/pretenderjs/pretender/pull/168) "Verb" methods now return handler
* [166](https://github.com/pretenderjs/pretender/pull/166) HTTP `options` request type added

Expand Down
2 changes: 2 additions & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/@xg-wang/whatwg-fetch/dist/fetch.umd.js',
'pretender.js',
'test/**/*.js'
],
Expand Down
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"url": "https://github.com/pretenderjs/pretender.git"
},
"devDependencies": {
"bower": "^1.3.5",
"abortcontroller-polyfill": "^1.1.9",
"bower": "^1.8.2",
"coveralls": "^2.11.3",
"es6-promise": "^4.0.5",
"jscs": "^3.0.7",
Expand All @@ -38,14 +39,16 @@
},
"dependencies": {
"fake-xml-http-request": "^2.0.0",
"route-recognizer": "^0.3.3"
"route-recognizer": "^0.3.3",
"@xg-wang/whatwg-fetch": "^3.0.0"
},
"jspm": {
"shim": {
"pretender": {
"deps": [
"route-recognizer",
"fake-xml-http-request"
"fake-xml-http-request",
"@xg-wang/whatwg-fetch"
],
"exports": "Pretender"
}
Expand Down
14 changes: 14 additions & 0 deletions pretender.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ var RouteRecognizer = appearsBrowserified ? getModuleDefault(require('route-reco
var FakeXMLHttpRequest = appearsBrowserified ? getModuleDefault(require('fake-xml-http-request')) :
self.FakeXMLHttpRequest;

// fetch related ponyfills
// TODO: use whatwg-fetch once new version release
var FakeFetch = appearsBrowserified ? getModuleDefault(require('@xg-wang/whatwg-fetch')) : self.WHATWGFetch;

/**
* parseURL - decompose a URL into its parts
* @param {String} url a URL
Expand Down Expand Up @@ -139,6 +143,13 @@ function Pretender(/* routeMap1, routeMap2, ..., options*/) {
// the route map.
self.XMLHttpRequest = interceptor(ctx);

// polyfill fetch when xhr is ready
this._fetchProps = ['fetch', 'Headers', 'Request', 'Response'];
this._fetchProps.forEach(function(name) {
this['_native' + name] = self[name];
self[name] = FakeFetch[name];
}, this);

// 'start' the server
this.running = true;

Expand Down Expand Up @@ -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;
Expand Down
123 changes: 123 additions & 0 deletions test/fetch_test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is async, so this needs to block

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is qunit lets use the async promise assert helpers

// This won't be called
assert.equal(val, 0);
val++;
});
assert.equal(val, 0);
});


// TODO: Pretender doesn't work with abortable fetch
// src in fake_xml_http_request.js
// ```
// abort: function abort() {
// this.aborted = true;
// this.responseText = null;
// this.errorFlag = true;
// this.requestHeaders = {};

// if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) {
// this._readyStateChange(FakeXMLHttpRequest.DONE);
// this.sendFlag = false;
// }

// this.readyState = FakeXMLHttpRequest.UNSENT;

// this.dispatchEvent(new _Event("abort", false, false, this));
// if (typeof this.onerror === "function") {
// this.onerror();
// }
// }
// ```
// 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.
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 })
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is qunit lets use the async promise assert helpers

.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
done();
});
});
});
Loading