Skip to content

Commit 2947f93

Browse files
authored
Merge pull request #219 from relay-tools/react-v18
support React v18
2 parents 3d2f914 + 5d64133 commit 2947f93

12 files changed

+411
-115
lines changed

__tests__/ReactRelayPaginationContainer-test.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -1821,7 +1821,9 @@ describe('ReactRelayPaginationContainer', () => {
18211821
},
18221822
},
18231823
});
1824-
instance.unmount();
1824+
ReactTestRenderer.act(() => {
1825+
instance.unmount();
1826+
});
18251827
expect(references.length).toBe(1);
18261828
expect(references[0].dispose).toBeCalled();
18271829
});

__tests__/ReactRelayQueryRenderer-test.tsx

+36-12
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
/* eslint-disable */
1212

13-
jest.mock('scheduler', () => require('scheduler/unstable_mock'));
13+
jest.mock('scheduler', () => jest.requireActual('scheduler/unstable_mock'));
1414

1515
import * as React from 'react';
1616
import * as Scheduler from 'scheduler';
@@ -350,22 +350,30 @@ describe('ReactRelayQueryRenderer', () => {
350350
);
351351
}
352352
}
353-
const renderer = ReactTestRenderer.create(<Example />, {
354-
unstable_isConcurrent: true,
353+
let renderer;
354+
// https://github.com/facebook/react/issues/24392
355+
React.startTransition(() => {
356+
renderer = ReactTestRenderer.create(<Example />, {
357+
unstable_isConcurrent: true,
358+
unstable_concurrentUpdatesByDefault: true,
359+
});
355360
});
356-
361+
357362
// Flush some of the changes, but don't commit
358363
(Scheduler as any).unstable_flushNumberOfYields(2);
364+
365+
359366
expect((Scheduler as any).unstable_clearYields()).toEqual(['A', 'B']);
360367
expect(renderer.toJSON()).toEqual(null);
361368
expect().loadingRendered();
362369
expect(environment.execute.mock.calls.length).toBe(1);
363370
render.mockClear();
364-
365371
// Interrupt with higher priority updates
366372
renderer.unstable_flushSync(() => {
367373
renderer.update(<Example />);
368374
});
375+
376+
369377
expect(environment.execute.mock.calls.length).toBe(1);
370378
expect().loadingRendered();
371379
});
@@ -412,8 +420,13 @@ describe('ReactRelayQueryRenderer', () => {
412420
);
413421
}
414422
}
415-
const renderer = ReactTestRenderer.create(<Example />, {
416-
unstable_isConcurrent: true,
423+
let renderer;
424+
// https://github.com/facebook/react/issues/24392
425+
React.startTransition(() => {
426+
renderer = ReactTestRenderer.create(<Example />, {
427+
unstable_isConcurrent: true,
428+
unstable_concurrentUpdatesByDefault: true,
429+
});
417430
});
418431
const owner = createOperationDescriptor(TestQuery, variables);
419432

@@ -501,8 +514,13 @@ describe('ReactRelayQueryRenderer', () => {
501514
);
502515
}
503516
}
504-
const renderer = ReactTestRenderer.create(<Example />, {
505-
unstable_isConcurrent: true,
517+
let renderer;
518+
// https://github.com/facebook/react/issues/24392
519+
React.startTransition(() => {
520+
renderer = ReactTestRenderer.create(<Example />, {
521+
unstable_isConcurrent: true,
522+
unstable_concurrentUpdatesByDefault: true,
523+
});
506524
});
507525
const owner = createOperationDescriptor(TestQuery, variables);
508526

@@ -1644,7 +1662,9 @@ describe('ReactRelayQueryRenderer', () => {
16441662
expect(environment.retain.mock.calls.length).toBe(1);
16451663
const dispose = environment.retain.mock.dispose;
16461664
expect(dispose).not.toBeCalled();
1647-
renderer.unmount();
1665+
ReactTestRenderer.act(() => {
1666+
renderer.unmount();
1667+
});
16481668
expect(dispose).toBeCalled();
16491669
});
16501670

@@ -1662,7 +1682,9 @@ describe('ReactRelayQueryRenderer', () => {
16621682
expect(environment.retain.mock.calls.length).toBe(1);
16631683
const dispose = environment.retain.mock.dispose;
16641684
expect(dispose).not.toBeCalled();
1665-
renderer.unmount();
1685+
ReactTestRenderer.act(() => {
1686+
renderer.unmount();
1687+
});
16661688
expect(dispose).toBeCalled();
16671689
});
16681690

@@ -1677,7 +1699,9 @@ describe('ReactRelayQueryRenderer', () => {
16771699
);
16781700
const subscription = environment.execute.mock.subscriptions[0];
16791701
expect(subscription.closed).toBe(false);
1680-
renderer.unmount();
1702+
ReactTestRenderer.act(() => {
1703+
renderer.unmount();
1704+
});
16811705
expect(subscription.closed).toBe(true);
16821706
});
16831707
});

__tests__/ReactRelayRefetchContainer-test.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,9 @@ describe('ReactRelayRefetchContainer', () => {
951951
},
952952
},
953953
});
954-
instance.unmount();
954+
ReactTestRenderer.act(() => {
955+
instance.unmount();
956+
});
955957
expect(references.length).toBe(1);
956958
expect(references[0].dispose).toBeCalled();
957959
});

__tests__/RelayHooks-test.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import {
1313
} from '../src';
1414

1515
function createHooks(component, options?: any) {
16-
const result = ReactTestRenderer.create(component, options);
16+
let result;// = ReactTestRenderer.create(component, options);
1717
ReactTestRenderer.act(() => {
18+
result= ReactTestRenderer.create(component, options);
1819
jest.runAllImmediates();
1920
});
2021
return result;

__tests__/enqueue.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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+
* @emails oncall+relay
8+
* @format
9+
*/
10+
11+
// This file is sync'd from https://github.com/facebook/react/tree/main/packages/jest-react
12+
13+
'use strict';
14+
15+
let didWarnAboutMessageChannel = false;
16+
let enqueueTaskImpl = null;
17+
18+
export function enqueueTask(task: () => void) {
19+
if (enqueueTaskImpl === null) {
20+
try {
21+
// read require off the module object to get around the bundlers.
22+
// we don't want them to detect a require and bundle a Node polyfill.
23+
const requireString = ('require' + Math.random()).slice(0, 7);
24+
const nodeRequire = module && module[requireString];
25+
// assuming we're in node, let's try to get node's
26+
// version of setImmediate, bypassing fake timers if any.
27+
enqueueTaskImpl = nodeRequire.call(module, 'timers').setImmediate;
28+
} catch {
29+
// we're in a browser
30+
// we can't use regular timers because they may still be faked
31+
// so we try MessageChannel+postMessage instead
32+
enqueueTaskImpl = function (callback: () => void) {
33+
if (__DEV__) {
34+
if (didWarnAboutMessageChannel === false) {
35+
didWarnAboutMessageChannel = true;
36+
if (typeof MessageChannel === 'undefined') {
37+
console.error(
38+
'This browser does not have a MessageChannel implementation, ' +
39+
'so enqueuing tasks via await act(async () => ...) will fail. ' +
40+
'Please file an issue at https://github.com/facebook/react/issues ' +
41+
'if you encounter this warning.',
42+
);
43+
}
44+
}
45+
}
46+
/*global MessageChannel*/
47+
const channel = new MessageChannel();
48+
channel.port1.onmessage = callback;
49+
channel.port2.postMessage(undefined);
50+
};
51+
}
52+
}
53+
return enqueueTaskImpl(task);
54+
}

__tests__/internalAct.ts

+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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+
* @emails oncall+relay
8+
* @format
9+
*/
10+
11+
/* global jest */
12+
13+
// This file is sync'd from https://github.com/facebook/react/tree/main/packages/jest-react
14+
15+
// This version of `act` is only used by our tests. Unlike the public version
16+
// of `act`, it's designed to work identically in both production and
17+
// development. It may have slightly different behavior from the public
18+
// version, too, since our constraints in our test suite are not the same as
19+
// those of developers using React — we're testing React itself, as opposed to
20+
// building an app with React.
21+
22+
'use strict';
23+
24+
import { enqueueTask } from "./enqueue";
25+
26+
const Scheduler = require('scheduler/unstable_mock');
27+
let actingUpdatesScopeDepth = 0;
28+
29+
export function act<T>(scope: any): any {
30+
if (Scheduler.unstable_flushAllWithoutAsserting === undefined) {
31+
throw Error(
32+
'This version of `act` requires a special mock build of Scheduler.',
33+
);
34+
}
35+
if ((setTimeout as any)._isMockFunction !== true) {
36+
throw Error(
37+
"This version of `act` requires Jest's timer mocks " +
38+
'(i.e. jest.useFakeTimers).',
39+
);
40+
}
41+
42+
const previousIsActEnvironment = global.IS_REACT_ACT_ENVIRONMENT;
43+
const previousActingUpdatesScopeDepth = actingUpdatesScopeDepth;
44+
actingUpdatesScopeDepth++;
45+
if (__DEV__ && actingUpdatesScopeDepth === 1) {
46+
// Because this is not the "real" `act`, we set this to `false` so React
47+
// knows not to fire `act` warnings.
48+
global.IS_REACT_ACT_ENVIRONMENT = false;
49+
}
50+
51+
const unwind = () => {
52+
if (__DEV__ && actingUpdatesScopeDepth === 1) {
53+
global.IS_REACT_ACT_ENVIRONMENT = previousIsActEnvironment;
54+
}
55+
actingUpdatesScopeDepth--;
56+
57+
if (__DEV__) {
58+
if (actingUpdatesScopeDepth > previousActingUpdatesScopeDepth) {
59+
// if it's _less than_ previousActingUpdatesScopeDepth, then we can
60+
// assume the 'other' one has warned
61+
console.error(
62+
'You seem to have overlapping act() calls, this is not supported. ' +
63+
'Be sure to await previous act() calls before making a new one. ',
64+
);
65+
}
66+
}
67+
};
68+
69+
// TODO: This would be way simpler if 1) we required a promise to be
70+
// returned and 2) we could use async/await. Since it's only our used in
71+
// our test suite, we should be able to.
72+
try {
73+
const result = scope();
74+
if (
75+
typeof result === 'object' &&
76+
result !== null &&
77+
typeof result.then === 'function'
78+
) {
79+
const thenableResult: any = (result as any);
80+
return {
81+
then(resolve, reject) {
82+
thenableResult.then(
83+
returnValue => {
84+
flushActWork(
85+
() => {
86+
unwind();
87+
resolve(returnValue);
88+
},
89+
error => {
90+
unwind();
91+
reject(error);
92+
},
93+
);
94+
},
95+
error => {
96+
unwind();
97+
reject(error);
98+
},
99+
);
100+
},
101+
};
102+
} else {
103+
const returnValue: any = (result as any);
104+
try {
105+
// TODO: Let's not support non-async scopes at all in our tests. Need to
106+
// migrate existing tests.
107+
let didFlushWork;
108+
do {
109+
didFlushWork = Scheduler.unstable_flushAllWithoutAsserting();
110+
} while (didFlushWork);
111+
return {
112+
then(resolve, reject) {
113+
resolve(returnValue);
114+
},
115+
};
116+
} finally {
117+
unwind();
118+
}
119+
}
120+
} catch (error) {
121+
unwind();
122+
throw error;
123+
}
124+
}
125+
126+
function flushActWork(resolve, reject) {
127+
// Flush suspended fallbacks
128+
// $FlowFixMe: Flow doesn't know about global Jest object
129+
jest.runOnlyPendingTimers();
130+
enqueueTask(() => {
131+
try {
132+
const didFlushWork = Scheduler.unstable_flushAllWithoutAsserting();
133+
if (didFlushWork) {
134+
flushActWork(resolve, reject);
135+
} else {
136+
resolve();
137+
}
138+
} catch (error) {
139+
reject(error);
140+
}
141+
});
142+
}
143+

0 commit comments

Comments
 (0)