2020-03-29 14:52:40 +00:00
|
|
|
import { AsyncTracker } from '../../src/AsyncTracker';
|
2020-03-29 13:43:52 +00:00
|
|
|
import { isNil } from '../../src/utils';
|
|
|
|
import { isDebug } from '../../src/utils/Env';
|
2020-03-03 00:33:48 +00:00
|
|
|
|
2019-09-17 12:45:15 +00:00
|
|
|
// this will pull Mocha internals out of the stacks
|
2019-11-09 22:50:30 +00:00
|
|
|
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
|
2019-09-17 12:45:15 +00:00
|
|
|
const { stackTraceFilter } = require('mocha/lib/utils');
|
|
|
|
const filterStack = stackTraceFilter();
|
|
|
|
|
|
|
|
type AsyncMochaTest = (this: Mocha.Context | void) => Promise<void>;
|
|
|
|
type AsyncMochaSuite = (this: Mocha.Suite) => Promise<void>;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
2020-03-29 13:43:52 +00:00
|
|
|
const tracker = new AsyncTracker();
|
2019-09-17 12:45:15 +00:00
|
|
|
|
|
|
|
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`;
|
2020-03-29 13:43:52 +00:00
|
|
|
if (isDebug()) {
|
2019-09-17 12:45:15 +00:00
|
|
|
throw new Error(msg);
|
|
|
|
} else {
|
2019-11-09 22:50:30 +00:00
|
|
|
/* eslint-disable-next-line no-console */
|
2019-09-17 12:45:15 +00:00
|
|
|
console.warn(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tracker.clear();
|
|
|
|
});
|
|
|
|
|
2020-03-29 13:43:52 +00:00
|
|
|
/* eslint-disable-next-line no-invalid-this */
|
2019-09-17 12:45:15 +00:00
|
|
|
const suite: PromiseLike<void> | undefined = cb.call(this);
|
|
|
|
if (isNil(suite) || !Reflect.has(suite, 'then')) {
|
2019-11-09 22:50:30 +00:00
|
|
|
/* eslint-disable-next-line no-console */
|
2019-09-17 12:45:15 +00:00
|
|
|
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<unknown>((res, rej) => {
|
2020-03-29 13:43:52 +00:00
|
|
|
/* eslint-disable-next-line no-invalid-this */
|
2019-09-17 12:45:15 +00:00
|
|
|
cb.call(this).then((value: unknown) => {
|
|
|
|
res(value);
|
|
|
|
}, (err: Error) => {
|
|
|
|
rej(err);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|