forked from pretenderjs/pretender
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tl;dr - This commit adds support for fetch. - pretender swap native fetch related API if exists - pretender.shutdown() restore native fetch related API - doesn't work with AbortController Motivation ------ Pretender has been working well with xhr, but doesn't handle fetch. It's been 2 years since @rwjblue first open the [issue](pretenderjs#60) for fetch support. So people don't need to do extra work to polyfill for testing. Changes ------ Include a fetch ponyfill and swap the native fetch during pretender creation, then restore them when `shutdown`. Since fetch polyfill uses xhr behind the scene, pretender should "just work". Caveats ------ 1. The supplement set of yetch impl and spec includes (not complete): - Inability to [set the redirect mode](JakeChampion/fetch#137) - Inability to [change the cache directive](JakeChampion/fetch#438 (comment)) - Inability to [disable same-origin cookies](JakeChampion/fetch#56 (comment)) 2. Abort - `xhr.abort()` first set state to done, finally response to a [network error](https://xhr.spec.whatwg.org/#the-abort()-method); - [fetch](https://dom.spec.whatwg.org/#aborting-ongoing-activities) will reject promise with a new "AbortError" DOMException. As implemented in `fake_xml_http_request`, the request is resolved once its state is changed to `DONE`. So the senario happens in pretender is: 1). state changes 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. 3. Though polyfilled by xhr, fetch returns a Promise and is asynchronous by nature.
- Loading branch information
Thomas Wang
committed
May 23, 2018
1 parent
7101a31
commit 3369d4e
Showing
5 changed files
with
259 additions
and
13 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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(); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.