mirror of
https://github.com/hanzoai/Detox.git
synced 2026-09-24 16:55:12 +00:00
refactor: Detox reporter (#4214)
This commit is contained in:
committed by
GitHub
parent
1243c75aa0
commit
32a6051292
@@ -0,0 +1,18 @@
|
||||
const DetoxReporter = require('../jest/reporter');
|
||||
|
||||
test('DetoxReporter', () => {
|
||||
const reporter = new DetoxReporter({
|
||||
bail: 0,
|
||||
reporters: [['default', {}]],
|
||||
});
|
||||
|
||||
expect(reporter.getLastError).toBeInstanceOf(Function);
|
||||
expect(reporter.onRunStart).toBeInstanceOf(Function);
|
||||
expect(reporter.onTestFileStart).toBeInstanceOf(Function);
|
||||
expect(reporter.onTestStart).toBeInstanceOf(Function);
|
||||
expect(reporter.onTestCaseStart).toBeInstanceOf(Function);
|
||||
expect(reporter.onTestCaseResult).toBeInstanceOf(Function);
|
||||
expect(reporter.onTestFileResult).toBeInstanceOf(Function);
|
||||
expect(reporter.onTestResult).toBeInstanceOf(Function);
|
||||
expect(reporter.onRunComplete).toBeInstanceOf(Function);
|
||||
});
|
||||
@@ -1 +1,21 @@
|
||||
module.exports = require('./reporters/DetoxReporter');
|
||||
/** @typedef {import('@jest/reporters').Reporter} Reporter */
|
||||
|
||||
const {
|
||||
DetoxIPCReporter,
|
||||
DetoxReporterDispatcher,
|
||||
DetoxSummaryReporter,
|
||||
DetoxVerboseReporter,
|
||||
} = require('./reporters');
|
||||
|
||||
/** @implements {Reporter} */
|
||||
class DetoxReporter extends DetoxReporterDispatcher {
|
||||
constructor(globalConfig) {
|
||||
super(globalConfig, {
|
||||
DetoxSummaryReporter,
|
||||
DetoxVerboseReporter,
|
||||
DetoxIPCReporter,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DetoxReporter;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
const { config, reportTestResults } = require('../../../internals');
|
||||
|
||||
/** @typedef {import('@jest/reporters').Reporter} Reporter */
|
||||
|
||||
/** @implements {Partial<Reporter>} */
|
||||
class DetoxIPCReporter {
|
||||
/**
|
||||
* @param {Set<import('@jest/reporters').TestContext>} testContexts
|
||||
* @param {import('@jest/reporters').AggregatedResult} aggregatedResult
|
||||
*/
|
||||
async onRunComplete(testContexts, aggregatedResult) {
|
||||
const lostTests = aggregatedResult.numTotalTestSuites - aggregatedResult.testResults.length;
|
||||
|
||||
await reportTestResults(aggregatedResult.testResults.map(r => ({
|
||||
success: !r.failureMessage,
|
||||
testFilePath: r.testFilePath,
|
||||
testExecError: r.testExecError,
|
||||
isPermanentFailure: lostTests > 0 || this._isPermanentFailure(r),
|
||||
})));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@jest/test-result').TestResult} testResult
|
||||
*/
|
||||
_isPermanentFailure(testResult) {
|
||||
if (config.testRunner.jest.retryAfterCircusRetries) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return testResult.testResults.some(r => r.status === 'failed' && r.invocations > 1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DetoxIPCReporter;
|
||||
@@ -1,154 +0,0 @@
|
||||
const resolveFrom = require('resolve-from');
|
||||
/** @type {new (globalConfig: any) => import('@jest/reporters').VerboseReporter} */
|
||||
const JestVerboseReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).VerboseReporter;
|
||||
/** @type {new (globalConfig: any) => import('@jest/reporters').SummaryReporter} */
|
||||
const SummaryReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).SummaryReporter;
|
||||
|
||||
const { config, reportTestResults, unsafe_conductEarlyTeardown, cleanup } = require('../../../internals');
|
||||
const Deferred = require('../../../src/utils/Deferred');
|
||||
|
||||
class DetoxReporter extends JestVerboseReporter {
|
||||
constructor(globalConfig) {
|
||||
super(globalConfig);
|
||||
|
||||
/** @type {Deferred | null} */
|
||||
this._lastRunComplete = null;
|
||||
/** @type {Set<string>} */
|
||||
this._pendingTestFiles = new Set();
|
||||
/** @type {Promise<any> | null} */
|
||||
this._runCompletePromise = null;
|
||||
/** @type {import('@jest/reporters').SummaryReporter | null} */
|
||||
this._summaryReporter = this._initSummaryReporter();
|
||||
}
|
||||
|
||||
onRunStart(aggregatedResults, options) {
|
||||
super.onRunStart(aggregatedResults, options);
|
||||
|
||||
if (this._summaryReporter) {
|
||||
this._summaryReporter.onRunStart(aggregatedResults, options);
|
||||
}
|
||||
}
|
||||
|
||||
onTestFileStart(test) {
|
||||
this._pendingTestFiles.add(test.path);
|
||||
|
||||
// @ts-ignore Precaution in case Jest migrates to the new signature
|
||||
if (typeof super.onTestFileStart === 'function') {
|
||||
// @ts-ignore
|
||||
super.onTestFileStart(test);
|
||||
} else {
|
||||
super.onTestStart(test);
|
||||
}
|
||||
}
|
||||
|
||||
onTestFileResult(test, testResult, aggregatedResult) {
|
||||
this._pendingTestFiles.delete(test.path);
|
||||
|
||||
// @ts-ignore Precaution in case Jest migrates to the new signature
|
||||
if (typeof super.onTestFileResult === 'function') {
|
||||
// @ts-ignore
|
||||
super.onTestFileResult(test, testResult, aggregatedResult);
|
||||
} else {
|
||||
super.onTestResult(test, testResult, aggregatedResult);
|
||||
}
|
||||
|
||||
if (this._lastRunComplete && this._pendingTestFiles.size === 0) {
|
||||
this._lastRunComplete.resolve(aggregatedResult);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Set<import('@jest/reporters').TestContext>} testContexts
|
||||
* @param {import('@jest/reporters').AggregatedResult} aggregatedResult
|
||||
* @returns {Promise<any> | null}
|
||||
*/
|
||||
// @ts-ignore We need to use the complete signature, not the one from the base class
|
||||
onRunComplete(testContexts, aggregatedResult) {
|
||||
if (!this._runCompletePromise) {
|
||||
// Both `_lastRunComplete` and `_runCompletePromise` are used to prevent
|
||||
// a bug in Jest, where `onRunComplete` is called multiple times when
|
||||
// Jest runs with `--bail` and multiple workers, and `onRunComplete`
|
||||
// is implemented as an asynchronous long-running operation.
|
||||
this._lastRunComplete = this._pendingTestFiles.size === 0
|
||||
? Deferred.resolved(aggregatedResult)
|
||||
: new Deferred();
|
||||
|
||||
this._runCompletePromise = this._onRunComplete(testContexts, aggregatedResult).catch(err => {
|
||||
console.error(err);
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
|
||||
return this._runCompletePromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Set<import('@jest/reporters').TestContext>} testContexts
|
||||
* @param {import('@jest/reporters').AggregatedResult} aggregatedResult
|
||||
* @returns {Promise<any> | null}
|
||||
*/
|
||||
async _onRunComplete(testContexts, { numFailedTests }) {
|
||||
const bail = this._globalConfig.bail;
|
||||
const earlyTeardown = bail > 0 && numFailedTests >= bail;
|
||||
if (earlyTeardown) {
|
||||
await unsafe_conductEarlyTeardown();
|
||||
}
|
||||
|
||||
const aggregatedResult = await this._lastRunComplete.promise;
|
||||
const lostTests = aggregatedResult.numTotalTestSuites - aggregatedResult.testResults.length;
|
||||
|
||||
// @ts-expect-error TS2554
|
||||
super.onRunComplete(testContexts, aggregatedResult);
|
||||
|
||||
if (earlyTeardown && lostTests > 0 && config.testRunner.retries > 0) {
|
||||
console.warn(
|
||||
'Jest aborted the test execution before all scheduled test files have been reported.\n' +
|
||||
'This brings us to a dilemma: retry the whole test run, or retry only known failed test files.\n' +
|
||||
'Both options are bad in their own way, this is why we have decided to not support this edge case.\n' +
|
||||
'If you want to retry the whole test run, please disable Jest\'s --bail option.'
|
||||
);
|
||||
}
|
||||
|
||||
await reportTestResults(aggregatedResult.testResults.map(r => ({
|
||||
success: !r.failureMessage,
|
||||
testFilePath: r.testFilePath,
|
||||
testExecError: r.testExecError,
|
||||
isPermanentFailure: lostTests > 0 || this._isPermanentFailure(r),
|
||||
})));
|
||||
|
||||
if (this._summaryReporter) {
|
||||
this._summaryReporter.onRunComplete(testContexts, aggregatedResult);
|
||||
}
|
||||
|
||||
if (earlyTeardown) {
|
||||
await cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {import('@jest/reporters').SummaryReporter | null}
|
||||
* @private
|
||||
*/
|
||||
_initSummaryReporter() {
|
||||
/** @type {(config: import('@jest/types').Config.ReporterConfig) => boolean} */
|
||||
const isSummaryReporter = (config) => config[0] === 'summary';
|
||||
if (this._globalConfig.reporters.some(isSummaryReporter)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SummaryReporter(this._globalConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('@jest/test-result').TestResult} testResult
|
||||
*/
|
||||
_isPermanentFailure(testResult) {
|
||||
if (config.testRunner.jest.retryAfterCircusRetries) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return testResult.testResults.some(r => r.status === 'failed' && r.invocations > 1);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DetoxReporter;
|
||||
@@ -0,0 +1,144 @@
|
||||
/** @typedef {import('@jest/reporters').Reporter} Reporter */
|
||||
|
||||
const { config, unsafe_conductEarlyTeardown } = require('../../../internals');
|
||||
const Deferred = require('../../../src/utils/Deferred');
|
||||
|
||||
/** @implements {Reporter} */
|
||||
class DetoxReporterDispatcher {
|
||||
/**
|
||||
* @param {import('@jest/types').Config.GlobalConfig} globalConfig
|
||||
* @param {Record<string, new (globalConfig: import('@jest/types').Config.GlobalConfig) => Partial<Reporter>>} reporters
|
||||
*/
|
||||
constructor(globalConfig, reporters) {
|
||||
this._bail = globalConfig.bail;
|
||||
/** @type {Deferred | null} */
|
||||
this._lastRunComplete = null;
|
||||
/** @type {Set<string>} */
|
||||
this._pendingTestFiles = new Set();
|
||||
/** @type {Promise<any> | null} */
|
||||
this._onRunCompletePromise = null;
|
||||
/** @type {Partial<Reporter>[]} */
|
||||
this._reporters = Object.values(reporters).map((Reporter) => new Reporter(globalConfig));
|
||||
}
|
||||
|
||||
getLastError() {
|
||||
for (const reporter of this._reporters) {
|
||||
let error = typeof reporter.getLastError === 'function'
|
||||
? reporter.getLastError()
|
||||
: undefined;
|
||||
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
onRunStart(aggregatedResult, options) {
|
||||
return this._dispatch('onRunStart', aggregatedResult, options);
|
||||
}
|
||||
|
||||
onTestFileStart(test) {
|
||||
this._pendingTestFiles.add(test.path);
|
||||
return this._dispatch(['onTestFileStart', 'onTestStart'], test);
|
||||
}
|
||||
|
||||
onTestStart(test) {
|
||||
// Legacy method
|
||||
return this.onTestFileStart(test);
|
||||
}
|
||||
|
||||
// NEW! Supported only since Jest 29.6.0
|
||||
onTestCaseStart(test, testCaseStartInfo) {
|
||||
return this._dispatch('onTestCaseStart', test, testCaseStartInfo);
|
||||
}
|
||||
|
||||
onTestCaseResult(test, testCaseResult) {
|
||||
return this._dispatch('onTestCaseResult', test, testCaseResult);
|
||||
}
|
||||
|
||||
async onTestFileResult(test, testResult, aggregatedResult) {
|
||||
this._pendingTestFiles.delete(test.path);
|
||||
|
||||
await this._dispatch(['onTestFileResult', 'onTestResult'], test, testResult, aggregatedResult);
|
||||
|
||||
if (this._lastRunComplete && this._pendingTestFiles.size === 0) {
|
||||
this._lastRunComplete.resolve(aggregatedResult);
|
||||
}
|
||||
}
|
||||
|
||||
onTestResult(test, testResult, aggregatedResult) {
|
||||
// Legacy method
|
||||
return this.onTestFileResult(test, testResult, aggregatedResult);
|
||||
}
|
||||
|
||||
onRunComplete(testContexts, aggregatedResult) {
|
||||
if (!this._lastRunComplete) {
|
||||
// Both `_lastRunComplete` and `_onRunCompletePromise` are used to prevent
|
||||
// a bug in Jest, where `onRunComplete` is called multiple times when
|
||||
// Jest runs with `--bail` and multiple workers, and `onRunComplete`
|
||||
// is implemented as an asynchronous long-running operation.
|
||||
this._lastRunComplete = this._pendingTestFiles.size === 0
|
||||
? Deferred.resolved(aggregatedResult)
|
||||
: new Deferred();
|
||||
}
|
||||
|
||||
if (!this._onRunCompletePromise) {
|
||||
this._onRunCompletePromise = this._doRunComplete(testContexts, aggregatedResult);
|
||||
}
|
||||
|
||||
return this._onRunCompletePromise;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Set<import('@jest/reporters').TestContext>} testContexts
|
||||
* @param {import('@jest/test-result').AggregatedResult} aggregatedResult
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async _doRunComplete(testContexts, aggregatedResult) {
|
||||
const earlyTeardown = this._bail > 0 && aggregatedResult.numFailedTests >= this._bail;
|
||||
if (earlyTeardown) {
|
||||
const lostTests = aggregatedResult.numTotalTestSuites - aggregatedResult.testResults.length;
|
||||
if (lostTests > 0 && config.testRunner.retries > 0) {
|
||||
console.warn(
|
||||
'Jest aborted the test execution before all scheduled test files have been reported.\n' +
|
||||
'If you want to retry the whole test run, please disable Jest\'s --bail option.'
|
||||
);
|
||||
}
|
||||
|
||||
await unsafe_conductEarlyTeardown();
|
||||
}
|
||||
|
||||
await this._lastRunComplete.promise;
|
||||
await this._dispatch('onRunComplete', testContexts, aggregatedResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {string | string[]} rawMethodNames
|
||||
* @param {...any} args
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
_dispatch(rawMethodNames, ...args) {
|
||||
const methodNames = Array.isArray(rawMethodNames)
|
||||
? rawMethodNames
|
||||
: [rawMethodNames];
|
||||
|
||||
const maybePromises = [];
|
||||
|
||||
for (const reporter of this._reporters) {
|
||||
for (const methodName of methodNames) {
|
||||
if (typeof reporter[methodName] === 'function') {
|
||||
maybePromises.push(reporter[methodName](...args));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.all(maybePromises).then(() => void 0);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DetoxReporterDispatcher;
|
||||
@@ -0,0 +1,157 @@
|
||||
jest.mock('../../../internals', () => ({
|
||||
config: { testRunner: {} },
|
||||
unsafe_conductEarlyTeardown: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('DetoxReporterDispatcher', () => {
|
||||
/** @type {import('./DetoxReporterDispatcher')} */
|
||||
let dispatcher;
|
||||
/** @type {any} */
|
||||
let mockGlobalConfig;
|
||||
/** @type {LegacyReporter} */
|
||||
let legacyReporter;
|
||||
/** @type {ModernReporter} */
|
||||
let modernReporter;
|
||||
|
||||
class LegacyReporter {
|
||||
constructor(globalConfig) {
|
||||
if (globalConfig) {
|
||||
legacyReporter = this;
|
||||
}
|
||||
}
|
||||
|
||||
getLastError = jest.fn();
|
||||
onRunStart = jest.fn();
|
||||
onTestStart = jest.fn();
|
||||
onTestCaseStart = jest.fn();
|
||||
onTestCaseResult = jest.fn();
|
||||
onTestResult = jest.fn();
|
||||
onRunComplete = jest.fn();
|
||||
}
|
||||
|
||||
class ModernReporter extends LegacyReporter {
|
||||
constructor() {
|
||||
super(null);
|
||||
modernReporter = this;
|
||||
}
|
||||
|
||||
onTestFileStart = jest.fn();
|
||||
onTestFileResult = jest.fn();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockGlobalConfig = { bail: 2 };
|
||||
|
||||
const DetoxReporterDispatcher = require('./DetoxReporterDispatcher');
|
||||
dispatcher = new DetoxReporterDispatcher(mockGlobalConfig, {
|
||||
LegacyReporter,
|
||||
ModernReporter,
|
||||
});
|
||||
});
|
||||
|
||||
it('should instantiate reporters', () => {
|
||||
expect(legacyReporter).toBeInstanceOf(LegacyReporter);
|
||||
expect(modernReporter).toBeInstanceOf(ModernReporter);
|
||||
});
|
||||
|
||||
it('should return first error from getLastError method', () => {
|
||||
const [error1, error2] = [new Error('1'), new Error('2')];
|
||||
legacyReporter.getLastError.mockReturnValue(error1);
|
||||
modernReporter.getLastError.mockReturnValue(error2);
|
||||
expect(dispatcher.getLastError()).toBe(error1);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['onRunStart'],
|
||||
['onTestCaseStart'],
|
||||
['onTestCaseResult'],
|
||||
['onRunComplete'],
|
||||
])('should dispatch %s calls to all reporters', async (methodName) => {
|
||||
const args = [{ path: 'test' }, { arg: 2 }, { arg: 3 }].slice(0, dispatcher[methodName].length);
|
||||
await dispatcher[methodName](...args);
|
||||
expect(legacyReporter[methodName]).toHaveBeenCalledWith(...args);
|
||||
expect(modernReporter[methodName]).toHaveBeenCalledWith(...args);
|
||||
});
|
||||
|
||||
it.each([
|
||||
['onTestFileStart', 'onTestStart'],
|
||||
['onTestFileResult', 'onTestResult'],
|
||||
])('should dispatch %s/%s calls to all reporters', async (modernMethod, legacyMethod) => {
|
||||
const args = [{ path: 'test' }, { arg: 2 }, { arg: 3 }].slice(0, dispatcher[modernMethod].length);
|
||||
|
||||
for (const methodName of [modernMethod, legacyMethod]) {
|
||||
await dispatcher[methodName](...args);
|
||||
|
||||
expect(legacyReporter[legacyMethod]).toHaveBeenCalledWith(...args);
|
||||
expect(modernReporter[modernMethod]).toHaveBeenCalledWith(...args);
|
||||
expect(modernReporter[legacyMethod]).not.toHaveBeenCalled();
|
||||
|
||||
legacyReporter[legacyMethod].mockClear();
|
||||
modernReporter[modernMethod].mockClear();
|
||||
}
|
||||
});
|
||||
|
||||
describe('onRunComplete', () => {
|
||||
let unsafe_conductEarlyTeardown;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(console, 'warn');
|
||||
unsafe_conductEarlyTeardown = require('../../../internals').unsafe_conductEarlyTeardown;
|
||||
});
|
||||
|
||||
describe('upon a regular exit', () => {
|
||||
const testContexts = new Set();
|
||||
const aggregatedResult = { numFailedTests: 1, numTotalSuites: 0, testResults: [] };
|
||||
|
||||
it('should not call unsafe cleanup on regular exit', async () => {
|
||||
await dispatcher.onRunComplete(testContexts, aggregatedResult);
|
||||
expect(unsafe_conductEarlyTeardown).not.toHaveBeenCalled();
|
||||
expect(console.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can be called only once per reporter lifecycle', async () => {
|
||||
const promise1 = dispatcher.onRunComplete(testContexts, aggregatedResult);
|
||||
const promise2 = dispatcher.onRunComplete(testContexts, aggregatedResult);
|
||||
|
||||
expect(promise1).toBe(promise2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('upon an early exit', () => {
|
||||
const testContexts = new Set();
|
||||
let aggregatedResult;
|
||||
let config;
|
||||
|
||||
beforeEach(() => {
|
||||
// 2 > 1 (numFailedTests > bail)
|
||||
aggregatedResult = { numFailedTests: 2, numTotalTestSuites: 1, testResults: [] };
|
||||
config = require('../../../internals').config;
|
||||
});
|
||||
|
||||
it('should call unsafe cleanup on early exit', async () => {
|
||||
await dispatcher.onRunComplete(testContexts, aggregatedResult);
|
||||
|
||||
expect(unsafe_conductEarlyTeardown).toHaveBeenCalled();
|
||||
expect(console.warn).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should wait for all test files to finish', async () => {
|
||||
await dispatcher.onTestFileStart({ path: 'a' });
|
||||
const runCompletePromise = dispatcher.onRunComplete(testContexts, aggregatedResult);
|
||||
const timeoutPromise = new Promise((resolve) => setTimeout(resolve, 100, 'timeout'));
|
||||
|
||||
await expect(Promise.race([runCompletePromise, timeoutPromise])).resolves.toBe('timeout');
|
||||
await dispatcher.onTestFileResult({ path: 'a' }, {});
|
||||
await expect(runCompletePromise).resolves.toBe(undefined);
|
||||
});
|
||||
|
||||
it('should warn about mismatching number of failed test suites if -R, --retries > 0', async () => {
|
||||
config.testRunner.retries = 1;
|
||||
|
||||
await dispatcher.onRunComplete(testContexts, aggregatedResult);
|
||||
expect(unsafe_conductEarlyTeardown).toHaveBeenCalled();
|
||||
expect(console.warn).toHaveBeenCalledWith(expect.stringContaining('aborted'));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
/** @typedef {import('@jest/reporters').Reporter} Reporter */
|
||||
|
||||
const resolveFrom = require('resolve-from');
|
||||
/** @type {new (globalConfig: any) => import('@jest/reporters').SummaryReporter} */
|
||||
const SummaryReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).SummaryReporter;
|
||||
|
||||
/** @implements {Partial<Reporter>} */
|
||||
class DetoxSummaryReporter {
|
||||
constructor(globalConfig) {
|
||||
if (globalConfig.reporters.every(([name]) => name !== 'default' && name !== 'summary')) {
|
||||
return new SummaryReporter(globalConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DetoxSummaryReporter;
|
||||
@@ -0,0 +1,16 @@
|
||||
/** @typedef {import('@jest/reporters').Reporter} Reporter */
|
||||
|
||||
const resolveFrom = require('resolve-from');
|
||||
/** @type {new (globalConfig: any) => import('@jest/reporters').VerboseReporter} */
|
||||
const VerboseReporter = require(resolveFrom(process.cwd(), '@jest/reporters')).VerboseReporter;
|
||||
|
||||
/** @implements {Partial<Reporter>} */
|
||||
class DetoxVerboseReporter {
|
||||
constructor(globalConfig) {
|
||||
if (globalConfig.reporters.every(([name]) => name !== 'default')) {
|
||||
return new VerboseReporter(globalConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DetoxVerboseReporter;
|
||||
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
DetoxIPCReporter: require('./DetoxIPCReporter'),
|
||||
DetoxReporterDispatcher: require('./DetoxReporterDispatcher'),
|
||||
DetoxSummaryReporter: require('./DetoxSummaryReporter'),
|
||||
DetoxVerboseReporter: require('./DetoxVerboseReporter'),
|
||||
};
|
||||
Reference in New Issue
Block a user