Add xplat test variants (#29734)
## Overview We didn't have any tests that ran in persistent mode with the xplat feature flags (for either variant). As a result, invalid test gating like in https://github.com/facebook/react/pull/29664 were not caught. This PR adds test flavors for `ReactFeatureFlag-native-fb.js` in both variants.
Ricky committed
Jun 4, 2024 at 13:07 UTC
eabb681535ab9582c0785049c5a16f8851430ff2
19 files changed
+247
-144
.circleci/config.yml
+10
@@ -507,6 +507,10 @@ workflows:
507
- "-r=www-modern --env=production --variant=false"
508
- "-r=www-modern --env=development --variant=true"
509
- "-r=www-modern --env=production --variant=true"
510
+ - "-r=xplat --env=development --variant=false"
511
+ - "-r=xplat --env=development --variant=true"
512
+ - "-r=xplat --env=production --variant=false"
513
+ - "-r=xplat --env=production --variant=true"
514
515
# TODO: Test more persistent configurations?
516
- '-r=stable --env=development --persistent'
@@ -552,6 +556,12 @@ workflows:
556
# - "-r=www-modern --env=development --variant=true"
557
# - "-r=www-modern --env=production --variant=true"
558
559
+ # TODO: Update test config to support xplat build tests
560
+ # - "-r=xplat --env=development --variant=false"
561
+ # - "-r=xplat --env=development --variant=true"
562
+ # - "-r=xplat --env=production --variant=false"
563
+ # - "-r=xplat --env=production --variant=true"
564
+
565
# TODO: Test more persistent configurations?
566
- download_base_build_for_sizebot:
567
filters:
packages/react-native-renderer/src/__tests__/ResponderEventPlugin-test.internal.js
+109
-104
@@ -1377,113 +1377,118 @@ describe('ResponderEventPlugin', () => {
1377
expect(ResponderEventPlugin._getResponder()).toBe(null);
1378
});
1379
1380
- it('should determine the first common ancestor correctly', async () => {
1381
- // This test was moved here from the ReactTreeTraversal test since only the
1382
- // ResponderEventPlugin uses `getLowestCommonAncestor`
1383
- const React = require('react');
1384
- const ReactDOMClient = require('react-dom/client');
1385
- const act = require('internal-test-utils').act;
1386
- const getLowestCommonAncestor =
1387
- require('react-native-renderer/src/legacy-events/ResponderEventPlugin').getLowestCommonAncestor;
1388
- // This works by accident and will likely break in the future.
1389
- const ReactDOMComponentTree = require('react-dom-bindings/src/client/ReactDOMComponentTree');
1390
-
1391
- class ChildComponent extends React.Component {
1392
- divRef = React.createRef();
1393
- div1Ref = React.createRef();
1394
- div2Ref = React.createRef();
1395
-
1396
- render() {
1397
- return (
1398
- <div ref={this.divRef} id={this.props.id + '__DIV'}>
1399
- <div ref={this.div1Ref} id={this.props.id + '__DIV_1'} />
1400
- <div ref={this.div2Ref} id={this.props.id + '__DIV_2'} />
1401
- </div>
1402
- );
1380
+ it(
1381
+ 'should determine the first common ancestor correctly',
1382
+ async () => {
1383
+ // This test was moved here from the ReactTreeTraversal test since only the
1384
+ // ResponderEventPlugin uses `getLowestCommonAncestor`
1385
+ const React = require('react');
1386
+ const ReactDOMClient = require('react-dom/client');
1387
+ const act = require('internal-test-utils').act;
1388
+ const getLowestCommonAncestor =
1389
+ require('react-native-renderer/src/legacy-events/ResponderEventPlugin').getLowestCommonAncestor;
1390
+ // This works by accident and will likely break in the future.
1391
+ const ReactDOMComponentTree = require('react-dom-bindings/src/client/ReactDOMComponentTree');
1392
+
1393
+ class ChildComponent extends React.Component {
1394
+ divRef = React.createRef();
1395
+ div1Ref = React.createRef();
1396
+ div2Ref = React.createRef();
1397
+
1398
+ render() {
1399
+ return (
1400
+ <div ref={this.divRef} id={this.props.id + '__DIV'}>
1401
+ <div ref={this.div1Ref} id={this.props.id + '__DIV_1'} />
1402
+ <div ref={this.div2Ref} id={this.props.id + '__DIV_2'} />
1403
+ </div>
1404
+ );
1405
+ }
1406
}
1404
- }
1407
1406
- class ParentComponent extends React.Component {
1407
- pRef = React.createRef();
1408
- p_P1Ref = React.createRef();
1409
- p_P1_C1Ref = React.createRef();
1410
- p_P1_C2Ref = React.createRef();
1411
- p_OneOffRef = React.createRef();
1412
-
1413
- render() {
1414
- return (
1415
- <div ref={this.pRef} id="P">
1416
- <div ref={this.p_P1Ref} id="P_P1">
1417
- <ChildComponent ref={this.p_P1_C1Ref} id="P_P1_C1" />
1418
- <ChildComponent ref={this.p_P1_C2Ref} id="P_P1_C2" />
1408
+ class ParentComponent extends React.Component {
1409
+ pRef = React.createRef();
1410
+ p_P1Ref = React.createRef();
1411
+ p_P1_C1Ref = React.createRef();
1412
+ p_P1_C2Ref = React.createRef();
1413
+ p_OneOffRef = React.createRef();
1414
+
1415
+ render() {
1416
+ return (
1417
+ <div ref={this.pRef} id="P">
1418
+ <div ref={this.p_P1Ref} id="P_P1">
1419
+ <ChildComponent ref={this.p_P1_C1Ref} id="P_P1_C1" />
1420
+ <ChildComponent ref={this.p_P1_C2Ref} id="P_P1_C2" />
1421
+ </div>
1422
+ <div ref={this.p_OneOffRef} id="P_OneOff" />
1423
</div>
1420
- <div ref={this.p_OneOffRef} id="P_OneOff" />
1421
- </div>
1422
- );
1424
+ );
1425
+ }
1426
}
1424
- }
1425
-
1426
- const container = document.createElement('div');
1427
- const root = ReactDOMClient.createRoot(container);
1428
- let parent;
1429
- await act(() => {
1430
- root.render(<ParentComponent ref={current => (parent = current)} />);
1431
- });
1427
1433
- const ancestors = [
1434
- // Common ancestor with self is self.
1435
- {
1436
- one: parent.p_P1_C1Ref.current.div1Ref.current,
1437
- two: parent.p_P1_C1Ref.current.div1Ref.current,
1438
- com: parent.p_P1_C1Ref.current.div1Ref.current,
1439
- },
1440
- // Common ancestor with self is self - even if topmost DOM.
1441
- {
1442
- one: parent.pRef.current,
1443
- two: parent.pRef.current,
1444
- com: parent.pRef.current,
1445
- },
1446
- // Siblings
1447
- {
1448
- one: parent.p_P1_C1Ref.current.div1Ref.current,
1449
- two: parent.p_P1_C1Ref.current.div2Ref.current,
1450
- com: parent.p_P1_C1Ref.current.divRef.current,
1451
- },
1452
- // Common ancestor with parent is the parent.
1453
- {
1454
- one: parent.p_P1_C1Ref.current.div1Ref.current,
1455
- two: parent.p_P1_C1Ref.current.divRef.current,
1456
- com: parent.p_P1_C1Ref.current.divRef.current,
1457
- },
1458
- // Common ancestor with grandparent is the grandparent.
1459
- {
1460
- one: parent.p_P1_C1Ref.current.div1Ref.current,
1461
- two: parent.p_P1Ref.current,
1462
- com: parent.p_P1Ref.current,
1463
- },
1464
- // Grandparent across subcomponent boundaries.
1465
- {
1466
- one: parent.p_P1_C1Ref.current.div1Ref.current,
1467
- two: parent.p_P1_C2Ref.current.div1Ref.current,
1468
- com: parent.p_P1Ref.current,
1469
- },
1470
- // Something deep with something one-off.
1471
- {
1472
- one: parent.p_P1_C1Ref.current.div1Ref.current,
1473
- two: parent.p_OneOffRef.current,
1474
- com: parent.pRef.current,
1475
- },
1476
- ];
1477
- let i;
1478
- for (i = 0; i < ancestors.length; i++) {
1479
- const plan = ancestors[i];
1480
- const firstCommon = getLowestCommonAncestor(
1481
- ReactDOMComponentTree.getInstanceFromNode(plan.one),
1482
- ReactDOMComponentTree.getInstanceFromNode(plan.two),
1483
- );
1484
- expect(firstCommon).toBe(
1485
- ReactDOMComponentTree.getInstanceFromNode(plan.com),
1486
- );
1487
- }
1488
- });
1428
+ const container = document.createElement('div');
1429
+ const root = ReactDOMClient.createRoot(container);
1430
+ let parent;
1431
+ await act(() => {
1432
+ root.render(<ParentComponent ref={current => (parent = current)} />);
1433
+ });
1434
+
1435
+ const ancestors = [
1436
+ // Common ancestor with self is self.
1437
+ {
1438
+ one: parent.p_P1_C1Ref.current.div1Ref.current,
1439
+ two: parent.p_P1_C1Ref.current.div1Ref.current,
1440
+ com: parent.p_P1_C1Ref.current.div1Ref.current,
1441
+ },
1442
+ // Common ancestor with self is self - even if topmost DOM.
1443
+ {
1444
+ one: parent.pRef.current,
1445
+ two: parent.pRef.current,
1446
+ com: parent.pRef.current,
1447
+ },
1448
+ // Siblings
1449
+ {
1450
+ one: parent.p_P1_C1Ref.current.div1Ref.current,
1451
+ two: parent.p_P1_C1Ref.current.div2Ref.current,
1452
+ com: parent.p_P1_C1Ref.current.divRef.current,
1453
+ },
1454
+ // Common ancestor with parent is the parent.
1455
+ {
1456
+ one: parent.p_P1_C1Ref.current.div1Ref.current,
1457
+ two: parent.p_P1_C1Ref.current.divRef.current,
1458
+ com: parent.p_P1_C1Ref.current.divRef.current,
1459
+ },
1460
+ // Common ancestor with grandparent is the grandparent.
1461
+ {
1462
+ one: parent.p_P1_C1Ref.current.div1Ref.current,
1463
+ two: parent.p_P1Ref.current,
1464
+ com: parent.p_P1Ref.current,
1465
+ },
1466
+ // Grandparent across subcomponent boundaries.
1467
+ {
1468
+ one: parent.p_P1_C1Ref.current.div1Ref.current,
1469
+ two: parent.p_P1_C2Ref.current.div1Ref.current,
1470
+ com: parent.p_P1Ref.current,
1471
+ },
1472
+ // Something deep with something one-off.
1473
+ {
1474
+ one: parent.p_P1_C1Ref.current.div1Ref.current,
1475
+ two: parent.p_OneOffRef.current,
1476
+ com: parent.pRef.current,
1477
+ },
1478
+ ];
1479
+ let i;
1480
+ for (i = 0; i < ancestors.length; i++) {
1481
+ const plan = ancestors[i];
1482
+ const firstCommon = getLowestCommonAncestor(
1483
+ ReactDOMComponentTree.getInstanceFromNode(plan.one),
1484
+ ReactDOMComponentTree.getInstanceFromNode(plan.two),
1485
+ );
1486
+ expect(firstCommon).toBe(
1487
+ ReactDOMComponentTree.getInstanceFromNode(plan.com),
1488
+ );
1489
+ }
1490
+ },
1491
+ // TODO: this is a long running test, we should speed it up.
1492
+ 60 * 1000,
1493
+ );
1494
});
packages/react-reconciler/src/__tests__/Activity-test.js
+2
-2
@@ -118,7 +118,7 @@ describe('Activity', () => {
118
);
119
});
120
121
- // @gate www && !disableLegacyMode
121
+ // @gate enableLegacyHidden && !disableLegacyMode
122
it('does not defer in legacy mode', async () => {
123
let setState;
124
function Foo() {
@@ -163,7 +163,7 @@ describe('Activity', () => {
163
);
164
});
165
166
- // @gate www
166
+ // @gate enableLegacyHidden
167
it('does defer in concurrent mode', async () => {
168
let setState;
169
function Foo() {
packages/react-reconciler/src/__tests__/ActivitySuspense-test.js
+4
-4
@@ -140,7 +140,7 @@ describe('Activity Suspense', () => {
140
);
141
});
142
143
- // @gate www
143
+ // @gate enableLegacyHidden
144
test('LegacyHidden does not handle suspense', async () => {
145
const root = ReactNoop.createRoot();
146
@@ -174,7 +174,7 @@ describe('Activity Suspense', () => {
174
);
175
});
176
177
- // @gate experimental || www
177
+ // @gate enableActivity
178
test("suspending inside currently hidden tree that's switching to visible", async () => {
179
const root = ReactNoop.createRoot();
180
@@ -319,7 +319,7 @@ describe('Activity Suspense', () => {
319
);
320
});
321
322
- // @gate experimental || www
322
+ // @gate enableActivity
323
test('update that suspends inside hidden tree', async () => {
324
let setText;
325
function Child() {
@@ -352,7 +352,7 @@ describe('Activity Suspense', () => {
352
});
353
});
354
355
- // @gate experimental || www
355
+ // @gate enableActivity
356
test('updates at multiple priorities that suspend inside hidden tree', async () => {
357
let setText;
358
let setStep;
packages/react-reconciler/src/__tests__/ReactContextPropagation-test.js
+3
-3
@@ -550,7 +550,7 @@ describe('ReactLazyContextPropagation', () => {
550
expect(root).toMatchRenderedOutput('BB');
551
});
552
553
- // @gate www
553
+ // @gate enableLegacyCache && enableLegacyHidden
554
test('context is propagated through offscreen trees', async () => {
555
const LegacyHidden = React.unstable_LegacyHidden;
556
@@ -596,7 +596,7 @@ describe('ReactLazyContextPropagation', () => {
596
expect(root).toMatchRenderedOutput('BB');
597
});
598
599
- // @gate www
599
+ // @gate enableLegacyCache && enableLegacyHidden
600
test('multiple contexts are propagated across through offscreen trees', async () => {
601
// Same as previous test, but with multiple context providers
602
const LegacyHidden = React.unstable_LegacyHidden;
@@ -822,7 +822,7 @@ describe('ReactLazyContextPropagation', () => {
822
expect(root).toMatchRenderedOutput('BB');
823
});
824
825
- // @gate www
825
+ // @gate enableLegacyCache && enableLegacyHidden
826
test('nested bailouts through offscreen trees', async () => {
827
// Lazy context propagation will stop propagating when it hits the first
828
// match. If we bail out again inside that tree, we must resume propagating.
packages/react-reconciler/src/__tests__/ReactIncremental-test.js
+3
-4
@@ -239,7 +239,7 @@ describe('ReactIncremental', () => {
239
expect(inst.state).toEqual({text: 'bar', text2: 'baz'});
240
});
241
242
- // @gate www
242
+ // @gate enableLegacyHidden
243
it('can deprioritize unfinished work and resume it later', async () => {
244
function Bar(props) {
245
Scheduler.log('Bar');
@@ -279,7 +279,7 @@ describe('ReactIncremental', () => {
279
await waitForAll(['Middle', 'Middle']);
280
});
281
282
- // @gate www
282
+ // @gate enableLegacyHidden
283
it('can deprioritize a tree from without dropping work', async () => {
284
function Bar(props) {
285
Scheduler.log('Bar');
@@ -1864,8 +1864,7 @@ describe('ReactIncremental', () => {
1864
]);
1865
});
1866
1867
- // @gate www
1868
- // @gate !disableLegacyContext
1867
+ // @gate enableLegacyHidden && !disableLegacyContext
1868
it('provides context when reusing work', async () => {
1869
class Intl extends React.Component {
1870
static childContextTypes = {
packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js
+1
-1
@@ -289,7 +289,7 @@ describe('ReactIncrementalErrorHandling', () => {
289
);
290
});
291
292
- // @gate www
292
+ // @gate enableLegacyHidden
293
it('does not include offscreen work when retrying after an error', async () => {
294
function App(props) {
295
if (props.isBroken) {
packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js
+5
-5
@@ -481,7 +481,7 @@ describe('ReactIncrementalSideEffects', () => {
481
);
482
});
483
484
- // @gate www
484
+ // @gate enableLegacyHidden
485
it('preserves a previously rendered node when deprioritized', async () => {
486
function Middle(props) {
487
Scheduler.log('Middle');
@@ -530,7 +530,7 @@ describe('ReactIncrementalSideEffects', () => {
530
);
531
});
532
533
- // @gate www
533
+ // @gate enableLegacyHidden
534
it('can reuse side-effects after being preempted', async () => {
535
function Bar(props) {
536
Scheduler.log('Bar');
@@ -610,7 +610,7 @@ describe('ReactIncrementalSideEffects', () => {
610
);
611
});
612
613
- // @gate www
613
+ // @gate enableLegacyHidden
614
it('can reuse side-effects after being preempted, if shouldComponentUpdate is false', async () => {
615
class Bar extends React.Component {
616
shouldComponentUpdate(nextProps) {
@@ -733,7 +733,7 @@ describe('ReactIncrementalSideEffects', () => {
733
expect(ReactNoop.getChildrenAsJSX()).toEqual(<span prop={3} />);
734
});
735
736
- // @gate www
736
+ // @gate enableLegacyHidden
737
it('updates a child even though the old props is empty', async () => {
738
function Foo(props) {
739
return (
@@ -984,7 +984,7 @@ describe('ReactIncrementalSideEffects', () => {
984
expect(ops).toEqual(['Bar', 'Baz', 'Bar', 'Bar']);
985
});
986
987
- // @gate www
987
+ // @gate enableLegacyHidden
988
it('deprioritizes setStates that happens within a deprioritized tree', async () => {
989
const barInstances = [];
990
packages/react-reconciler/src/__tests__/ReactNewContext-test.js
+1
-1
@@ -699,7 +699,7 @@ describe('ReactNewContext', () => {
699
);
700
});
701
702
- // @gate www
702
+ // @gate enableLegacyHidden
703
it("context consumer doesn't bail out inside hidden subtree", async () => {
704
const Context = React.createContext('dark');
705
const Consumer = getConsumer(Context);
packages/react-reconciler/src/__tests__/ReactSchedulerIntegration-test.js
+1
-1
@@ -131,7 +131,7 @@ describe('ReactSchedulerIntegration', () => {
131
await waitForAll(['D', 'E']);
132
});
133
134
- // @gate www
134
+ // @gate enableLegacyHidden
135
it('idle updates are not blocked by offscreen work', async () => {
136
function Text({text}) {
137
Scheduler.log(text);
packages/react-reconciler/src/__tests__/ReactScope-test.internal.js
+10
-10
@@ -41,7 +41,7 @@ describe('ReactScope', () => {
41
container = null;
42
});
43
44
- // @gate www
44
+ // @gate enableScopeAPI
45
it('DO_NOT_USE_queryAllNodes() works as intended', async () => {
46
const testScopeQuery = (type, props) => true;
47
const TestScope = React.unstable_Scope;
@@ -86,7 +86,7 @@ describe('ReactScope', () => {
86
expect(scopeRef.current).toBe(null);
87
});
88
89
- // @gate www
89
+ // @gate enableScopeAPI
90
it('DO_NOT_USE_queryAllNodes() provides the correct host instance', async () => {
91
const testScopeQuery = (type, props) => type === 'div';
92
const TestScope = React.unstable_Scope;
@@ -143,7 +143,7 @@ describe('ReactScope', () => {
143
expect(scopeRef.current).toBe(null);
144
});
145
146
- // @gate www
146
+ // @gate enableScopeAPI
147
it('DO_NOT_USE_queryFirstNode() works as intended', async () => {
148
const testScopeQuery = (type, props) => true;
149
const TestScope = React.unstable_Scope;
@@ -188,7 +188,7 @@ describe('ReactScope', () => {
188
expect(scopeRef.current).toBe(null);
189
});
190
191
- // @gate www
191
+ // @gate enableScopeAPI
192
it('containsNode() works as intended', async () => {
193
const TestScope = React.unstable_Scope;
194
const scopeRef = React.createRef();
@@ -248,7 +248,7 @@ describe('ReactScope', () => {
248
expect(scopeRef.current.containsNode(emRef.current)).toBe(false);
249
});
250
251
- // @gate www
251
+ // @gate enableScopeAPI
252
it('scopes support server-side rendering and hydration', async () => {
253
const TestScope = React.unstable_Scope;
254
const scopeRef = React.createRef();
@@ -281,7 +281,7 @@ describe('ReactScope', () => {
281
expect(nodes).toEqual([divRef.current, spanRef.current, aRef.current]);
282
});
283
284
- // @gate www
284
+ // @gate enableScopeAPI
285
it('getChildContextValues() works as intended', async () => {
286
const TestContext = React.createContext();
287
const TestScope = React.unstable_Scope;
@@ -320,7 +320,7 @@ describe('ReactScope', () => {
320
expect(scopeRef.current).toBe(null);
321
});
322
323
- // @gate www
323
+ // @gate enableScopeAPI
324
it('correctly works with suspended boundaries that are hydrated', async () => {
325
let suspend = false;
326
let resolve;
@@ -392,7 +392,7 @@ describe('ReactScope', () => {
392
ReactTestRenderer = require('react-test-renderer');
393
});
394
395
- // @gate www
395
+ // @gate enableScopeAPI
396
it('DO_NOT_USE_queryAllNodes() works as intended', async () => {
397
const testScopeQuery = (type, props) => true;
398
const TestScope = React.unstable_Scope;
@@ -434,7 +434,7 @@ describe('ReactScope', () => {
434
expect(nodes).toEqual([aRef.current, divRef.current, spanRef.current]);
435
});
436
437
- // @gate www
437
+ // @gate enableScopeAPI
438
it('DO_NOT_USE_queryFirstNode() works as intended', async () => {
439
const testScopeQuery = (type, props) => true;
440
const TestScope = React.unstable_Scope;
@@ -477,7 +477,7 @@ describe('ReactScope', () => {
477
expect(node).toEqual(aRef.current);
478
});
479
480
- // @gate www
480
+ // @gate enableScopeAPI
481
it('containsNode() works as intended', async () => {
482
const TestScope = React.unstable_Scope;
483
const scopeRef = React.createRef();
packages/react-reconciler/src/__tests__/ReactSubtreeFlagsWarning-test.js
+1
-1
@@ -130,7 +130,7 @@ describe('ReactSuspenseWithNoopRenderer', () => {
130
131
const resolveText = resolveMostRecentTextCache;
132
133
- // @gate www && !disableLegacyMode
133
+ // @gate enableLegacyCache && !disableLegacyMode
134
it('regression: false positive for legacy suspense', async () => {
135
const Child = ({text}) => {
136
// If text hasn't resolved, this will throw and exit before the passive
packages/react-refresh/src/__tests__/ReactFresh-test.js
+1
-1
@@ -2441,7 +2441,7 @@ describe('ReactFresh', () => {
2441
}
2442
});
2443
2444
- // @gate www && __DEV__
2444
+ // @gate enableLegacyHidden && __DEV__
2445
it('can hot reload offscreen components', async () => {
2446
const AppV1 = prepare(() => {
2447
function Hello() {
packages/react/src/__tests__/ReactProfiler-test.internal.js
+11
@@ -170,6 +170,17 @@ describe(`onRender`, () => {
170
'read current time',
171
'read current time',
172
]);
173
+ } else if (gate(flags => !flags.allowConcurrentByDefault)) {
174
+ assertLog([
175
+ 'read current time',
176
+ 'read current time',
177
+ 'read current time',
178
+ 'read current time',
179
+ 'read current time',
180
+ 'read current time',
181
+ 'read current time',
182
+ // TODO: why is there one less in this case?
183
+ ]);
184
} else {
185
assertLog([
186
'read current time',
scripts/jest/TestFlags.js
+3
-2
@@ -60,6 +60,7 @@ function getTestFlags() {
60
const schedulerFeatureFlags = require('scheduler/src/SchedulerFeatureFlags');
61
62
const www = global.__WWW__ === true;
63
+ const xplat = global.__XPLAT__ === true;
64
const releaseChannel = www
65
? __EXPERIMENTAL__
66
? 'modern'
@@ -79,8 +80,8 @@ function getTestFlags() {
80
www,
81
82
// These aren't flags, just a useful aliases for tests.
82
- enableActivity: releaseChannel === 'experimental' || www,
83
- enableSuspenseList: releaseChannel === 'experimental' || www,
83
+ enableActivity: releaseChannel === 'experimental' || www || xplat,
84
+ enableSuspenseList: releaseChannel === 'experimental' || www || xplat,
85
enableLegacyHidden: www,
86
87
// This flag is used to determine whether we should run Fizz tests using
scripts/jest/config.source-xplat.js
new
+30
@@ -0,0 +1,30 @@
1
+'use strict';
2
+
3
+const baseConfig = require('./config.base');
4
+
5
+module.exports = Object.assign({}, baseConfig, {
6
+ modulePathIgnorePatterns: [
7
+ ...baseConfig.modulePathIgnorePatterns,
8
+ 'packages/react-devtools-extensions',
9
+ 'packages/react-devtools-shared',
10
+ 'ReactIncrementalPerf',
11
+ 'ReactIncrementalUpdatesMinimalism',
12
+ 'ReactIncrementalTriangle',
13
+ 'ReactIncrementalReflection',
14
+ 'forwardRef',
15
+ ],
16
+ // RN configs should not run react-dom tests.
17
+ // There are many other tests that use react-dom
18
+ // and for those we will use the www entrypoint,
19
+ // but those tests should be migrated to Noop renderer.
20
+ testPathIgnorePatterns: [
21
+ 'node_modules',
22
+ 'packages/react-dom',
23
+ 'packages/react-server-dom-webpack',
24
+ ],
25
+ setupFiles: [
26
+ ...baseConfig.setupFiles,
27
+ require.resolve('./setupTests.xplat.js'),
28
+ require.resolve('./setupHostConfigs.js'),
29
+ ],
30
+});
scripts/jest/jest-cli.js
+16
-2
@@ -9,6 +9,7 @@ const semver = require('semver');
9
10
const ossConfig = './scripts/jest/config.source.js';
11
const wwwConfig = './scripts/jest/config.source-www.js';
12
+const xplatConfig = './scripts/jest/config.source-xplat.js';
13
const devToolsConfig = './scripts/jest/config.build-devtools.js';
14
15
// TODO: These configs are separate but should be rolled into the configs above
@@ -46,7 +47,7 @@ const argv = yargs
47
requiresArg: true,
48
type: 'string',
49
default: 'experimental',
49
- choices: ['experimental', 'stable', 'www-classic', 'www-modern'],
50
+ choices: ['experimental', 'stable', 'www-classic', 'www-modern', 'xplat'],
51
},
52
env: {
53
alias: 'e',
@@ -124,6 +125,10 @@ function isWWWConfig() {
125
);
126
}
127
128
+function isXplatConfig() {
129
+ return argv.releaseChannel === 'xplat' && argv.project !== 'devtools';
130
+}
131
+
132
function isOSSConfig() {
133
return (
134
argv.releaseChannel === 'stable' || argv.releaseChannel === 'experimental'
@@ -189,7 +194,7 @@ function validateOptions() {
194
}
195
}
196
192
- if (isWWWConfig()) {
197
+ if (isWWWConfig() || isXplatConfig()) {
198
if (argv.variant === undefined) {
199
// Turn internal experiments on by default
200
argv.variant = true;
@@ -224,6 +229,13 @@ function validateOptions() {
229
success = false;
230
}
231
232
+ if (argv.build && isXplatConfig()) {
233
+ logError(
234
+ 'Build targets are only not supported for xplat release channels. Update these options to continue.'
235
+ );
236
+ success = false;
237
+ }
238
+
239
if (argv.env && argv.env !== 'production' && argv.prod) {
240
logError(
241
'Build type does not match --prod. Update these options to continue.'
@@ -277,6 +289,8 @@ function getCommandArgs() {
289
args.push(persistentConfig);
290
} else if (isWWWConfig()) {
291
args.push(wwwConfig);
292
+ } else if (isXplatConfig()) {
293
+ args.push(xplatConfig);
294
} else if (isOSSConfig()) {
295
args.push(ossConfig);
296
} else {
scripts/jest/setupHostConfigs.js
+3
-3
@@ -77,7 +77,7 @@ function mockReact() {
77
jest.mock('react', () => {
78
const resolvedEntryPoint = resolveEntryFork(
79
require.resolve('react'),
80
- global.__WWW__
80
+ global.__WWW__ || global.__XPLAT__
81
);
82
return jest.requireActual(resolvedEntryPoint);
83
});
@@ -100,7 +100,7 @@ jest.mock('react/react.react-server', () => {
100
});
101
const resolvedEntryPoint = resolveEntryFork(
102
require.resolve('react/src/ReactServer'),
103
- global.__WWW__
103
+ global.__WWW__ || global.__XPLAT__
104
);
105
return jest.requireActual(resolvedEntryPoint);
106
});
@@ -198,7 +198,7 @@ inlinedHostConfigs.forEach(rendererInfo => {
198
mockAllConfigs(rendererInfo);
199
const resolvedEntryPoint = resolveEntryFork(
200
require.resolve(entryPoint),
201
- global.__WWW__
201
+ global.__WWW__ || global.__XPLAT__
202
);
203
return jest.requireActual(resolvedEntryPoint);
204
});
scripts/jest/setupTests.xplat.js
new
+33
@@ -0,0 +1,33 @@
1
+'use strict';
2
+
3
+jest.mock('shared/ReactFeatureFlags', () => {
4
+ jest.mock(
5
+ 'ReactNativeInternalFeatureFlags',
6
+ () =>
7
+ jest.requireActual('shared/forks/ReactFeatureFlags.native-fb-dynamic.js'),
8
+ {virtual: true}
9
+ );
10
+ const actual = jest.requireActual(
11
+ 'shared/forks/ReactFeatureFlags.native-fb.js'
12
+ );
13
+
14
+ // Lots of tests use these, but we don't want to expose it to RN.
15
+ // Ideally, tests for xplat wouldn't use react-dom, but many of our tests do.
16
+ // Since the xplat tests run with the www entry points, some of these flags
17
+ // need to be set to the www value for the entrypoint, otherwise gating would
18
+ // fail due to the tests passing. Ideally, the www entry points for these APIs
19
+ // would be gated, and then these would fail correctly.
20
+ actual.enableLegacyCache = true;
21
+ actual.enableLegacyHidden = true;
22
+ actual.enableScopeAPI = true;
23
+ actual.enableTaint = false;
24
+
25
+ return actual;
26
+});
27
+
28
+jest.mock('react-noop-renderer', () =>
29
+ jest.requireActual('react-noop-renderer/persistent')
30
+);
31
+
32
+global.__PERSISTENT__ = true;
33
+global.__XPLAT__ = true;