diff --git a/test/integration/LockingResourceStore.test.ts b/test/integration/LockingResourceStore.test.ts index 80b8cbf94..08940d168 100644 --- a/test/integration/LockingResourceStore.test.ts +++ b/test/integration/LockingResourceStore.test.ts @@ -16,6 +16,8 @@ import { SingleThreadedResourceLocker } from '../../src/util/locking/SingleThrea import { WrappedExpiringReadWriteLocker } from '../../src/util/locking/WrappedExpiringReadWriteLocker'; import { guardedStreamFrom } from '../../src/util/StreamUtil'; import { PIM, RDF } from '../../src/util/Vocabularies'; +import { flushPromises } from '../util/Util'; + jest.useFakeTimers('legacy'); describe('A LockingResourceStore', (): void => { @@ -67,7 +69,7 @@ describe('A LockingResourceStore', (): void => { // Wait 1000ms and read jest.advanceTimersByTime(1000); - await new Promise(setImmediate); + await flushPromises(); expect(representation.data.destroyed).toBe(true); // Verify a timeout error was thrown @@ -95,7 +97,7 @@ describe('A LockingResourceStore', (): void => { // Wait 1000ms and watch the stream be destroyed jest.advanceTimersByTime(1000); - await new Promise(setImmediate); + await flushPromises(); expect(representation.data.destroyed).toBe(true); // Verify a timeout error was thrown diff --git a/test/integration/RedisResourceLockerIntegration.test.ts b/test/integration/RedisResourceLockerIntegration.test.ts index a0e13096d..eebbaee8d 100644 --- a/test/integration/RedisResourceLockerIntegration.test.ts +++ b/test/integration/RedisResourceLockerIntegration.test.ts @@ -1,7 +1,6 @@ import fetch from 'cross-fetch'; import type { App, RedisResourceLocker } from '../../src'; - -import { describeIf, getPort } from '../util/Util'; +import { describeIf, flushPromises, getPort } from '../util/Util'; import { getDefaultVariables, getTestConfigPath, instantiateFromConfig } from './Config'; /** @@ -139,7 +138,7 @@ describeIf('docker', 'A server with a RedisResourceLocker as ResourceLocker', () const lock2 = locker.acquire(identifier); const lock3 = locker.acquire(identifier); - await new Promise((resolve): any => setImmediate(resolve)); + await flushPromises(); const l2 = lock2.then(async(): Promise => { res += 'l2'; diff --git a/test/unit/init/AppRunner.test.ts b/test/unit/init/AppRunner.test.ts index 2c41aa1c0..2b728e4f9 100644 --- a/test/unit/init/AppRunner.test.ts +++ b/test/unit/init/AppRunner.test.ts @@ -4,6 +4,7 @@ import { AppRunner } from '../../../src/init/AppRunner'; import type { CliExtractor } from '../../../src/init/cli/CliExtractor'; import type { SettingsResolver } from '../../../src/init/variables/SettingsResolver'; import { joinFilePath } from '../../../src/util/PathUtil'; +import { flushPromises } from '../../util/Util'; const app: jest.Mocked = { start: jest.fn(), @@ -315,9 +316,7 @@ describe('AppRunner', (): void => { new AppRunner().runCliSync({ argv: [ 'node', 'script' ]}); // Wait until app.start has been called, because we can't await AppRunner.run. - await new Promise((resolve): void => { - setImmediate(resolve); - }); + await flushPromises(); expect(ComponentsManager.build).toHaveBeenCalledTimes(1); expect(ComponentsManager.build).toHaveBeenCalledWith({ @@ -348,9 +347,7 @@ describe('AppRunner', (): void => { new AppRunner().runCliSync({ argv: [ 'node', 'script' ]}); // Wait until app.start has been called, because we can't await AppRunner.runCli. - await new Promise((resolve): void => { - setImmediate(resolve); - }); + await flushPromises(); expect(write).toHaveBeenCalledTimes(1); expect(write).toHaveBeenLastCalledWith(expect.stringMatching(/Cause: Fatal/mu)); diff --git a/test/unit/storage/LockingResourceStore.test.ts b/test/unit/storage/LockingResourceStore.test.ts index eab032962..5323080cf 100644 --- a/test/unit/storage/LockingResourceStore.test.ts +++ b/test/unit/storage/LockingResourceStore.test.ts @@ -7,6 +7,7 @@ import { LockingResourceStore } from '../../../src/storage/LockingResourceStore' import type { ResourceStore } from '../../../src/storage/ResourceStore'; import type { ExpiringReadWriteLocker } from '../../../src/util/locking/ExpiringReadWriteLocker'; import { guardedStreamFrom } from '../../../src/util/StreamUtil'; +import { flushPromises } from '../../util/Util'; function emptyFn(): void { // Empty @@ -170,7 +171,7 @@ describe('A LockingResourceStore', (): void => { registerEventOrder(representation.data, 'end'); // Provide opportunity for async events - await new Promise(setImmediate); + await flushPromises(); // Verify the lock was acquired and released at the right time expect(locker.withReadLock).toHaveBeenCalledTimes(1); @@ -187,7 +188,7 @@ describe('A LockingResourceStore', (): void => { registerEventOrder(representation.data, 'end'); // Provide opportunity for async events - await new Promise(setImmediate); + await flushPromises(); // Verify the lock was acquired and released at the right time expect(locker.withReadLock).toHaveBeenCalledTimes(1); @@ -205,7 +206,7 @@ describe('A LockingResourceStore', (): void => { registerEventOrder(representation.data, 'close'); // Provide opportunity for async events - await new Promise(setImmediate); + await flushPromises(); // Verify the lock was acquired and released at the right time expect(locker.withReadLock).toHaveBeenCalledTimes(1); @@ -222,7 +223,7 @@ describe('A LockingResourceStore', (): void => { registerEventOrder(representation.data, 'close'); // Provide opportunity for async events - await new Promise(setImmediate); + await flushPromises(); // Verify the lock was acquired and released at the right time expect(locker.withReadLock).toHaveBeenCalledTimes(1); @@ -241,7 +242,7 @@ describe('A LockingResourceStore', (): void => { }); // Provide opportunity for async events - await new Promise(setImmediate); + await flushPromises(); // Verify the lock was acquired and released at the right time expect(locker.withReadLock).toHaveBeenCalledTimes(1); @@ -258,7 +259,7 @@ describe('A LockingResourceStore', (): void => { timeoutTrigger.emit('timeout'); // Provide opportunity for async events - await new Promise(setImmediate); + await flushPromises(); // Verify the lock was acquired and released at the right time expect(locker.withReadLock).toHaveBeenCalledTimes(1); diff --git a/test/unit/util/StreamUtil.test.ts b/test/unit/util/StreamUtil.test.ts index f82ca61df..2e72743ca 100644 --- a/test/unit/util/StreamUtil.test.ts +++ b/test/unit/util/StreamUtil.test.ts @@ -8,6 +8,7 @@ import { guardedStreamFrom, pipeSafely, transformSafely, readableToString, readableToQuads, readJsonStream, getSingleItem, } from '../../../src/util/StreamUtil'; +import { flushPromises } from '../../util/Util'; jest.mock('../../../src/logging/LogUtil', (): any => { const logger: Logger = { warn: jest.fn(), log: jest.fn() } as any; @@ -132,7 +133,7 @@ describe('StreamUtil', (): void => { piped.destroy(new Error('this causes an unpipe!')); // Allow events to propagate - await new Promise(setImmediate); + await flushPromises(); expect(input.destroyed).toBe(true); }); @@ -149,7 +150,7 @@ describe('StreamUtil', (): void => { piped.destroy(new Error('error!')); // Allow events to propagate - await new Promise(setImmediate); + await flushPromises(); expect(input.destroyed).toBe(false); }); diff --git a/test/unit/util/locking/GreedyReadWriteLocker.test.ts b/test/unit/util/locking/GreedyReadWriteLocker.test.ts index f39087b59..bacf7f846 100644 --- a/test/unit/util/locking/GreedyReadWriteLocker.test.ts +++ b/test/unit/util/locking/GreedyReadWriteLocker.test.ts @@ -5,6 +5,7 @@ import { ForbiddenHttpError } from '../../../../src/util/errors/ForbiddenHttpErr import { InternalServerError } from '../../../../src/util/errors/InternalServerError'; import { GreedyReadWriteLocker } from '../../../../src/util/locking/GreedyReadWriteLocker'; import type { ResourceLocker } from '../../../../src/util/locking/ResourceLocker'; +import { flushPromises } from '../../../util/Util'; // A simple ResourceLocker that keeps a queue of lock requests class MemoryLocker implements ResourceLocker { @@ -86,7 +87,7 @@ describe('A GreedyReadWriteLocker', (): void => { })); // Allow time to attach listeners - await new Promise(setImmediate); + await flushPromises(); emitter.emit('release2'); await expect(promises[2]).resolves.toBe(2); @@ -112,12 +113,12 @@ describe('A GreedyReadWriteLocker', (): void => { })); // Allow time to attach listeners - await new Promise(setImmediate); + await flushPromises(); emitter.emit('release2'); // Allow time to finish write 2 - await new Promise(setImmediate); + await flushPromises(); emitter.emit('release0'); emitter.emit('release1'); @@ -140,7 +141,7 @@ describe('A GreedyReadWriteLocker', (): void => { })); // Allow time to attach listeners - await new Promise(setImmediate); + await flushPromises(); emitter.emit('release1'); await expect(promises[1]).resolves.toBe(1); @@ -178,7 +179,7 @@ describe('A GreedyReadWriteLocker', (): void => { }); // Allow time to attach listeners - await new Promise(setImmediate); + await flushPromises(); const promAll = Promise.all([ delayedLockWrite, lockRead ]); @@ -213,7 +214,7 @@ describe('A GreedyReadWriteLocker', (): void => { }); // Allow time to attach listeners - await new Promise(setImmediate); + await flushPromises(); const promAll = Promise.all([ delayedLockWrite, lockRead ]); @@ -260,14 +261,14 @@ describe('A GreedyReadWriteLocker', (): void => { }); // Allow time to attach listeners - await new Promise(setImmediate); + await flushPromises(); const promAll = Promise.all([ delayedLockWrite, lockRead, delayedLockRead2 ]); emitter.emit('releaseRead1'); // Allow time to finish read 1 - await new Promise(setImmediate); + await flushPromises(); emitter.emit('releaseRead2'); await promAll; @@ -302,7 +303,7 @@ describe('A GreedyReadWriteLocker', (): void => { }); // Allow time to attach listeners - await new Promise(setImmediate); + await flushPromises(); const promAll = Promise.all([ delayedLockRead, lockWrite ]); diff --git a/test/util/Util.ts b/test/util/Util.ts index 2a32a3480..dc2a3590d 100644 --- a/test/util/Util.ts +++ b/test/util/Util.ts @@ -44,6 +44,17 @@ export function describeIf(envFlag: string, name: string, fn: () => void): void return enabled ? describe(name, fn) : describe.skip(name, fn); } +/** + * This is needed when you want to wait for all promises to resolve. + * Also works when using jest.useFakeTimers(). + * For more details see the links below + * - https://github.com/facebook/jest/issues/2157 + * - https://stackoverflow.com/questions/52177631/jest-timer-and-promise-dont-work-well-settimeout-and-async-function + */ +export async function flushPromises(): Promise { + return new Promise(jest.requireActual('timers').setImmediate); +} + /** * Mocks (some) functions of the fs system library. * It is important that you call `jest.mock('fs');` in your test file before calling this!!!