unify usage of sleepAsync and add tests
The tests mock JS setTimeout API. However promise.resolve() is not working without flushing the promise queue (which could be done just by awaiting Promise.resolve()), similar issue has been discussed in facebook/jest#2157.
This commit is contained in:
5
src/infrastructure/Threading/AsyncSleep.ts
Normal file
5
src/infrastructure/Threading/AsyncSleep.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export type SchedulerType = (callback: (...args: any[]) => void, ms: number) => void;
|
||||
|
||||
export function sleepAsync(time: number, scheduler: SchedulerType = setTimeout) {
|
||||
return new Promise((resolve) => scheduler(() => resolve(undefined), time));
|
||||
}
|
||||
@@ -22,7 +22,8 @@ import LiquorTree from 'liquor-tree';
|
||||
import Node from './Node/Node.vue';
|
||||
import { INode } from './Node/INode';
|
||||
import { convertExistingToNode, toNewLiquorTreeNode } from './LiquorTree/NodeWrapper/NodeTranslator';
|
||||
import { INodeSelectedEvent } from './/INodeSelectedEvent';
|
||||
import { INodeSelectedEvent } from './INodeSelectedEvent';
|
||||
import { sleepAsync } from '@/infrastructure/Threading/AsyncSleep';
|
||||
import { getNewState } from './LiquorTree/NodeWrapper/NodeStateUpdater';
|
||||
import { LiquorTreeOptions } from './LiquorTree/LiquorTreeOptions';
|
||||
import { FilterPredicate, NodePredicateFilter } from './LiquorTree/NodeWrapper/NodePredicateFilter';
|
||||
@@ -121,7 +122,6 @@ function recurseDown(
|
||||
async function tryUntilDefinedAsync<T>(
|
||||
accessor: () => T | undefined,
|
||||
delayInMs: number, maxTries: number): Promise<T | undefined> {
|
||||
const sleepAsync = () => new Promise(((resolve) => setTimeout(resolve, delayInMs)));
|
||||
let triesLeft = maxTries;
|
||||
let value: T;
|
||||
while (triesLeft !== 0) {
|
||||
@@ -130,7 +130,7 @@ async function tryUntilDefinedAsync<T>(
|
||||
return value;
|
||||
}
|
||||
triesLeft--;
|
||||
await sleepAsync();
|
||||
await sleepAsync(delayInMs);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { AsyncLazy } from '@/infrastructure/Threading/AsyncLazy';
|
||||
import { sleepAsync } from '@/infrastructure/Threading/AsyncSleep';
|
||||
|
||||
describe('AsyncLazy', () => {
|
||||
it('returns value from lambda', async () => {
|
||||
@@ -33,7 +34,6 @@ describe('AsyncLazy', () => {
|
||||
});
|
||||
it('when running long-running task in parallel', async () => {
|
||||
// act
|
||||
const sleepAsync = (time: number) => new Promise(((resolve) => setTimeout(resolve, time)));
|
||||
const sut = new AsyncLazy(async () => {
|
||||
await sleepAsync(100);
|
||||
totalExecuted++;
|
||||
79
tests/unit/infrastructure/Threading/AsyncSleep.spec.ts
Normal file
79
tests/unit/infrastructure/Threading/AsyncSleep.spec.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'mocha';
|
||||
import { expect } from 'chai';
|
||||
import { sleepAsync, SchedulerType } from '@/infrastructure/Threading/AsyncSleep';
|
||||
|
||||
describe('AsyncSleep', () => {
|
||||
it('fulfills after delay', async () => {
|
||||
// arrange
|
||||
const delayInMs = 10;
|
||||
const scheduler = new SchedulerMock();
|
||||
// act
|
||||
const sleep = sleepAsync(delayInMs, scheduler.mock);
|
||||
const promiseState = watchPromiseState(sleep);
|
||||
scheduler.tickNext(delayInMs);
|
||||
await flushPromiseResolutionQueue();
|
||||
// assert
|
||||
const actual = promiseState.isFulfilled();
|
||||
expect(actual).to.equal(true);
|
||||
});
|
||||
it('pending before delay', async () => {
|
||||
// arrange
|
||||
const delayInMs = 10;
|
||||
const scheduler = new SchedulerMock();
|
||||
// act
|
||||
const sleep = sleepAsync(delayInMs, scheduler.mock);
|
||||
const promiseState = watchPromiseState(sleep);
|
||||
scheduler.tickNext(delayInMs / 5);
|
||||
await flushPromiseResolutionQueue();
|
||||
// assert
|
||||
const actual = promiseState.isPending();
|
||||
expect(actual).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
function flushPromiseResolutionQueue() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
class SchedulerMock {
|
||||
public readonly mock: SchedulerType;
|
||||
private currentTime = 0;
|
||||
private scheduledActions = new Array<{time: number, action: (...args: any[]) => void}>();
|
||||
constructor() {
|
||||
this.mock = (callback: (...args: any[]) => void, ms: number) => {
|
||||
this.scheduledActions.push({ time: this.currentTime + ms, action: callback });
|
||||
};
|
||||
}
|
||||
public tickNext(ms: number) {
|
||||
const newTime = this.currentTime + ms;
|
||||
let newActions = this.scheduledActions;
|
||||
for (const action of this.scheduledActions) {
|
||||
if (newTime >= action.time) {
|
||||
newActions = newActions.filter((a) => a !== action);
|
||||
action.action();
|
||||
}
|
||||
}
|
||||
this.scheduledActions = newActions;
|
||||
}
|
||||
}
|
||||
|
||||
function watchPromiseState<T>(promise: Promise<T>) {
|
||||
let isPending = true;
|
||||
let isRejected = false;
|
||||
let isFulfilled = false;
|
||||
promise.then(
|
||||
() => {
|
||||
isFulfilled = true;
|
||||
isPending = false;
|
||||
},
|
||||
() => {
|
||||
isRejected = true;
|
||||
isPending = false;
|
||||
},
|
||||
);
|
||||
return {
|
||||
isFulfilled: () => isFulfilled,
|
||||
isPending: () => isPending,
|
||||
isRejected: () => isRejected,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user