Skip to content

Commit f231ea3

Browse files
feat: option for enabling concurrent rendering (#1685)
1 parent 8e1e6ac commit f231ea3

File tree

9 files changed

+77
-5
lines changed

9 files changed

+77
-5
lines changed

.github/workflows/ci.yml

+16-1
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,26 @@ jobs:
5656
uses: ./.github/actions/setup-deps
5757

5858
- name: Test
59-
run: yarn test:ci
59+
run: yarn test:ci:coverage
6060

6161
- name: Upload coverage to Codecov
6262
uses: codecov/codecov-action@v4
6363

64+
65+
test-concurrent:
66+
needs: [install-cache-deps]
67+
runs-on: ubuntu-latest
68+
name: Test (concurrent mode)
69+
steps:
70+
- name: Checkout
71+
uses: actions/checkout@v4
72+
73+
- name: Setup Node.js and deps
74+
uses: ./.github/actions/setup-deps
75+
76+
- name: Test in concurrent mode
77+
run: CONCURRENT_MODE=1 yarn test:ci
78+
6479
test-website:
6580
runs-on: ubuntu-latest
6681
name: Test Website

jest-setup.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { resetToDefaults } from './src/pure';
1+
import { resetToDefaults, configure } from './src/pure';
22
import './src/matchers/extend-expect';
33

44
beforeEach(() => {
55
resetToDefaults();
6+
if (process.env.CONCURRENT_MODE === '1') {
7+
configure({ concurrentRoot: true });
8+
}
69
});

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@
2525
"scripts": {
2626
"clean": "del build",
2727
"test": "jest",
28-
"test:ci": "jest --maxWorkers=2 --collectCoverage=true --coverage-provider=v8",
28+
"test:ci": "jest --maxWorkers=2",
29+
"test:ci:coverage": "jest --maxWorkers=2 --collectCoverage=true --coverage-provider=v8",
2930
"typecheck": "tsc",
3031
"copy-flowtypes": "cp typings/index.flow.js build",
3132
"lint": "eslint src --cache",

src/__tests__/config.test.ts

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { getConfig, configure, resetToDefaults, configureInternal } from '../config';
22

3+
beforeEach(() => {
4+
resetToDefaults();
5+
});
6+
37
test('getConfig() returns existing configuration', () => {
48
expect(getConfig().asyncUtilTimeout).toEqual(1000);
59
expect(getConfig().defaultIncludeHiddenElements).toEqual(false);
@@ -12,6 +16,7 @@ test('configure() overrides existing config values', () => {
1216
asyncUtilTimeout: 5000,
1317
defaultDebugOptions: { message: 'debug message' },
1418
defaultIncludeHiddenElements: false,
19+
concurrentRoot: false,
1520
});
1621
});
1722

src/__tests__/render.test.tsx

+10
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,13 @@ test('render calls detects host component names', () => {
241241
render(<View testID="test" />);
242242
expect(getConfig().hostComponentNames).not.toBeUndefined();
243243
});
244+
245+
test('supports legacy rendering', () => {
246+
render(<View testID="test" />, { concurrentRoot: false });
247+
expect(screen.root).toBeDefined();
248+
});
249+
250+
test('supports concurrent rendering', () => {
251+
render(<View testID="test" />, { concurrentRoot: true });
252+
expect(screen.root).toBeDefined();
253+
});

src/config.ts

+7
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ export type Config = {
1313

1414
/** Default options for `debug` helper. */
1515
defaultDebugOptions?: Partial<DebugOptions>;
16+
17+
/**
18+
* Set to `true` to enable concurrent rendering.
19+
* Otherwise `render` will default to legacy synchronous rendering.
20+
*/
21+
concurrentRoot: boolean;
1622
};
1723

1824
export type ConfigAliasOptions = {
@@ -37,6 +43,7 @@ export type InternalConfig = Config & {
3743
const defaultConfig: InternalConfig = {
3844
asyncUtilTimeout: 1000,
3945
defaultIncludeHiddenElements: false,
46+
concurrentRoot: false,
4047
};
4148

4249
let config = { ...defaultConfig };

src/render.tsx

+24-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
1+
import type {
2+
ReactTestInstance,
3+
ReactTestRenderer,
4+
TestRendererOptions,
5+
} from 'react-test-renderer';
26
import * as React from 'react';
37
import { Profiler } from 'react';
48
import act from './act';
@@ -14,7 +18,18 @@ import { setRenderResult } from './screen';
1418
import { getQueriesForElement } from './within';
1519

1620
export interface RenderOptions {
21+
/**
22+
* Pass a React Component as the wrapper option to have it rendered around the inner element. This is most useful for creating
23+
* reusable custom render functions for common data providers.
24+
*/
1725
wrapper?: React.ComponentType<any>;
26+
27+
/**
28+
* Set to `true` to enable concurrent rendering.
29+
* Otherwise `render` will default to legacy synchronous rendering.
30+
*/
31+
concurrentRoot?: boolean | undefined;
32+
1833
createNodeMock?: (element: React.ReactElement) => unknown;
1934
unstable_validateStringsRenderedWithinText?: boolean;
2035
}
@@ -39,11 +54,18 @@ export function renderInternal<T>(
3954
) {
4055
const {
4156
wrapper: Wrapper,
57+
concurrentRoot,
4258
detectHostComponentNames = true,
4359
unstable_validateStringsRenderedWithinText,
44-
...testRendererOptions
60+
...rest
4561
} = options || {};
4662

63+
const testRendererOptions: TestRendererOptions = {
64+
...rest,
65+
// @ts-expect-error incomplete typing on RTR package
66+
unstable_isConcurrent: concurrentRoot ?? getConfig().concurrentRoot,
67+
};
68+
4769
if (detectHostComponentNames) {
4870
configureHostComponentNamesIfNeeded();
4971
}

website/docs/12.x/docs/api/misc/config.mdx

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type Config = {
77
asyncUtilTimeout: number;
88
defaultHidden: boolean;
99
defaultDebugOptions: Partial<DebugOptions>;
10+
concurrentRoot: boolean;
1011
};
1112

1213
function configure(options: Partial<Config>) {}
@@ -26,6 +27,10 @@ This option is also available as `defaultHidden` alias for compatibility with [R
2627

2728
Default [debug options](#debug) to be used when calling `debug()`. These default options will be overridden by the ones you specify directly when calling `debug()`.
2829

30+
### `concurrentRoot` option
31+
32+
Set to `true` to enable concurrent rendering used in the React Native New Architecture. Otherwise `render` will default to legacy synchronous rendering.
33+
2934
## `resetToDefaults()`
3035

3136
```ts

website/docs/12.x/docs/api/render.mdx

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ wrapper?: React.ComponentType<any>,
3232

3333
This option allows you to wrap the tested component, passed as the first option to the `render()` function, in an additional wrapper component. This is useful for creating reusable custom render functions for common React Context providers.
3434

35+
#### `concurrentRoot` option
36+
37+
Set to `true` to enable concurrent rendering used in the React Native New Architecture. Otherwise `render` will default to legacy synchronous rendering.
38+
3539
#### `createNodeMock` option
3640

3741
```ts

0 commit comments

Comments
 (0)