[devtools][tests] add support for @gate pragma (#28479)
Adds support for `// @gate` pragma in devtools tests Adds a couple gate test assertions to ensure they continue to work
Josh Story committed
Mar 1, 2024 at 13:26 UTC
30ae0baed1cfe670dea41d7c7abdc375c9a4859a
3 files changed
+96
.eslintrc.js
+7
@@ -381,6 +381,13 @@ module.exports = {
381
'jest/valid-expect-in-promise': ERROR,
382
},
383
},
384
+ {
385
+ // disable no focused tests for test setup helper files even if they are inside __tests__ directory
386
+ files: ['**/setupTests.js'],
387
+ rules: {
388
+ 'jest/no-focused-tests': OFF,
389
+ },
390
+ },
391
{
392
files: [
393
'**/__tests__/**/*.js',
packages/react-devtools-shared/src/__tests__/gate-test.js
new
+18
@@ -0,0 +1,18 @@
1
+/**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ */
9
+
10
+describe('gate', () => {
11
+ //@gate false
12
+ it('should expect an error for this test', () => {
13
+ throw new Error('This test should fail');
14
+ });
15
+
16
+ //@gate true
17
+ it('should not an error for this test', () => {});
18
+});
packages/react-devtools-shared/src/__tests__/setupTests.js
+71
@@ -13,6 +13,7 @@ import type {
13
BackendBridge,
14
FrontendBridge,
15
} from 'react-devtools-shared/src/bridge';
16
+const {getTestFlags} = require('../../../../scripts/jest/TestFlags');
17
18
// Argument is serialized when passed from jest-cli script through to setupTests.
19
const compactConsole = process.env.compactConsole === 'true';
@@ -32,6 +33,76 @@ if (compactConsole) {
33
global.console = new CustomConsole(process.stdout, process.stderr, formatter);
34
}
35
36
+const expectTestToFail = async (callback, error) => {
37
+ if (callback.length > 0) {
38
+ throw Error(
39
+ 'Gated test helpers do not support the `done` callback. Return a ' +
40
+ 'promise instead.',
41
+ );
42
+ }
43
+ try {
44
+ const maybePromise = callback();
45
+ if (
46
+ maybePromise !== undefined &&
47
+ maybePromise !== null &&
48
+ typeof maybePromise.then === 'function'
49
+ ) {
50
+ await maybePromise;
51
+ }
52
+ } catch (testError) {
53
+ return;
54
+ }
55
+ throw error;
56
+};
57
+
58
+const gatedErrorMessage = 'Gated test was expected to fail, but it passed.';
59
+global._test_gate = (gateFn, testName, callback) => {
60
+ let shouldPass;
61
+ try {
62
+ const flags = getTestFlags();
63
+ shouldPass = gateFn(flags);
64
+ } catch (e) {
65
+ test(testName, () => {
66
+ throw e;
67
+ });
68
+ return;
69
+ }
70
+ if (shouldPass) {
71
+ test(testName, callback);
72
+ } else {
73
+ const error = new Error(gatedErrorMessage);
74
+ Error.captureStackTrace(error, global._test_gate);
75
+ test(`[GATED, SHOULD FAIL] ${testName}`, () =>
76
+ expectTestToFail(callback, error));
77
+ }
78
+};
79
+global._test_gate_focus = (gateFn, testName, callback) => {
80
+ let shouldPass;
81
+ try {
82
+ const flags = getTestFlags();
83
+ shouldPass = gateFn(flags);
84
+ } catch (e) {
85
+ test.only(testName, () => {
86
+ throw e;
87
+ });
88
+ return;
89
+ }
90
+ if (shouldPass) {
91
+ test.only(testName, callback);
92
+ } else {
93
+ const error = new Error(gatedErrorMessage);
94
+ Error.captureStackTrace(error, global._test_gate_focus);
95
+ test.only(`[GATED, SHOULD FAIL] ${testName}`, () =>
96
+ expectTestToFail(callback, error));
97
+ }
98
+};
99
+
100
+// Dynamic version of @gate pragma
101
+global.gate = fn => {
102
+ const flags = getTestFlags();
103
+ return fn(flags);
104
+};
105
+
106
beforeEach(() => {
107
global.mockClipboardCopy = jest.fn();
108