From ad6b1c16975a5418afc5880d7c9da61928ca5cb4 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Tue, 19 Mar 2024 19:40:28 -0400 Subject: [PATCH] Update gate pragma to detect global error events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a global error event is dispatched during a test, Jest reports that test as a failure. Our `@gate` pragma feature should account for this — if the gate condition is false, and the global error event is dispatched, then the test should be reported as a success. The solution is to install an error event handler right before invoking the test function. Because we install our own handler, Jest will not report the test as a failure if a global error event is dispatched; it's conceptually as if we wrapped the whole test event in a try-catch. --- .../transform-test-gate-pragma-test.js | 9 +++++ scripts/jest/setupTests.js | 36 ++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/scripts/babel/__tests__/transform-test-gate-pragma-test.js b/scripts/babel/__tests__/transform-test-gate-pragma-test.js index 1970ab4c1c2c2..e4b76dd05e859 100644 --- a/scripts/babel/__tests__/transform-test-gate-pragma-test.js +++ b/scripts/babel/__tests__/transform-test-gate-pragma-test.js @@ -203,6 +203,15 @@ describe('transform test-gate-pragma: actual runtime', () => { console.error('Stop that!'); throw Error('I told you to stop!'); }); + + // @gate false + test('a global error event is treated as a test failure', () => { + dispatchEvent( + new ErrorEvent('error', { + error: new Error('Oops!'), + }) + ); + }); }); describe('dynamic gate method', () => { diff --git a/scripts/jest/setupTests.js b/scripts/jest/setupTests.js index 0d23861568fe4..14864264c9092 100644 --- a/scripts/jest/setupTests.js +++ b/scripts/jest/setupTests.js @@ -241,13 +241,30 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { global.Error = ErrorProxy; } - const expectTestToFail = async (callback, error) => { + const expectTestToFail = async (callback, errorToThrowIfTestSucceeds) => { if (callback.length > 0) { throw Error( 'Gated test helpers do not support the `done` callback. Return a ' + 'promise instead.' ); } + + // Install a global error event handler. We treat global error events as + // test failures, same as Jest's default behavior. + // + // Becaused we installed our own error event handler, Jest will not report a + // test failure. Conceptually it's as if we wrapped the entire test event in + // a try-catch. + let didError = false; + const errorEventHandler = () => { + didError = true; + }; + // eslint-disable-next-line no-restricted-globals + if (typeof addEventListener === 'function') { + // eslint-disable-next-line no-restricted-globals + addEventListener('error', errorEventHandler); + } + try { const maybePromise = callback(); if ( @@ -262,11 +279,20 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) { // throws, we won't have captured it. flushAllUnexpectedConsoleCalls(); } catch (testError) { - // Failed as expected - resetAllUnexpectedConsoleCalls(); - return; + didError = true; + } + resetAllUnexpectedConsoleCalls(); + // eslint-disable-next-line no-restricted-globals + if (typeof removeEventListener === 'function') { + // eslint-disable-next-line no-restricted-globals + removeEventListener('error', errorEventHandler); + } + + if (!didError) { + // The test did not error like we expected it to. Report this to Jest as + // a failure. + throw errorToThrowIfTestSucceeds; } - throw error; }; const gatedErrorMessage = 'Gated test was expected to fail, but it passed.';