You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1170 lines
28 KiB
1170 lines
28 KiB
'use strict';
|
|
var fetchBuilder = require('../../');
|
|
var sinon = require('sinon');
|
|
var expect = require('expectations');
|
|
|
|
describe('fetchBuilder', function () {
|
|
|
|
it('throws when fetch is not a function', function () {
|
|
expect(function () {
|
|
fetchBuilder();
|
|
}).toThrow({
|
|
name: 'ArgumentError',
|
|
message: 'fetch must be a function'
|
|
});
|
|
});
|
|
|
|
it('throws when default is not an object', function () {
|
|
expect(function () {
|
|
fetchBuilder(function () { }, 'this is a string, not an object');
|
|
}).toThrow({
|
|
name: 'ArgumentError',
|
|
message: 'defaults must be an object'
|
|
});
|
|
});
|
|
|
|
it('returns fetchRetry when provided valid constructor arguments', function () {
|
|
expect(typeof fetchBuilder(function () { }, {retries: 1})).toBe('function');
|
|
});
|
|
});
|
|
|
|
describe('fetch-retry', function () {
|
|
|
|
var fetch;
|
|
var fetchRetry;
|
|
|
|
var deferred1;
|
|
var deferred2;
|
|
var deferred3;
|
|
var deferred4;
|
|
|
|
var thenCallback;
|
|
var catchCallback;
|
|
|
|
var clock;
|
|
var delay;
|
|
|
|
beforeEach(function () {
|
|
delay = 1000;
|
|
clock = sinon.useFakeTimers();
|
|
});
|
|
|
|
afterEach(function () {
|
|
clock.restore();
|
|
});
|
|
|
|
beforeEach(function () {
|
|
deferred1 = defer();
|
|
deferred2 = defer();
|
|
deferred3 = defer();
|
|
deferred4 = defer();
|
|
|
|
fetch = sinon.stub();
|
|
fetch.onCall(0).returns(deferred1.promise);
|
|
fetch.onCall(1).returns(deferred2.promise);
|
|
fetch.onCall(2).returns(deferred3.promise);
|
|
fetch.onCall(3).returns(deferred4.promise);
|
|
|
|
fetchRetry = fetchBuilder(fetch);
|
|
});
|
|
|
|
describe('#input', function () {
|
|
|
|
var expectedUrl = 'http://some-url.com';
|
|
|
|
beforeEach(function () {
|
|
fetchRetry(expectedUrl);
|
|
});
|
|
|
|
it('passes #input to fetch', function () {
|
|
expect(fetch.getCall(0).args[0]).toBe(expectedUrl);
|
|
});
|
|
|
|
});
|
|
|
|
describe('#init', function () {
|
|
|
|
describe('when #init is provided', function () {
|
|
|
|
var init;
|
|
|
|
beforeEach(function () {
|
|
init = {
|
|
retries: 3,
|
|
whatever: 'something'
|
|
};
|
|
|
|
fetchRetry('http://someUrl', init);
|
|
});
|
|
|
|
it('passes init to fetch', function () {
|
|
expect(fetch.getCall(0).args[1]).toEqual(init);
|
|
});
|
|
|
|
describe('when #init.retryOn is not an array or function', () => {
|
|
|
|
it('throws exception', () => {
|
|
expect(function () {
|
|
init.retryOn = 503;
|
|
fetchRetry('http://someUrl', init);
|
|
}).toThrow({
|
|
name: 'ArgumentError',
|
|
message: 'retryOn property expects an array or function'
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init is undefined or null', function () {
|
|
|
|
[undefined, null].forEach(function (testCase) {
|
|
|
|
beforeEach(function () {
|
|
fetchRetry('http://someUrl', testCase);
|
|
});
|
|
|
|
it('does not pass through init to fetch', function () {
|
|
expect(fetch.getCall(0).args[1]).toEqual(undefined);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('#init.retries', function () {
|
|
|
|
describe('when #init.retries=3 (default)', function () {
|
|
|
|
beforeEach(function () {
|
|
thenCallback = sinon.spy();
|
|
catchCallback = sinon.spy();
|
|
|
|
fetchRetry('http://someurl')
|
|
.then(thenCallback)
|
|
.catch(catchCallback);
|
|
});
|
|
|
|
describe('when first call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.resolve({ status: 200 });
|
|
});
|
|
|
|
describe('when resolved', function () {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch once', function () {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when first call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.reject();
|
|
});
|
|
|
|
describe('when second call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
clock.tick(delay);
|
|
deferred2.resolve({ status: 200 });
|
|
});
|
|
|
|
describe('when resolved', function () {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch twice', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when second call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred2.reject();
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when third call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred3.resolve({ status: 200 });
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when resolved', function () {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch three times', function () {
|
|
expect(fetch.callCount).toBe(3);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when third call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred3.reject();
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when fourth call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred4.resolve({ status: 200 });
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when resolved', function () {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch four times', function () {
|
|
expect(fetch.callCount).toBe(4);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when fourth call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred4.reject();
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when rejected', function () {
|
|
|
|
it('invokes the catch callback', function () {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
|
|
it('does not call fetch again', function () {
|
|
expect(fetch.callCount).toBe(4);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #defaults.retries is not a a positive integer', () => {
|
|
['1', -1, 'not a number', null].forEach(invalidRetries => {
|
|
it('throws error', () => {
|
|
const expectedError = {
|
|
name: 'ArgumentError',
|
|
message: 'retries must be a positive integer'
|
|
};
|
|
expect(() => {
|
|
var fetchRetryWithDefaults = fetchBuilder(fetch, {retries: invalidRetries});
|
|
fetchRetryWithDefaults('http://someurl');
|
|
}).toThrow(expectedError);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when #defaults.retryDelay is not a a positive integer', () => {
|
|
|
|
['1', -1, 'not a number', null].forEach(invalidDelay => {
|
|
|
|
it('throws error', () => {
|
|
const expectedError = {
|
|
name: 'ArgumentError',
|
|
message: 'retryDelay must be a positive integer or a function returning a positive integer'
|
|
};
|
|
expect(() => {
|
|
var fetchRetryWithDefaults = fetchBuilder(fetch, { retryDelay: invalidDelay });
|
|
fetchRetryWithDefaults('http://someurl');
|
|
}).toThrow(expectedError);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #defaults.retryDelay is a function', function () {
|
|
|
|
var defaults;
|
|
var retryDelay;
|
|
|
|
beforeEach(function () {
|
|
retryDelay = sinon.stub().returns(5000);
|
|
defaults = {
|
|
retryDelay: retryDelay
|
|
};
|
|
|
|
thenCallback = sinon.spy();
|
|
|
|
var fetchRetryWithDefaults = fetchBuilder(fetch, defaults);
|
|
fetchRetryWithDefaults('http://someUrl')
|
|
.then(thenCallback);
|
|
});
|
|
});
|
|
|
|
describe('when #defaults.retryOn is not an array or function', function () {
|
|
|
|
var defaults = {};
|
|
|
|
describe('when #defaults.retryOn is not an array or function', () => {
|
|
|
|
it('throws exception', () => {
|
|
expect(function () {
|
|
defaults.retryOn = 503;
|
|
var fetchRetryWithDefaults = fetchBuilder(fetch, defaults);
|
|
fetchRetryWithDefaults('http://someUrl');
|
|
}).toThrow({
|
|
name: 'ArgumentError',
|
|
message: 'retryOn property expects an array or function'
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #defaults.retries=0', function () {
|
|
beforeEach(function () {
|
|
thenCallback = sinon.spy();
|
|
catchCallback = sinon.spy();
|
|
|
|
var fetchRetryWithDefaults = fetchBuilder(fetch, {retries: 0});
|
|
|
|
fetchRetryWithDefaults('http://someurl')
|
|
.then(thenCallback)
|
|
.catch(catchCallback);
|
|
});
|
|
|
|
describe('when first call is a failure', function () {
|
|
beforeEach(function () {
|
|
deferred1.reject();
|
|
});
|
|
|
|
describe('when rejected', function () {
|
|
|
|
it('invokes the catch callback', function () {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
|
|
it('does not call fetch again', function () {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when #init.retries=1', function () {
|
|
|
|
beforeEach(function () {
|
|
thenCallback = sinon.spy();
|
|
catchCallback = sinon.spy();
|
|
|
|
fetchRetry('http://someurl', { retries: 1 })
|
|
.then(thenCallback)
|
|
.catch(catchCallback);
|
|
});
|
|
|
|
describe('when first call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.resolve({ status: 200 });
|
|
});
|
|
|
|
describe('when resolved', function () {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch once', function () {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when first call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.reject();
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when second call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred2.resolve({ status: 200 });
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when resolved', function () {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch twice', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when second call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred2.reject();
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when rejected', function () {
|
|
|
|
it('invokes the catch callback', function () {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
|
|
it('does not call fetch again', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init.retries=0', function () {
|
|
|
|
beforeEach(function () {
|
|
thenCallback = sinon.spy();
|
|
catchCallback = sinon.spy();
|
|
|
|
fetchRetry('http://someurl', { retries: 0 })
|
|
.then(thenCallback)
|
|
.catch(catchCallback);
|
|
});
|
|
|
|
describe('when first call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.resolve({ status: 200 });
|
|
});
|
|
|
|
describe('when resolved', function () {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch once', function () {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when first call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.reject();
|
|
});
|
|
|
|
describe('when rejected', () => {
|
|
|
|
it('invokes the catch callback', function () {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init.retries is not a a positive integer', () => {
|
|
|
|
['1', -1, 'not a number', null].forEach(invalidRetries => {
|
|
|
|
it('throws error', () => {
|
|
const expectedError = {
|
|
name: 'ArgumentError',
|
|
message: 'retries must be a positive integer'
|
|
};
|
|
expect(() => {
|
|
fetchRetry('http://someurl', { retries: invalidRetries });
|
|
}).toThrow(expectedError);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('#init.retryDelay', function () {
|
|
|
|
describe('when #init.retryDelay is a number', function () {
|
|
|
|
var init;
|
|
var retryDelay;
|
|
|
|
beforeEach(function () {
|
|
retryDelay = 5000;
|
|
init = {
|
|
retryDelay: retryDelay
|
|
};
|
|
|
|
thenCallback = sinon.spy();
|
|
|
|
fetchRetry('http://someUrl', init)
|
|
.then(thenCallback);
|
|
});
|
|
|
|
describe('when first call is unsuccessful', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.reject();
|
|
});
|
|
|
|
describe('after specified time', function () {
|
|
|
|
beforeEach(function () {
|
|
clock.tick(retryDelay);
|
|
});
|
|
|
|
it('invokes fetch again', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
});
|
|
|
|
describe('after less than specified time', function () {
|
|
|
|
beforeEach(function () {
|
|
clock.tick(1000);
|
|
});
|
|
|
|
it('does not invoke fetch again', function () {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init.retryDelay is 0', function () {
|
|
|
|
var init;
|
|
var retryDelay;
|
|
|
|
beforeEach(function () {
|
|
retryDelay = 0;
|
|
init = {
|
|
retryDelay: retryDelay
|
|
};
|
|
|
|
thenCallback = sinon.spy();
|
|
|
|
fetchRetry('http://someUrl', init)
|
|
.then(thenCallback);
|
|
});
|
|
|
|
describe('when first call is unsuccessful', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.reject();
|
|
});
|
|
|
|
describe('after one event loop tick', function () {
|
|
|
|
beforeEach(function () {
|
|
clock.tick(0);
|
|
});
|
|
|
|
it('invokes fetch again', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init.retryDelay is not a a positive integer', () => {
|
|
|
|
['1', -1, 'not a number', null].forEach(invalidDelay => {
|
|
|
|
it('throws error', () => {
|
|
const expectedError = {
|
|
name: 'ArgumentError',
|
|
message: 'retryDelay must be a positive integer or a function returning a positive integer'
|
|
};
|
|
expect(() => {
|
|
fetchRetry('http://someurl', { retryDelay: invalidDelay });
|
|
}).toThrow(expectedError);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init.retryDelay is a function', function () {
|
|
|
|
var init;
|
|
var retryDelay;
|
|
|
|
beforeEach(function () {
|
|
retryDelay = sinon.stub().returns(5000);
|
|
init = {
|
|
retryDelay: retryDelay
|
|
};
|
|
|
|
thenCallback = sinon.spy();
|
|
|
|
fetchRetry('http://someUrl', init)
|
|
.then(thenCallback);
|
|
});
|
|
|
|
describe('when first call is unsuccessful', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred1.reject(new Error('first error'));
|
|
});
|
|
|
|
describe('when the second call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred2.resolve({ status: 200 });
|
|
clock.tick(5000);
|
|
});
|
|
|
|
it('invokes the retryDelay function', function () {
|
|
expect(retryDelay.called).toBe(true);
|
|
expect(retryDelay.lastCall.args[0]).toEqual(0);
|
|
expect(retryDelay.lastCall.args[1].message).toEqual('first error');
|
|
});
|
|
|
|
|
|
});
|
|
|
|
describe('when second call is a failure', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred2.reject(new Error('second error'));
|
|
clock.tick(5000);
|
|
});
|
|
|
|
describe('when the third call is a success', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred3.resolve({ status: 200 });
|
|
clock.tick(5000);
|
|
});
|
|
|
|
it('invokes the retryDelay function again', function () {
|
|
expect(retryDelay.callCount).toBe(2);
|
|
expect(retryDelay.lastCall.args[0]).toEqual(1);
|
|
expect(retryDelay.lastCall.args[1].message).toEqual('second error');
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('#init.retryOn', () => {
|
|
|
|
describe('when #init.retryOn is an array', () => {
|
|
|
|
var init;
|
|
var retryOn;
|
|
|
|
beforeEach(function () {
|
|
retryOn = [503, 404];
|
|
init = {
|
|
retryOn: retryOn
|
|
};
|
|
|
|
thenCallback = sinon.spy();
|
|
catchCallback = sinon.spy();
|
|
|
|
fetchRetry('http://someUrl', init)
|
|
.then(thenCallback)
|
|
.catch((catchCallback));
|
|
});
|
|
|
|
describe('when first fetch is resolved with status code specified in retryOn array', () => {
|
|
|
|
beforeEach(() => {
|
|
deferred1.resolve({ status: 503 });
|
|
});
|
|
|
|
describe('after specified delay', () => {
|
|
|
|
beforeEach(() => {
|
|
clock.tick(delay);
|
|
});
|
|
|
|
it('retries fetch', () => {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
describe('when second fetch resolves with a different status code', () => {
|
|
|
|
beforeEach(() => {
|
|
deferred2.resolve({ status: 200 });
|
|
});
|
|
|
|
describe('when resolved', () => {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('has called fetch twice', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init.retryOn is a function', function () {
|
|
|
|
var init;
|
|
var retryOn;
|
|
var fetchRetryChain;
|
|
|
|
beforeEach(function () {
|
|
retryOn = sinon.stub();
|
|
init = {
|
|
retryOn: retryOn
|
|
};
|
|
|
|
thenCallback = sinon.spy();
|
|
catchCallback = sinon.spy();
|
|
|
|
fetchRetryChain = fetchRetry('http://someUrl', init)
|
|
.then(thenCallback)
|
|
.catch((catchCallback));
|
|
});
|
|
|
|
describe('when first attempt is rejected due to network error', function () {
|
|
|
|
describe('when #retryOn() returns true', () => {
|
|
|
|
beforeEach(function () {
|
|
retryOn.returns(true);
|
|
deferred1.reject(new Error('first error'));
|
|
});
|
|
|
|
describe('when rejected', function () {
|
|
|
|
it('invokes #retryOn function with an error', function () {
|
|
expect(retryOn.called).toBe(true);
|
|
expect(retryOn.lastCall.args.length).toBe(3);
|
|
expect(retryOn.lastCall.args[0]).toBe(0);
|
|
expect(retryOn.lastCall.args[1] instanceof Error).toBe(true);
|
|
expect(retryOn.lastCall.args[2]).toBe(null);
|
|
});
|
|
|
|
describe('after specified time', function () {
|
|
|
|
beforeEach(function () {
|
|
clock.tick(delay);
|
|
});
|
|
|
|
it('invokes fetch again', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
describe('when the second call is unsuccessful', function () {
|
|
|
|
beforeEach(function () {
|
|
deferred2.reject(new Error('second error'));
|
|
clock.tick(delay);
|
|
});
|
|
|
|
describe('when rejected', function () {
|
|
|
|
it('invokes the #retryOn function twice', function () {
|
|
expect(retryOn.callCount).toBe(2);
|
|
expect(retryOn.lastCall.args[0]).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #retryOn() returns false', () => {
|
|
|
|
beforeEach(function () {
|
|
retryOn.returns(false);
|
|
deferred1.reject(new Error('first error'));
|
|
});
|
|
|
|
describe('when rejected', function () {
|
|
|
|
it('invokes #retryOn function with an error', function () {
|
|
expect(retryOn.called).toBe(true);
|
|
expect(retryOn.lastCall.args.length).toBe(3);
|
|
expect(retryOn.lastCall.args[0]).toBe(0);
|
|
expect(retryOn.lastCall.args[1] instanceof Error).toBe(true);
|
|
expect(retryOn.lastCall.args[2]).toBe(null);
|
|
});
|
|
|
|
describe('after specified time', function () {
|
|
|
|
beforeEach(function () {
|
|
clock.tick(delay);
|
|
});
|
|
|
|
it('invokes the catch callback', function () {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
|
|
it('does not call fetch again', function () {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when first attempt is resolved', function () {
|
|
|
|
describe('when #retryOn() returns true', () => {
|
|
|
|
beforeEach(function () {
|
|
retryOn.returns(true);
|
|
deferred1.resolve({ status: 200 });
|
|
});
|
|
|
|
describe('after specified delay', () => {
|
|
|
|
beforeEach(function () {
|
|
clock.tick(delay);
|
|
});
|
|
|
|
it('calls fetch again', function () {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
describe('when second call is resolved', () => {
|
|
|
|
beforeEach(function () {
|
|
deferred2.resolve({ status: 200 });
|
|
clock.tick(delay);
|
|
});
|
|
|
|
it('invokes the #retryOn function with the response', function () {
|
|
expect(retryOn.called).toBe(true);
|
|
expect(retryOn.lastCall.args.length).toBe(3);
|
|
expect(retryOn.lastCall.args[0]).toBe(0);
|
|
expect(retryOn.lastCall.args[1]).toBe(null);
|
|
expect(retryOn.lastCall.args[2]).toEqual({ status: 200 });
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #retryOn() returns false', () => {
|
|
|
|
beforeEach(function () {
|
|
retryOn.returns(false);
|
|
deferred1.resolve({ status: 502 });
|
|
});
|
|
|
|
describe('when resolved', () => {
|
|
|
|
it('invokes the then callback', function () {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch 1 time only', function () {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when first attempt is resolved with Promise', function() {
|
|
|
|
describe('when #retryOn() returns Promise with true resolve', () => {
|
|
|
|
beforeEach(function() {
|
|
retryOn.resolves(true);
|
|
deferred1.resolve({ status: 200 });
|
|
});
|
|
|
|
describe('after specified delay', () => {
|
|
|
|
beforeEach(function() {
|
|
clock.tick(delay);
|
|
});
|
|
|
|
it('calls fetch again', function() {
|
|
expect(fetch.callCount).toBe(2);
|
|
});
|
|
|
|
describe('when second call is resolved', () => {
|
|
|
|
beforeEach(function() {
|
|
deferred2.resolve({ status: 200 });
|
|
clock.tick(delay);
|
|
});
|
|
|
|
it('invokes the #retryOn function with the response', function() {
|
|
expect(retryOn.called).toBe(true);
|
|
expect(retryOn.lastCall.args.length).toBe(3);
|
|
expect(retryOn.lastCall.args[0]).toBe(0);
|
|
expect(retryOn.lastCall.args[1]).toBe(null);
|
|
expect(retryOn.lastCall.args[2]).toEqual({ status: 200 });
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #retryOn() returns Promise with false resolve', () => {
|
|
|
|
beforeEach(function() {
|
|
retryOn.resolves(false);
|
|
deferred1.resolve({ status: 502 });
|
|
});
|
|
|
|
describe('when resolved', () => {
|
|
|
|
it('invokes the then callback', function() {
|
|
expect(thenCallback.called).toBe(true);
|
|
});
|
|
|
|
it('calls fetch 1 time only', function() {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #retryOn() throws an error', () => {
|
|
|
|
beforeEach(function() {
|
|
retryOn.throws();
|
|
});
|
|
|
|
describe('when rejected', () => {
|
|
|
|
beforeEach(function() {
|
|
deferred1.reject();
|
|
});
|
|
|
|
it('retryOn called only once', () => {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(retryOn.callCount).toBe(1);
|
|
});
|
|
});
|
|
|
|
it('invokes the catch callback', function() {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('called fetch', function() {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
describe('when resolved', () => {
|
|
|
|
beforeEach(function() {
|
|
deferred1.resolve({ status: 200 });
|
|
});
|
|
|
|
it('retryOn called only once', () => {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(retryOn.callCount).toBe(1);
|
|
});
|
|
});
|
|
|
|
it('invokes the catch callback', function() {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('called fetch', function() {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #retryOn() returns a Promise that rejects', () => {
|
|
|
|
beforeEach(function() {
|
|
retryOn.rejects();
|
|
});
|
|
|
|
describe('when rejected', () => {
|
|
|
|
beforeEach(function() {
|
|
deferred1.reject();
|
|
});
|
|
|
|
it('retryOn called only once', () => {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(retryOn.callCount).toBe(1);
|
|
});
|
|
});
|
|
|
|
it('invokes the catch callback', function() {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('called fetch', function() {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
describe('when resolved', () => {
|
|
|
|
beforeEach(function() {
|
|
deferred1.resolve({ status: 200 });
|
|
});
|
|
|
|
it('retryOn called only once', () => {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(retryOn.callCount).toBe(1);
|
|
});
|
|
});
|
|
|
|
it('invokes the catch callback', function() {
|
|
return fetchRetryChain.finally(() => {
|
|
expect(catchCallback.called).toBe(true);
|
|
});
|
|
});
|
|
|
|
it('called fetch', function() {
|
|
expect(fetch.callCount).toBe(1);
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
describe('when #init.retryOn is not an array or function', function () {
|
|
|
|
var init;
|
|
|
|
describe('when #init.retryOn is not an array or function', () => {
|
|
|
|
it('throws exception', () => {
|
|
expect(function () {
|
|
init.retryOn = 503;
|
|
fetchRetry('http://someUrl', init);
|
|
}).toThrow({
|
|
name: 'ArgumentError',
|
|
message: 'retryOn property expects an array or function'
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
function defer() {
|
|
var resolve, reject;
|
|
// eslint-disable-next-line no-undef
|
|
var promise = new Promise(function () {
|
|
resolve = arguments[0];
|
|
reject = arguments[1];
|
|
});
|
|
return {
|
|
resolve: resolve,
|
|
reject: reject,
|
|
promise: promise
|
|
};
|
|
}
|