2022-08-18 21:08:39 +00:00
|
|
|
import {delay} from 'app/common/delay';
|
|
|
|
import * as gutil from 'app/common/gutil';
|
|
|
|
import {assert} from 'chai';
|
|
|
|
import {Observable} from 'grainjs';
|
|
|
|
import * as ko from 'knockout';
|
|
|
|
import * as sinon from 'sinon';
|
|
|
|
|
|
|
|
describe('gutil2', function() {
|
|
|
|
describe('waitObs', function() {
|
|
|
|
it('should resolve promise when predicate matches', async function() {
|
|
|
|
const obs: ko.Observable<number|null> = ko.observable<number|null>(null);
|
|
|
|
const promise1 = gutil.waitObs(obs, (val) => Boolean(val));
|
|
|
|
const promise2 = gutil.waitObs(obs, (val) => (val === null));
|
|
|
|
const promise3 = gutil.waitObs(obs, (val) => (val! > 20));
|
|
|
|
const spy1 = sinon.spy(), spy2 = sinon.spy(), spy3 = sinon.spy();
|
|
|
|
const done = Promise.all([
|
|
|
|
promise1.then((val) => { spy1(); assert.strictEqual(val, 17); }),
|
|
|
|
promise2.then((val) => { spy2(); assert.strictEqual(val, null); }),
|
|
|
|
promise3.then((val) => { spy3(); assert.strictEqual(val, 30); }),
|
|
|
|
]);
|
|
|
|
|
|
|
|
await delay(1);
|
|
|
|
obs(17);
|
|
|
|
await delay(1);
|
|
|
|
obs(30);
|
|
|
|
await delay(1);
|
|
|
|
|
|
|
|
await done;
|
|
|
|
sinon.assert.callOrder(spy2, spy1, spy3);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('waitGrainObs', function() {
|
|
|
|
it('should resolve promise when predicate matches', async function() {
|
|
|
|
const obs = Observable.create<number|null>(null, null);
|
|
|
|
const promise1 = gutil.waitGrainObs(obs, (val) => Boolean(val));
|
|
|
|
const promise2 = gutil.waitGrainObs(obs, (val) => (val === null));
|
|
|
|
const promise3 = gutil.waitGrainObs(obs, (val) => (val! > 20));
|
|
|
|
const spy1 = sinon.spy(), spy2 = sinon.spy(), spy3 = sinon.spy();
|
|
|
|
const done = Promise.all([
|
|
|
|
promise1.then((val) => { spy1(); assert.strictEqual(val, 17); }),
|
|
|
|
promise2.then((val) => { spy2(); assert.strictEqual(val, null); }),
|
|
|
|
promise3.then((val) => { spy3(); assert.strictEqual(val, 30); }),
|
|
|
|
]);
|
|
|
|
|
|
|
|
await delay(1);
|
|
|
|
obs.set(17);
|
|
|
|
await delay(1);
|
|
|
|
obs.set(30);
|
|
|
|
await delay(1);
|
|
|
|
|
|
|
|
await done;
|
|
|
|
sinon.assert.callOrder(spy2, spy1, spy3);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('PromiseChain', function() {
|
|
|
|
it('should resolve promises in order', async function() {
|
|
|
|
const chain = new gutil.PromiseChain();
|
|
|
|
|
|
|
|
const spy1 = sinon.spy(), spy2 = sinon.spy(), spy3 = sinon.spy();
|
|
|
|
const done = Promise.all([
|
|
|
|
chain.add(() => delay(30).then(spy1).then(() => 1)),
|
|
|
|
chain.add(() => delay(20).then(spy2).then(() => 2)),
|
|
|
|
chain.add(() => delay(10).then(spy3).then(() => 3)),
|
|
|
|
]);
|
|
|
|
assert.deepEqual(await done, [1, 2, 3]);
|
|
|
|
sinon.assert.callOrder(spy1, spy2, spy3);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should skip pending callbacks, but not new callbacks, on error', async function() {
|
|
|
|
const chain = new gutil.PromiseChain();
|
|
|
|
|
|
|
|
const spy1 = sinon.spy(), spy2 = sinon.spy(), spy3 = sinon.spy();
|
|
|
|
let res1: any, res2: any, res3: any;
|
|
|
|
await assert.isRejected(Promise.all([
|
|
|
|
res1 = chain.add(() => delay(30).then(spy1).then(() => { throw new Error('Err1'); })),
|
|
|
|
res2 = chain.add(() => delay(20).then(spy2)),
|
|
|
|
res3 = chain.add(() => delay(10).then(spy3)),
|
|
|
|
]), /Err1/);
|
|
|
|
|
|
|
|
// Check that already-scheduled callbacks did not get called.
|
|
|
|
sinon.assert.calledOnce(spy1);
|
|
|
|
sinon.assert.notCalled(spy2);
|
|
|
|
sinon.assert.notCalled(spy3);
|
|
|
|
spy1.resetHistory();
|
|
|
|
|
|
|
|
// Ensure skipped add() calls return a rejection.
|
|
|
|
await assert.isRejected(res1, /^Err1/);
|
|
|
|
await assert.isRejected(res2, /^Skipped due to an earlier error/);
|
|
|
|
await assert.isRejected(res3, /^Skipped due to an earlier error/);
|
|
|
|
|
|
|
|
// New promises do get scheduled.
|
|
|
|
await assert.isRejected(Promise.all([
|
|
|
|
res1 = chain.add(() => delay(1).then(spy1).then(() => 17)),
|
|
|
|
res2 = chain.add(() => delay(1).then(spy2).then(() => { throw new Error('Err2'); })),
|
|
|
|
res3 = chain.add(() => delay(1).then(spy3)),
|
|
|
|
]), /Err2/);
|
|
|
|
sinon.assert.callOrder(spy1, spy2);
|
|
|
|
sinon.assert.notCalled(spy3);
|
|
|
|
|
|
|
|
// Check the return values of add() calls.
|
|
|
|
assert.strictEqual(await res1, 17);
|
|
|
|
await assert.isRejected(res2, /^Err2/);
|
|
|
|
await assert.isRejected(res3, /^Skipped due to an earlier error/);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("isLongerThan", function() {
|
|
|
|
it('should work correctly', async function() {
|
|
|
|
assert.equal(await gutil.isLongerThan(delay(200), 100), true);
|
|
|
|
assert.equal(await gutil.isLongerThan(delay(10), 100), false);
|
|
|
|
|
|
|
|
// A promise that throws before the timeout, causes the returned promise to resolve to false.
|
|
|
|
const errorObj = {};
|
|
|
|
let promise = delay(10).then(() => { throw errorObj; });
|
|
|
|
assert.equal(await gutil.isLongerThan(promise, 100), false);
|
|
|
|
await assert.isRejected(promise);
|
|
|
|
|
|
|
|
// A promise that throws after the timeout, causes the returned promise to resolve to true.
|
|
|
|
promise = delay(200).then(() => { throw errorObj; });
|
|
|
|
assert.equal(await gutil.isLongerThan(promise, 100), true);
|
|
|
|
await assert.isRejected(promise);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2023-09-13 05:58:05 +00:00
|
|
|
describe("timeoutReached", function() {
|
|
|
|
const DELAY_1 = 20;
|
|
|
|
const DELAY_2 = 2 * DELAY_1;
|
|
|
|
it("should return true for timed out promise", async function() {
|
|
|
|
assert.isTrue(await gutil.timeoutReached(DELAY_1, delay(DELAY_2)));
|
|
|
|
assert.isTrue(await gutil.timeoutReached(DELAY_1, delay(DELAY_2).then(() => { throw new Error("test error"); })));
|
|
|
|
});
|
|
|
|
|
|
|
|
it("should return false for promise that completes before timeout", async function() {
|
|
|
|
assert.isFalse(await gutil.timeoutReached(DELAY_2, delay(DELAY_1)));
|
|
|
|
assert.isFalse(await gutil.timeoutReached(DELAY_2, delay(DELAY_1)
|
|
|
|
.then(() => { throw new Error("test error"); })));
|
|
|
|
assert.isFalse(await gutil.timeoutReached(DELAY_2, Promise.resolve('foo')));
|
|
|
|
assert.isFalse(await gutil.timeoutReached(DELAY_2, Promise.reject('bar')));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-08-18 21:08:39 +00:00
|
|
|
describe("isValidHex", function() {
|
|
|
|
it('should work correctly', async function() {
|
|
|
|
assert.equal(gutil.isValidHex('#FF00FF'), true);
|
|
|
|
assert.equal(gutil.isValidHex('#FF00FFF'), false);
|
|
|
|
assert.equal(gutil.isValidHex('#FF0'), false);
|
|
|
|
assert.equal(gutil.isValidHex('#FF00'), false);
|
|
|
|
assert.equal(gutil.isValidHex('FF00FF'), false);
|
|
|
|
assert.equal(gutil.isValidHex('#FF00FG'), false);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("pruneArray", function() {
|
|
|
|
function check<T>(arr: T[], indexes: number[], expect: T[]) {
|
|
|
|
gutil.pruneArray(arr, indexes);
|
|
|
|
assert.deepEqual(arr, expect);
|
|
|
|
}
|
|
|
|
it('should remove correct elements', function() {
|
|
|
|
check(['a', 'b', 'c'], [], ['a', 'b', 'c']);
|
|
|
|
check(['a', 'b', 'c'], [0], ['b', 'c']);
|
|
|
|
check(['a', 'b', 'c'], [1], ['a', 'c']);
|
|
|
|
check(['a', 'b', 'c'], [2], ['a', 'b']);
|
|
|
|
check(['a', 'b', 'c'], [0, 1], ['c']);
|
|
|
|
check(['a', 'b', 'c'], [0, 2], ['b']);
|
|
|
|
check(['a', 'b', 'c'], [1, 2], ['a']);
|
|
|
|
check(['a', 'b', 'c'], [0, 1, 2], []);
|
|
|
|
check([], [], []);
|
|
|
|
check(['a'], [], ['a']);
|
|
|
|
check(['a'], [0], []);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|