mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Do not rely on native setTimeout
in the promise service tests. (#19891)
This commit is contained in:
parent
9dc9644e95
commit
8c2a8d25c1
1 changed files with 172 additions and 87 deletions
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue