@samitouri / QOS-React-1 / commits / eabb681535

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;