pendings
Version:
Better control of promises
378 lines (322 loc) • 12.2 kB
JavaScript
'use strict';
const Pending = require(`${srcPath}/pending`);
const noop = () => {};
describe('pending', function () {
beforeEach(function () {
this.pending = new Pending();
});
describe('call', function () {
it('should return Promise', function () {
const res = this.pending.call(noop);
assert.instanceOf(res, Promise);
});
it('should store Promise', function () {
const res = this.pending.call(noop);
assert.instanceOf(this.pending.promise, Promise);
assert.equal(res, this.pending.promise);
});
it('should call passed fn', function () {
let a = 0;
this.pending.call(() => a++);
assert.equal(a, 1);
});
it('should return the same promise for second call if previous was not fulfilled', function () {
const p1 = this.pending.call(noop);
const p2 = this.pending.call(noop);
assert.equal(p1, p2);
});
it('should return the same promise for second call if previous was fulfilled', function () {
const p1 = this.pending.call(noop);
this.pending.resolve();
const p2 = this.pending.call(noop);
assert.equal(p1, p2);
});
it('should return new promise for second call after resolve + reset', function () {
const p1 = this.pending.call(noop);
this.pending.resolve();
this.pending.reset();
const p2 = this.pending.call(noop);
assert.notEqual(p1, p2);
});
it('should return new promise for second call after reject + reset', function () {
const p1 = this.pending.call(noop);
this.pending.reject();
this.pending.reset();
const p2 = this.pending.call(noop);
assert.notEqual(p1, p2);
});
it('should allow to call without fn', function () {
const res = this.pending.call();
this.pending.resolve('foo');
return assert.eventually.equal(res, 'foo');
});
it('should proxy resolve if fn returns promise', function () {
const res = this.pending.call(() => Promise.resolve('foo'));
return assert.eventually.equal(res, 'foo');
});
it('should proxy reject if fn returns promise', function () {
const res = this.pending.call(() => Promise.reject(new Error('err')));
return assert.isRejected(res, 'err');
});
});
describe('resolve', function () {
it('should resolve directly', function () {
const res = this.pending.call(noop);
this.pending.resolve('foo');
return assert.eventually.equal(res, 'foo');
});
it('should resolve inside fn', function () {
const res = this.pending.call(() => this.pending.resolve('foo'));
return assert.eventually.equal(res, 'foo');
});
it('should keep first value if resolved twice', function () {
const res = this.pending.call(noop);
this.pending.resolve('foo');
this.pending.resolve('bar');
return assert.eventually.equal(res, 'foo');
});
it('should do nothing for resolve without call', function () {
assert.doesNotThrow(() => this.pending.resolve('foo'));
});
});
describe('reject', function () {
it('should reject in case of error in fn', function () {
const res = this.pending.call(() => {
throw new Error('err');
});
return assert.isRejected(res, 'err');
});
it('should reject directly', function () {
const res = this.pending.call(noop);
this.pending.reject(new Error('err'));
return assert.isRejected(res, 'err');
});
it('should reject inside fn', function () {
const res = this.pending.call(() => this.pending.reject(new Error('err')));
return assert.isRejected(res, 'err');
});
it('should keep first value if rejected twice', function () {
const res = this.pending.call(noop);
this.pending.reject(new Error('foo'));
this.pending.reject(new Error('bar'));
return assert.isRejected(res, 'foo');
});
it('should do nothing for reject without call', function () {
assert.doesNotThrow(() => this.pending.reject('foo'));
});
});
describe('fulfill', function () {
it('should resolve', function () {
const res = this.pending.call(noop);
this.pending.fulfill('foo');
return assert.eventually.equal(res, 'foo');
});
it('should reject with error', function () {
const res = this.pending.call(noop);
this.pending.fulfill('foo', new Error('err'));
return assert.isRejected(res, 'err');
});
it('should keep first value if fulfilled twice', function () {
const res = this.pending.call(noop);
this.pending.fulfill('foo');
this.pending.fulfill('bar', new Error('err'));
return assert.eventually.equal(res, 'foo');
});
});
describe('isPending', function () {
it('should be false until call', function () {
assert.notOk(this.pending.isPending);
});
it('should be true after call', function () {
this.pending.call();
assert.ok(this.pending.isPending);
});
it('should be false after resolve', function () {
this.pending.call();
this.pending.resolve('foo');
assert.notOk(this.pending.isPending);
});
it('should be false after reject', function () {
this.pending.call().catch(() => {});
this.pending.reject('foo');
assert.notOk(this.pending.isPending);
});
});
describe('isFulfilled', function () {
it('should set after resolve', function () {
assert.notOk(this.pending.isFulfilled);
this.pending.call();
assert.notOk(this.pending.isFulfilled);
this.pending.resolve('foo');
assert.ok(this.pending.isFulfilled);
});
it('should set after manual reject', function () {
assert.notOk(this.pending.isFulfilled);
const res = this.pending.call();
assert.notOk(this.pending.isFulfilled);
this.pending.reject('foo');
assert.ok(this.pending.isFulfilled);
return assert.isRejected(res, 'foo');
});
it('should set after reject by error in fn', function () {
assert.notOk(this.pending.isFulfilled);
const res = this.pending.call(() => {
throw new Error('err');
});
assert.ok(this.pending.isFulfilled);
return assert.isRejected(res, 'err');
});
});
describe('onFulfilled', function () {
it('should call after resolve', function () {
let a = 0;
this.pending.onFulfilled = () => a++;
this.pending.call();
this.pending.resolve('foo');
assert.equal(a, 1);
});
it('should set after reject', function () {
let a = 0;
this.pending.onFulfilled = () => a++;
this.pending.call().catch(noop);
this.pending.reject('foo');
assert.equal(a, 1);
});
it('should set after reject (by error in fn)', function () {
let a = 0;
this.pending.onFulfilled = () => a++;
this.pending.call(() => { throw new Error('err'); }).catch(noop);
assert.equal(a, 1);
});
});
describe('reset', function () {
it('should reset resolved promise', function () {
this.pending.call();
this.pending.resolve();
this.pending.reset();
assert.equal(this.pending.isResolved, false);
});
it('should reject pending promise', function () {
const res = this.pending.call(noop);
this.pending.reset();
return assert.isRejected(res, 'Promise rejected by reset');
});
it('should reject pending promise with custom message', function () {
const res = this.pending.call(noop);
this.pending.reset(new Error('err'));
return assert.isRejected(res, 'err');
});
});
describe('value', function () {
it('should return resolved value', function () {
this.pending.call();
this.pending.resolve('foo');
assert.equal(this.pending.value, 'foo');
});
it('should return rejected value', function () {
this.pending.call().catch(() => {});
this.pending.reject('err');
assert.equal(this.pending.value, 'err');
});
});
describe('constructor options: timeout', function () {
it('should resolve before timeout', function () {
this.pending = new Pending({timeout: 10});
const res = this.pending.call(noop);
setTimeout(() => this.pending.resolve('foo'), 5);
return assert.eventually.equal(res, 'foo');
});
it('should reject after timeout', function () {
this.pending = new Pending({timeout: 10});
const res = this.pending.call(noop);
setTimeout(() => this.pending.resolve('foo'), 20);
return assert.isRejected(res, 'Promise rejected by timeout (10 ms)');
});
});
describe('call options: timeout', function () {
it('should resolve before timeout', function () {
const res = this.pending.call(noop, {timeout: 10});
setTimeout(() => this.pending.resolve('foo'), 5);
return assert.eventually.equal(res, 'foo');
});
it('should reject after timeout', function () {
const res = this.pending.call(noop, {timeout: 10});
setTimeout(() => this.pending.resolve('foo'), 20);
return assert.isRejected(res, 'Promise rejected by timeout (10 ms)');
});
it('should overwrite default timeout', function () {
this.pending = new Pending({timeout: 10});
const res = this.pending.call(noop, {timeout: 20});
setTimeout(() => this.pending.resolve('foo'), 15);
return assert.eventually.equal(res, 'foo');
});
it('should not overwrite default timeout with undefined', function () {
this.pending = new Pending({timeout: 10});
const res = this.pending.call(noop, {timeout: undefined});
setTimeout(() => this.pending.resolve('foo'), 5);
return assert.eventually.equal(res, 'foo');
});
});
describe('constructor options: autoReset', function () {
describe('never', function () {
it('should keep promise on next calls after resolve', function () {
this.pending = new Pending({autoReset: 'never'});
const p = this.pending.call();
this.pending.resolve();
assert.equal(p, this.pending.call());
});
it('should keep promise on next calls after reject', function () {
this.pending = new Pending({autoReset: 'never'});
const p = this.pending.call();
p.catch(() => {});
this.pending.reject();
assert.equal(p, this.pending.call());
});
});
describe('resolved', function () {
it('should not keep promise on next calls after resolve', function () {
this.pending = new Pending({autoReset: 'resolved'});
const p = this.pending.call();
this.pending.resolve();
assert.notEqual(p, this.pending.call());
});
it('should keep promise on next calls after reject', function () {
this.pending = new Pending({autoReset: 'resolved'});
const p = this.pending.call();
p.catch(() => {});
this.pending.reject();
assert.equal(p, this.pending.call());
});
});
describe('rejected', function () {
it('should keep promise on next calls after resolve', function () {
this.pending = new Pending({autoReset: 'rejected'});
const p = this.pending.call();
this.pending.resolve();
assert.equal(p, this.pending.call());
});
it('should not keep promise on next calls after reject', function () {
this.pending = new Pending({autoReset: 'rejected'});
const p = this.pending.call();
p.catch(() => {});
this.pending.reject();
assert.notEqual(p, this.pending.call());
});
});
describe('fulfilled', function () {
it('should not keep promise on next calls after resolve', function () {
this.pending = new Pending({autoReset: 'fulfilled'});
const p = this.pending.call();
this.pending.resolve();
assert.notEqual(p, this.pending.call());
});
it('should not keep promise on next calls after reject', function () {
this.pending = new Pending({autoReset: 'fulfilled'});
const p = this.pending.call();
p.catch(() => {});
this.pending.reject();
assert.notEqual(p, this.pending.call());
});
});
});
});