From e34641a42d49599e4862ea7f19c7dd19e48c36b3 Mon Sep 17 00:00:00 2001 From: ssube Date: Tue, 30 Jun 2020 08:14:30 -0500 Subject: [PATCH] remove: async test tracker and pid file utils BREAKING CHANGE: this removes the test helpers, which were not well tested and required the `async_hooks` module, and the PID file helpers, which introduced a requirement on `fs` that could not be easily polyfilled. This should make the library easier to use in browsers and bundlers. --- src/AsyncTracker.ts | 96 ----------------------------------- src/PidFile.ts | 44 ---------------- src/index.ts | 7 --- test/TestApp.ts | 5 +- test/TestHelpers.ts | 6 --- test/helpers/async.ts | 74 --------------------------- test/utils/TestArrayMapper.ts | 11 ++-- test/utils/TestAsync.ts | 11 ++-- test/utils/TestBuffer.ts | 15 +++--- test/utils/TestChecklist.ts | 15 +++--- test/utils/TestChild.ts | 13 +++-- test/utils/TestMap.ts | 35 +++++++------ test/utils/TestPidFile.ts | 42 --------------- test/utils/TestSignal.ts | 5 +- test/utils/TestString.ts | 17 +++---- test/utils/TestUtils.ts | 47 +++++++++-------- 16 files changed, 82 insertions(+), 361 deletions(-) delete mode 100644 src/AsyncTracker.ts delete mode 100644 src/PidFile.ts delete mode 100644 test/TestHelpers.ts delete mode 100644 test/helpers/async.ts delete mode 100644 test/utils/TestPidFile.ts diff --git a/src/AsyncTracker.ts b/src/AsyncTracker.ts deleted file mode 100644 index 6f28f2f..0000000 --- a/src/AsyncTracker.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { AsyncHook, createHook } from 'async_hooks'; - -import { isDebug } from './Env'; -import { isNil, Optional } from './Maybe'; - -export interface TrackedResource { - source: string; - triggerAsyncId: number; - type: string; -} - -export type StackFilter = (stack: string) => string; - -/** - * Async resource tracker using node's internal hooks. - * - * This probably won't work in a browser. It does not hold references to the resource, to avoid leaks. - * Adapted from https://gist.github.com/boneskull/7fe75b63d613fa940db7ec990a5f5843#file-async-dump-js - * - * @public - */ -export class AsyncTracker { - public filter: Optional; - private readonly hook: AsyncHook; - private readonly resources: Map; - - constructor() { - this.resources = new Map(); - this.hook = createHook({ - destroy: (id: number) => { - this.resources.delete(id); - }, - init: (id: number, type: string, triggerAsyncId: number) => { - const source = this.getStack(); - // @TODO: exclude async hooks, including this one - this.resources.set(id, { - source, - triggerAsyncId, - type, - }); - }, - promiseResolve: (id: number) => { - this.resources.delete(id); - }, - }); - } - - /** - * Get a filtered version of the current call stack. This creates a new error to generate the - * stack trace and will be quite slow. - */ - public getStack(): string { - const err = new Error(); - if (isNil(err.stack)) { - return 'no stack trace available'; - } - - if (isNil(this.filter)) { - return err.stack; - } - - return this.filter(err.stack); - } - - public clear() { - this.resources.clear(); - } - - public disable() { - this.hook.disable(); - } - - /** - * Print a listing of all tracked resources. When debug mode is enabled (DEBUG=TRUE), include - * stack traces. - */ - /* eslint-disable no-console, no-invalid-this */ - public dump() { - console.error(`tracking ${this.resources.size} async resources`); - this.resources.forEach((res, id) => { - console.error(`${id}: ${res.type}`); - if (isDebug()) { - console.error(res.source); - console.error('\n'); - } - }); - } - - public enable() { - this.hook.enable(); - } - - public get size(): number { - return this.resources.size; - } -} diff --git a/src/PidFile.ts b/src/PidFile.ts deleted file mode 100644 index d2d436b..0000000 --- a/src/PidFile.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { open, unlink, write } from 'fs'; -import { pid } from 'process'; - -import { doesExist, Optional } from './Maybe'; - -type OptionalErrno = Optional; - -/** - * Write the current process ID to a file at the given `path`. - * - * @public - */ -export async function writePid(path: string): Promise { - return new Promise((res, rej) => { - open(path, 'wx', (openErr: OptionalErrno, fd: number) => { - if (doesExist(openErr)) { - rej(openErr); - } else { - write(fd, pid.toString(), 0, 'utf8', (writeErr: OptionalErrno) => { - if (doesExist(writeErr)) { - rej(writeErr); - } else { - res(); - } - }); - } - }); - }); -} - -/** - * Remove the file at the given `path`. - */ -export async function removePid(path: string): Promise { - return new Promise((res, rej) => { - unlink(path, (err: OptionalErrno) => { - if (doesExist(err)) { - rej(err); - } else { - res(); - } - }); - }); -} diff --git a/src/index.ts b/src/index.ts index 7330f1c..97e9bde 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,9 +5,6 @@ export { NotFoundError } from './error/NotFoundError'; export { NotImplementedError } from './error/NotImplementedError'; export { TimeoutError } from './error/TimeoutError'; -export { - AsyncTracker, -} from './AsyncTracker'; export { ArrayMapper, ArrayMapperOptions, @@ -71,10 +68,6 @@ export { mustExist, mustFind, } from './Maybe'; -export { - removePid, - writePid, -} from './PidFile'; export { constructorName, getConstructor, diff --git a/test/TestApp.ts b/test/TestApp.ts index 5336563..3efc25b 100644 --- a/test/TestApp.ts +++ b/test/TestApp.ts @@ -2,10 +2,9 @@ import { expect } from 'chai'; import { spy } from 'sinon'; import { main } from '../src/app'; -import { describeLeaks, itLeaks } from './helpers/async'; -describeLeaks('app', async () => { - itLeaks('should log a message', async () => { +describe('app', async () => { + it('should log a message', async () => { /* tslint:disable-next-line:no-console no-unbound-method */ const logSpy = spy(console, 'log'); diff --git a/test/TestHelpers.ts b/test/TestHelpers.ts deleted file mode 100644 index 2488ed7..0000000 --- a/test/TestHelpers.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { describeLeaks, itLeaks } from './helpers/async'; - -describeLeaks('test helpers', async () => { - itLeaks('should wrap suites'); - itLeaks('should wrap tests'); -}); diff --git a/test/helpers/async.ts b/test/helpers/async.ts deleted file mode 100644 index 568fabb..0000000 --- a/test/helpers/async.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { AsyncTracker } from '../../src/AsyncTracker'; -import { isNil } from '../../src/Maybe'; -import { isDebug } from '../../src/Env'; - -// this will pull Mocha internals out of the stacks -/* eslint-disable-next-line @typescript-eslint/no-var-requires */ -const { stackTraceFilter } = require('mocha/lib/utils'); - -type AsyncMochaTest = (this: Mocha.Context | void) => Promise; -type AsyncMochaSuite = (this: Mocha.Suite) => Promise; - -/** - * Describe a suite of async tests. This wraps mocha's describe to track async resources and report leaks. - */ -export function describeLeaks(description: string, cb: AsyncMochaSuite): Mocha.Suite { - return describe(description, function trackSuite(this: Mocha.Suite) { - const tracker = new AsyncTracker(); - tracker.filter = stackTraceFilter; - - beforeEach(() => { - tracker.enable(); - }); - - afterEach(() => { - tracker.disable(); - const leaked = tracker.size; - - // @TODO: this should only exclude the single Immediate set by the Tracker - if (leaked > 1) { - tracker.dump(); - const msg = `test leaked ${leaked - 1} async resources`; - if (isDebug()) { - throw new Error(msg); - } else { - /* eslint-disable-next-line no-console */ - console.warn(msg); - } - } - - tracker.clear(); - }); - - /* eslint-disable-next-line no-invalid-this */ - const suite: PromiseLike | undefined = cb.call(this); - if (isNil(suite) || !Reflect.has(suite, 'then')) { - /* eslint-disable-next-line no-console */ - console.error(`test suite '${description}' did not return a promise`); - } - - return suite; - }); -} - -/** - * Run an asynchronous test with unhandled rejection guards. - * - * This function may not have any direct test coverage. It is too simple to reasonably mock. - */ -export function itLeaks(expectation: string, cb?: AsyncMochaTest): Mocha.Test { - if (isNil(cb)) { - return it(expectation); - } - - return it(expectation, function trackTest(this: Mocha.Context) { - return new Promise((res, rej) => { - /* eslint-disable-next-line no-invalid-this */ - cb.call(this).then((value: unknown) => { - res(value); - }, (err: Error) => { - rej(err); - }); - }); - }); -} diff --git a/test/utils/TestArrayMapper.ts b/test/utils/TestArrayMapper.ts index 6194fd1..e442cf7 100644 --- a/test/utils/TestArrayMapper.ts +++ b/test/utils/TestArrayMapper.ts @@ -1,11 +1,10 @@ import { expect } from 'chai'; import { ArrayMapper } from '../../src/ArrayMapper'; -import { describeLeaks, itLeaks } from '../helpers/async'; -describeLeaks('utils', async () => { - describeLeaks('array mapper', async () => { - itLeaks('should take initial args', async () => { +describe('utils', async () => { + describe('array mapper', async () => { + it('should take initial args', async () => { const mapper = new ArrayMapper({ rest: 'others', skip: 0, @@ -18,7 +17,7 @@ describeLeaks('utils', async () => { expect(results.get('others'), 'rest should be collected').to.deep.equal(['3', '4']); }); - itLeaks('should always include rest arg', async () => { + it('should always include rest arg', async () => { const mapper = new ArrayMapper({ rest: 'empty', skip: 0, @@ -29,7 +28,7 @@ describeLeaks('utils', async () => { expect(results.get('empty'), 'rest key should be empty').to.have.lengthOf(0); }); - itLeaks('should skit initial args', async () => { + it('should skit initial args', async () => { const mapper = new ArrayMapper({ rest: 'empty', skip: 3, diff --git a/test/utils/TestAsync.ts b/test/utils/TestAsync.ts index 193522c..2abeffc 100644 --- a/test/utils/TestAsync.ts +++ b/test/utils/TestAsync.ts @@ -2,14 +2,13 @@ import { expect } from 'chai'; import { defer, timeout } from '../../src/Async'; import { TimeoutError } from '../../src/error/TimeoutError'; -import { describeLeaks, itLeaks } from '../helpers/async'; -describeLeaks('async utils', async () => { - describeLeaks('defer', async () => { - itLeaks('should resolve', async () => expect(defer(10, true)).to.eventually.equal(true)); +describe('async utils', async () => { + describe('defer', async () => { + it('should resolve', async () => expect(defer(10, true)).to.eventually.equal(true)); }); - describeLeaks('timeout', async () => { - itLeaks('should reject slow promises', async () => expect(timeout(10, defer(20))).to.eventually.be.rejectedWith(TimeoutError)); + describe('timeout', async () => { + it('should reject slow promises', async () => expect(timeout(10, defer(20))).to.eventually.be.rejectedWith(TimeoutError)); }); }); diff --git a/test/utils/TestBuffer.ts b/test/utils/TestBuffer.ts index 950d62a..37d7eb5 100644 --- a/test/utils/TestBuffer.ts +++ b/test/utils/TestBuffer.ts @@ -1,11 +1,10 @@ import { expect } from 'chai'; import { concat, encode } from '../../src/Buffer'; -import { describeLeaks, itLeaks } from '../helpers/async'; -describeLeaks('buffer utils', async () => { - describeLeaks('concat', async () => { - itLeaks('should append chunk buffers', async () => { +describe('buffer utils', async () => { + describe('concat', async () => { + it('should append chunk buffers', async () => { expect(concat([ Buffer.from('hello'), Buffer.from('world'), @@ -13,18 +12,18 @@ describeLeaks('buffer utils', async () => { }); }); - describeLeaks('encode', async () => { - itLeaks('should encode chunk buffers', async () => { + describe('encode', async () => { + it('should encode chunk buffers', async () => { expect(encode([ Buffer.from('hello world'), ], 'utf-8')).to.equal('hello world'); }); - itLeaks('should encode no buffers', async () => { + it('should encode no buffers', async () => { expect(encode([], 'utf-8')).to.equal(''); }); - itLeaks('should encode empty buffers', async () => { + it('should encode empty buffers', async () => { expect(encode([ new Buffer(0), ], 'utf-8')).to.equal(''); diff --git a/test/utils/TestChecklist.ts b/test/utils/TestChecklist.ts index d62d0f8..5c6a284 100644 --- a/test/utils/TestChecklist.ts +++ b/test/utils/TestChecklist.ts @@ -1,16 +1,15 @@ import { expect } from 'chai'; import { Checklist, ChecklistMode } from '../../src/Checklist'; -import { describeLeaks, itLeaks } from '../helpers/async'; const EXISTING_ITEM = 'foo'; const MISSING_ITEM = 'bin'; const TEST_DATA = [EXISTING_ITEM, 'bar']; // tslint:disable:no-duplicate-functions -describeLeaks('checklist', async () => { - describeLeaks('exclude mode', async () => { - itLeaks('should check for present values', async () => { +describe('checklist', async () => { + describe('exclude mode', async () => { + it('should check for present values', async () => { const list = new Checklist({ data: TEST_DATA, mode: ChecklistMode.EXCLUDE, @@ -18,7 +17,7 @@ describeLeaks('checklist', async () => { expect(list.check(EXISTING_ITEM)).to.equal(false); }); - itLeaks('should check for missing values', async () => { + it('should check for missing values', async () => { const list = new Checklist({ data: TEST_DATA, mode: ChecklistMode.EXCLUDE, @@ -27,8 +26,8 @@ describeLeaks('checklist', async () => { }); }); - describeLeaks('include mode', async () => { - itLeaks('should check for present values', async () => { + describe('include mode', async () => { + it('should check for present values', async () => { const list = new Checklist({ data: TEST_DATA, mode: ChecklistMode.INCLUDE, @@ -36,7 +35,7 @@ describeLeaks('checklist', async () => { expect(list.check(EXISTING_ITEM)).to.equal(true); }); - itLeaks('should check for missing values', async () => { + it('should check for missing values', async () => { const list = new Checklist({ data: TEST_DATA, mode: ChecklistMode.INCLUDE, diff --git a/test/utils/TestChild.ts b/test/utils/TestChild.ts index ba95dbf..e1fc838 100644 --- a/test/utils/TestChild.ts +++ b/test/utils/TestChild.ts @@ -3,7 +3,6 @@ import { expect } from 'chai'; import { ChildProcessError } from '../../src'; import { ChildStreams, waitForChild } from '../../src/Child'; import { mustExist, Optional } from '../../src/Maybe'; -import { describeLeaks, itLeaks } from '../helpers/async'; type Closer = (status: number) => Promise; @@ -29,9 +28,9 @@ function createChild(): ChildStreams & { closer: Optional } { } as any; } -describeLeaks('child process utils', async () => { - describeLeaks('wait for child helper', async () => { - itLeaks('should read stdout data', async () => { +describe('child process utils', async () => { + describe('wait for child helper', async () => { + it('should read stdout data', async () => { const child = createChild(); const resultPromise = waitForChild(child); @@ -41,10 +40,10 @@ describeLeaks('child process utils', async () => { expect(result.status).to.equal(0); }); - itLeaks('should read stderr data'); - itLeaks('should resolve on success status'); + it('should read stderr data'); + it('should resolve on success status'); - itLeaks('should reject on failure status', async () => { + it('should reject on failure status', async () => { const child = createChild(); const resultPromise = waitForChild(child); diff --git a/test/utils/TestMap.ts b/test/utils/TestMap.ts index c8717d4..9da7758 100644 --- a/test/utils/TestMap.ts +++ b/test/utils/TestMap.ts @@ -15,7 +15,6 @@ import { pushMergeMap, normalizeMap, } from '../../src/Map'; -import { describeLeaks, itLeaks } from '../helpers/async'; const DEFAULT_VALUE = 'default'; const mapKey = 'key'; @@ -29,22 +28,22 @@ const multiItem = new Map([ /* eslint-enable */ ]); -describeLeaks('map utils', async () => { - describeLeaks('make dict', async () => { - itLeaks('should return an empty dict for nil values', async () => { +describe('map utils', async () => { + describe('make dict', async () => { + it('should return an empty dict for nil values', async () => { /* eslint-disable-next-line no-null/no-null */ expect(makeDict(null)).to.deep.equal({}); expect(makeDict(undefined)).to.deep.equal({}); }); - itLeaks('should return an existing dict', async () => { + it('should return an existing dict', async () => { const input = {}; expect(makeDict(input)).to.equal(input); }); }); - describeLeaks('make map', async () => { - itLeaks('should convert objects to maps', async () => { + describe('make map', async () => { + it('should convert objects to maps', async () => { const data = { bar: '2', foo: '1', @@ -55,44 +54,44 @@ describeLeaks('map utils', async () => { }); }); - describeLeaks('must get helper', async () => { - itLeaks('should get existing keys', async () => { + describe('must get helper', async () => { + it('should get existing keys', async () => { expect(mustGet(singleItem, mapKey)).to.equal(mapValue); }); - itLeaks('should throw on missing keys', async () => { + it('should throw on missing keys', async () => { expect(() => { mustGet(singleItem, 'nope'); }).to.throw(NotFoundError); }); }); - describeLeaks('get head helper', async () => { - itLeaks('should get the first item from existing keys', async () => { + describe('get head helper', async () => { + it('should get the first item from existing keys', async () => { expect(getHead(multiItem, mapKey)).to.equal(mapValue); }); - itLeaks('should throw on missing keys', async () => { + it('should throw on missing keys', async () => { expect(() => { getHead(multiItem, 'nope'); }).to.throw(NotFoundError); }); }); - describeLeaks('get head or default helper', async () => { - itLeaks('should get the first item from existing keys', async () => { + describe('get head or default helper', async () => { + it('should get the first item from existing keys', async () => { expect(getHeadOrDefault(multiItem, mapKey, 'nope')).to.equal(mapValue); }); - itLeaks('should get the default for missing keys', async () => { + it('should get the default for missing keys', async () => { expect(getHeadOrDefault(multiItem, 'nope', mapValue)).to.equal(mapValue); }); - itLeaks('should return the default value for nil values', async () => { + it('should return the default value for nil values', async () => { expect(getHeadOrDefault(multiItem, 'nilValue', mapValue)).to.equal(mapValue); }); - itLeaks('should return the default value for nil keys', async () => { + it('should return the default value for nil keys', async () => { expect(getHeadOrDefault(multiItem, 'nilKey', mapValue)).to.equal(mapValue); }); }); diff --git a/test/utils/TestPidFile.ts b/test/utils/TestPidFile.ts deleted file mode 100644 index b8b5043..0000000 --- a/test/utils/TestPidFile.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { expect } from 'chai'; -import mockFS from 'mock-fs'; - -import { removePid, writePid } from '../../src'; -import { describeLeaks, itLeaks } from '../helpers/async'; - -const PID_PATH = 'foo'; -const PID_NAME = 'foo/test.pid'; - -describeLeaks('pid file utils', async () => { - beforeEach(() => { - mockFS({ - [PID_PATH]: mockFS.directory(), - }); - }); - - afterEach(() => { - mockFS.restore(); - }); - - itLeaks('should create a marker', async () => { - await writePid(PID_NAME); - - mockFS.restore(); - }); - - itLeaks('should not replace an existing marker', async () => { - await writePid(PID_NAME); - return expect(writePid(PID_PATH)).to.eventually.be.rejectedWith(Error); - }); - - itLeaks('should remove an existing marker', async () => { - await writePid(PID_NAME); - await removePid(PID_NAME); - - mockFS.restore(); - }); - - itLeaks('should fail to remove a missing marker', async () => - expect(removePid(PID_PATH)).to.eventually.be.rejectedWith(Error) - ); -}); diff --git a/test/utils/TestSignal.ts b/test/utils/TestSignal.ts index 26be9e6..a004d7d 100644 --- a/test/utils/TestSignal.ts +++ b/test/utils/TestSignal.ts @@ -2,12 +2,11 @@ import { expect } from 'chai'; import { timeout } from '../../src/Async'; import { signal, SIGNAL_RESET } from '../../src/Signal'; -import { describeLeaks, itLeaks } from '../helpers/async'; const MAX_SIGNAL_TIME = 500; -describeLeaks('signal utils', async () => { - itLeaks('should wait for a signal', async () => { +describe('signal utils', async () => { + it('should wait for a signal', async () => { const signalPromise = signal(SIGNAL_RESET); process.kill(process.pid, SIGNAL_RESET); diff --git a/test/utils/TestString.ts b/test/utils/TestString.ts index c6bb1fc..59c28ff 100644 --- a/test/utils/TestString.ts +++ b/test/utils/TestString.ts @@ -1,39 +1,38 @@ import { expect } from 'chai'; import { leftPad, trim } from '../../src/String'; -import { describeLeaks, itLeaks } from '../helpers/async'; const TEST_SHORT = 'hello'; const TEST_LONG = 'hello world'; -describeLeaks('left pad helper', async () => { - itLeaks('should prepend padding', async () => { +describe('left pad helper', async () => { + it('should prepend padding', async () => { expect(leftPad('test')).to.equal('0000test'); }); - itLeaks('should return long strings as-is', async () => { + it('should return long strings as-is', async () => { const long = 'testing-words'; expect(leftPad(long, 8)).to.equal(long); }); - itLeaks('should use padding string', async () => { + it('should use padding string', async () => { expect(leftPad('test', 8, 'too')).to.equal('toottest', 'must repeat and truncate the padding string'); }); }); -describeLeaks('trim helper', async () => { - itLeaks('should return strings shorter than max', async () => { +describe('trim helper', async () => { + it('should return strings shorter than max', async () => { expect(trim('yes', 5)).to.equal('yes', 'shorter than max'); expect(trim(TEST_SHORT, 5)).to.equal(TEST_SHORT, 'equal to max'); }); - itLeaks('should trim strings longer than max', async () => { + it('should trim strings longer than max', async () => { expect(trim(TEST_LONG, 3, '...')).to.equal('...'); expect(trim(TEST_LONG, 5)).to.equal('he...'); expect(trim(TEST_LONG, 8)).to.equal('hello...'); }); - itLeaks('should not add tail when max is small', async () => { + it('should not add tail when max is small', async () => { expect(trim(TEST_SHORT, 2, '...')).to.equal('he'); expect(trim(TEST_LONG, 5, 'very long tail')).to.equal(TEST_SHORT); expect(trim(TEST_SHORT, 8, 'very long tail')).to.equal(TEST_SHORT); diff --git a/test/utils/TestUtils.ts b/test/utils/TestUtils.ts index b38ffc9..8844763 100644 --- a/test/utils/TestUtils.ts +++ b/test/utils/TestUtils.ts @@ -1,62 +1,61 @@ import { expect } from 'chai'; +import { ensureArray, hasItems } from '../../src'; import { NotFoundError } from '../../src/error/NotFoundError'; import { countOf, defaultWhen, filterNil, mustCoalesce, mustFind } from '../../src/Maybe'; -import { describeLeaks, itLeaks } from '../helpers/async'; -import { hasItems, ensureArray } from '../../src'; -describeLeaks('utils', async () => { - describeLeaks('count list', async () => { - itLeaks('should count a single item', async () => { +describe('utils', async () => { + describe('count list', async () => { + it('should count a single item', async () => { expect(countOf(1)).to.equal(1, 'numbers'); expect(countOf('')).to.equal(1, 'empty strings'); expect(countOf('123')).to.equal(1, 'other strings'); }); - itLeaks('should count an array of items', async () => { + it('should count an array of items', async () => { expect(countOf([1])).to.equal(1, 'single item list'); expect(countOf([1, 2, 3])).to.equal(3, 'multi item list'); }); - itLeaks('should count an unknown argument as 0', async () => { + it('should count an unknown argument as 0', async () => { expect(countOf(undefined)).to.equal(0, 'undefined'); // eslint-disable-next-line no-null/no-null expect(countOf(null)).to.equal(0, 'null'); }); }); - describeLeaks('filter nil', async () => { - itLeaks('should remove nil items', async () => { + describe('filter nil', async () => { + it('should remove nil items', async () => { // eslint-disable-next-line no-null/no-null expect(filterNil([1, undefined, 2, null, 3])).to.deep.equal([1, 2, 3]); }); }); - describeLeaks('must find helper', async () => { - itLeaks('should return matching item', async () => { + describe('must find helper', async () => { + it('should return matching item', async () => { expect(mustFind([1, 2, 3], (val) => (val % 2) === 0)).to.equal(2); }); - itLeaks('should throw if no item matches', async () => { + it('should throw if no item matches', async () => { expect(() => { mustFind([1, 2, 3], (val) => val === 4); }).to.throw(NotFoundError); }); }); - describeLeaks('default when', async () => { - itLeaks('should return the first item when the condition is true', async () => { + describe('default when', async () => { + it('should return the first item when the condition is true', async () => { expect(defaultWhen(true, 1, 2)).to.equal(1); }); - itLeaks('should return the second item otherwise', async () => { + it('should return the second item otherwise', async () => { expect(defaultWhen(false, 1, 2)).to.equal(2); }); }); - describeLeaks('must coalesce helper', async () => { + describe('must coalesce helper', async () => { /* eslint-disable no-null/no-null */ - itLeaks('should return the first existent value', async () => { + it('should return the first existent value', async () => { expect(mustCoalesce(null, null, 3, null)).to.equal(3); expect(mustCoalesce(null, null, undefined, 'string')).to.equal('string'); expect(mustCoalesce(null, undefined, [], null)).to.deep.equal([]); @@ -64,19 +63,19 @@ describeLeaks('utils', async () => { /* eslint-enable no-null/no-null */ }); - describeLeaks('has items helper', async () => { - itLeaks('should return false for non-array values', async () => { + describe('has items helper', async () => { + it('should return false for non-array values', async () => { /* eslint-disable @typescript-eslint/no-explicit-any */ expect(hasItems({} as any)).to.equal(false); expect(hasItems(4 as any)).to.equal(false); /* eslint-enable @typescript-eslint/no-explicit-any */ }); - itLeaks('should return false for empty arrays', async () => { + it('should return false for empty arrays', async () => { expect(hasItems([])).to.equal(false); }); - itLeaks('should return true for arrays with elements', async () => { + it('should return true for arrays with elements', async () => { expect(hasItems([ true, false, @@ -84,14 +83,14 @@ describeLeaks('utils', async () => { }); }); - describeLeaks('ensure array helper', async () => { - itLeaks('should convert nil values to arrays', async () => { + describe('ensure array helper', async () => { + it('should convert nil values to arrays', async () => { /* eslint-disable-next-line no-null/no-null */ expect(ensureArray(null)).to.deep.equal([]); expect(ensureArray(undefined)).to.deep.equal([]); }); - itLeaks('should copy arrays', async () => { + it('should copy arrays', async () => { const data: Array = []; const copy = ensureArray(data);