Do not rely on native setTimeout in the promise service tests. (#19891)

This commit is contained in:
Aleh Zasypkin 2018-06-15 11:37:26 +02:00 committed by GitHub
parent 9dc9644e95
commit 8c2a8d25c1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -21,155 +21,234 @@ import expect from 'expect.js';
import ngMock from 'ng_mock';
import sinon from 'sinon';
describe('Promise service', function () {
describe('Promise service', () => {
let Promise;
let $rootScope;
const sandbox = sinon.createSandbox();
function tick(ms = 0) {
sandbox.clock.tick(ms);
// Ugly, but necessary for promises to resolve: https://github.com/angular/angular.js/issues/12555
$rootScope.$apply();
}
beforeEach(ngMock.module('kibana'));
beforeEach(ngMock.inject(function ($injector) {
beforeEach(ngMock.inject(($injector) => {
sandbox.useFakeTimers();
Promise = $injector.get('Promise');
$rootScope = $injector.get('$rootScope');
}));
describe('Constructor', function () {
it('provides resolve and reject function', function () {
new Promise(function (resolve, reject) {
expect(resolve).to.be.a('function');
expect(reject).to.be.a('function');
expect(arguments).to.have.length(2);
});
afterEach(() => sandbox.restore());
describe('Constructor', () => {
it('provides resolve and reject function', () => {
const executor = sinon.stub();
new Promise(executor);
sinon.assert.calledOnce(executor);
sinon.assert.calledWithExactly(executor, sinon.match.func, sinon.match.func);
});
});
it('Promise.resolve', function (done) {
Promise.resolve(true).then(() => { done(); });
// Ugly, but necessary for promises to resolve: https://github.com/angular/angular.js/issues/12555
$rootScope.$apply();
it('Promise.resolve', () => {
const onResolve = sinon.stub();
Promise.resolve(true).then(onResolve);
tick();
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, true);
});
describe('Promise.fromNode', function () {
it('creates a callback that controls a promise', function () {
let callback;
Promise.fromNode(cb => (callback = cb)());
$rootScope.$apply();
expect(callback).to.be.a('function');
describe('Promise.fromNode', () => {
it('creates a callback that controls a promise', () => {
const callback = sinon.stub();
Promise.fromNode(callback);
tick();
sinon.assert.calledOnce(callback);
sinon.assert.calledWithExactly(callback, sinon.match.func);
});
it('rejects if the callback receives an error', function () {
const errback = sinon.stub();
it('rejects if the callback receives an error', () => {
const err = new Error();
Promise.fromNode(cb => cb(err)).catch(errback);
$rootScope.$apply();
const onReject = sinon.stub();
Promise.fromNode(sinon.stub().yields(err)).catch(onReject);
expect(errback.callCount).to.be(1);
expect(errback.getCall(0).args[0]).to.be(err);
tick();
sinon.assert.calledOnce(onReject);
sinon.assert.calledWithExactly(onReject, sinon.match.same(err));
});
it('resolves with the second argument', function () {
const thenback = sinon.stub();
it('resolves with the second argument', () => {
const result = {};
Promise.fromNode(cb => cb(null, result)).then(thenback);
$rootScope.$apply();
const onResolve = sinon.stub();
Promise.fromNode(sinon.stub().yields(null, result)).then(onResolve);
expect(thenback.callCount).to.be(1);
expect(thenback.getCall(0).args[0]).to.be(result);
tick();
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, sinon.match.same(result));
});
it('resolves with an array if multiple arguments are received', function () {
const thenback = sinon.stub();
it('resolves with an array if multiple arguments are received', () => {
const result1 = {};
const result2 = {};
Promise.fromNode(cb => cb(null, result1, result2)).then(thenback);
$rootScope.$apply();
const onResolve = sinon.stub();
Promise.fromNode(sinon.stub().yields(null, result1, result2)).then(onResolve);
expect(thenback.callCount).to.be(1);
expect(thenback.getCall(0).args[0][0]).to.be(result1);
expect(thenback.getCall(0).args[0][1]).to.be(result2);
tick();
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(
onResolve,
[sinon.match.same(result1), sinon.match.same(result2)]
);
});
it('resolves with an array if multiple undefined are received', function () {
const thenback = sinon.stub();
Promise.fromNode(cb => cb(null, undefined, undefined)).then(thenback);
$rootScope.$apply();
it('resolves with an array if multiple undefined are received', () => {
const onResolve = sinon.stub();
Promise.fromNode(sinon.stub().yields(null, undefined, undefined)).then(onResolve);
expect(thenback.callCount).to.be(1);
expect(thenback.getCall(0).args[0][0]).to.be(undefined);
expect(thenback.getCall(0).args[0][1]).to.be(undefined);
tick();
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, [undefined, undefined]);
});
});
describe('Promise.race()', () => {
let crankTimeout;
beforeEach(() => {
// constantly call $rootScope.$apply() in a loop so we can
// pretend that these are real promises
(function crank$apply() {
$rootScope.$apply();
crankTimeout = setTimeout(crank$apply, 1);
}());
});
afterEach(() => {
clearTimeout(crankTimeout);
});
it(`resolves with the first resolved promise's value`, async () => {
it(`resolves with the first resolved promise's value`, () => {
const p1 = new Promise(resolve => setTimeout(resolve, 100, 1));
const p2 = new Promise(resolve => setTimeout(resolve, 200, 2));
expect(await Promise.race([p1, p2])).to.be(1);
const onResolve = sinon.stub();
Promise.race([p1, p2]).then(onResolve);
tick(200);
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, 1);
});
it(`rejects with the first rejected promise's rejection reason`, async () => {
const p1 = new Promise((r, reject) => setTimeout(reject, 200, new Error(1)));
const p2 = new Promise((r, reject) => setTimeout(reject, 100, new Error(2)));
expect(await Promise.race([p1, p2]).catch(e => e.message)).to.be('2');
it(`rejects with the first rejected promise's rejection reason`, () => {
const p1Error = new Error('1');
const p1 = new Promise((r, reject) => setTimeout(reject, 200, p1Error));
const p2Error = new Error('2');
const p2 = new Promise((r, reject) => setTimeout(reject, 100, p2Error));
const onReject = sinon.stub();
Promise.race([p1, p2]).catch(onReject);
tick(200);
sinon.assert.calledOnce(onReject);
sinon.assert.calledWithExactly(onReject, sinon.match.same(p2Error));
});
it('does not wait for subsequent promises to resolve/reject', async () => {
const start = Date.now();
const p1 = new Promise(resolve => setTimeout(resolve, 100));
const p2 = new Promise(resolve => setTimeout(resolve, 5000));
await Promise.race([p1, p2]);
const time = Date.now() - start;
expect(time).to.not.be.lessThan(100);
expect(time).to.not.be.greaterThan(2000);
it('does not wait for subsequent promises to resolve/reject', () => {
const onP1Resolve = sinon.stub();
const p1 = new Promise(resolve => setTimeout(resolve, 100)).then(onP1Resolve);
const onP2Resolve = sinon.stub();
const p2 = new Promise(resolve => setTimeout(resolve, 101)).then(onP2Resolve);
const onResolve = sinon.stub();
Promise.race([p1, p2]).then(onResolve);
tick(100);
sinon.assert.calledOnce(onResolve);
sinon.assert.calledOnce(onP1Resolve);
sinon.assert.callOrder(onP1Resolve, onResolve);
sinon.assert.notCalled(onP2Resolve);
});
it('allows non-promises in the array', async () => {
expect(await Promise.race([1, 2, 3])).to.be(1);
it('allows non-promises in the array', () => {
const onResolve = sinon.stub();
Promise.race([1, 2, 3]).then(onResolve);
tick();
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, 1);
});
describe('argument is undefined', () => {
it('rejects the promise', async () => {
it('rejects the promise', () => {
const football = {};
expect(await Promise.race().catch(() => football)).to.be(football);
const onReject = sinon.stub();
Promise.race().catch(() => football).then(onReject);
tick();
sinon.assert.calledOnce(onReject);
sinon.assert.calledWithExactly(onReject, sinon.match.same(football));
});
});
describe('argument is a string', () => {
it(`resolves with the first character`, async () => {
expect(await Promise.race('abc')).to.be('a');
it(`resolves with the first character`, () => {
const onResolve = sinon.stub();
Promise.race('abc').then(onResolve);
tick();
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, 'a');
});
});
describe('argument is a non-iterable object', () => {
it('reject the promise', async () => {
it('reject the promise', () => {
const football = {};
expect(await Promise.race({}).catch(() => football)).to.be(football);
const onReject = sinon.stub();
Promise.race({}).catch(() => football).then(onReject);
tick();
sinon.assert.calledOnce(onReject);
sinon.assert.calledWithExactly(onReject, sinon.match.same(football));
});
});
describe('argument is a generator', () => {
it('resolves with the first resolved value', async () => {
it('resolves with the first resolved value', () => {
function *gen() {
yield new Promise(resolve => setTimeout(resolve, 100, 1));
yield new Promise(resolve => setTimeout(resolve, 200, 2));
}
expect(await Promise.race(gen())).to.be(1);
const onResolve = sinon.stub();
Promise.race(gen()).then(onResolve);
tick(200);
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, 1);
});
it('resolves with the first non-promise value', async () => {
it('resolves with the first non-promise value', () => {
function *gen() {
yield 1;
yield new Promise(resolve => setTimeout(resolve, 200, 2));
}
expect(await Promise.race(gen())).to.be(1);
const onResolve = sinon.stub();
Promise.race(gen()).then(onResolve);
tick(200);
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, 1);
});
it('iterates all values from the generator, even if one is already "resolved"', async () => {
it('iterates all values from the generator, even if one is already "resolved"', () => {
let yieldCount = 0;
function *gen() {
yieldCount += 1;
@ -178,7 +257,13 @@ describe('Promise service', function () {
yield new Promise(resolve => setTimeout(resolve, 200, 2));
}
expect(await Promise.race(gen())).to.be(1);
const onResolve = sinon.stub();
Promise.race(gen()).then(onResolve);
tick(200);
sinon.assert.calledOnce(onResolve);
sinon.assert.calledWithExactly(onResolve, 1);
expect(yieldCount).to.be(2);
});
});