Skip to content

Commit

Permalink
feat: option for enabling concurrent rendering (#1685)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdjastrzebski authored Oct 25, 2024
1 parent 8e1e6ac commit f231ea3
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 5 deletions.
17 changes: 16 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,26 @@ jobs:
uses: ./.github/actions/setup-deps

- name: Test
run: yarn test:ci
run: yarn test:ci:coverage

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


test-concurrent:
needs: [install-cache-deps]
runs-on: ubuntu-latest
name: Test (concurrent mode)
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js and deps
uses: ./.github/actions/setup-deps

- name: Test in concurrent mode
run: CONCURRENT_MODE=1 yarn test:ci

test-website:
runs-on: ubuntu-latest
name: Test Website
Expand Down
5 changes: 4 additions & 1 deletion jest-setup.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { resetToDefaults } from './src/pure';
import { resetToDefaults, configure } from './src/pure';
import './src/matchers/extend-expect';

beforeEach(() => {
resetToDefaults();
if (process.env.CONCURRENT_MODE === '1') {
configure({ concurrentRoot: true });
}
});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"scripts": {
"clean": "del build",
"test": "jest",
"test:ci": "jest --maxWorkers=2 --collectCoverage=true --coverage-provider=v8",
"test:ci": "jest --maxWorkers=2",
"test:ci:coverage": "jest --maxWorkers=2 --collectCoverage=true --coverage-provider=v8",
"typecheck": "tsc",
"copy-flowtypes": "cp typings/index.flow.js build",
"lint": "eslint src --cache",
Expand Down
5 changes: 5 additions & 0 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { getConfig, configure, resetToDefaults, configureInternal } from '../config';

beforeEach(() => {
resetToDefaults();
});

test('getConfig() returns existing configuration', () => {
expect(getConfig().asyncUtilTimeout).toEqual(1000);
expect(getConfig().defaultIncludeHiddenElements).toEqual(false);
Expand All @@ -12,6 +16,7 @@ test('configure() overrides existing config values', () => {
asyncUtilTimeout: 5000,
defaultDebugOptions: { message: 'debug message' },
defaultIncludeHiddenElements: false,
concurrentRoot: false,
});
});

Expand Down
10 changes: 10 additions & 0 deletions src/__tests__/render.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,13 @@ test('render calls detects host component names', () => {
render(<View testID="test" />);
expect(getConfig().hostComponentNames).not.toBeUndefined();
});

test('supports legacy rendering', () => {
render(<View testID="test" />, { concurrentRoot: false });
expect(screen.root).toBeDefined();
});

test('supports concurrent rendering', () => {
render(<View testID="test" />, { concurrentRoot: true });
expect(screen.root).toBeDefined();
});
7 changes: 7 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export type Config = {

/** Default options for `debug` helper. */
defaultDebugOptions?: Partial<DebugOptions>;

/**
* Set to `true` to enable concurrent rendering.
* Otherwise `render` will default to legacy synchronous rendering.
*/
concurrentRoot: boolean;
};

export type ConfigAliasOptions = {
Expand All @@ -37,6 +43,7 @@ export type InternalConfig = Config & {
const defaultConfig: InternalConfig = {
asyncUtilTimeout: 1000,
defaultIncludeHiddenElements: false,
concurrentRoot: false,
};

let config = { ...defaultConfig };
Expand Down
26 changes: 24 additions & 2 deletions src/render.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { ReactTestInstance, ReactTestRenderer } from 'react-test-renderer';
import type {
ReactTestInstance,
ReactTestRenderer,
TestRendererOptions,
} from 'react-test-renderer';
import * as React from 'react';
import { Profiler } from 'react';
import act from './act';
Expand All @@ -14,7 +18,18 @@ import { setRenderResult } from './screen';
import { getQueriesForElement } from './within';

export interface RenderOptions {
/**
* Pass a React Component as the wrapper option to have it rendered around the inner element. This is most useful for creating
* reusable custom render functions for common data providers.
*/
wrapper?: React.ComponentType<any>;

/**
* Set to `true` to enable concurrent rendering.
* Otherwise `render` will default to legacy synchronous rendering.
*/
concurrentRoot?: boolean | undefined;

createNodeMock?: (element: React.ReactElement) => unknown;
unstable_validateStringsRenderedWithinText?: boolean;
}
Expand All @@ -39,11 +54,18 @@ export function renderInternal<T>(
) {
const {
wrapper: Wrapper,
concurrentRoot,
detectHostComponentNames = true,
unstable_validateStringsRenderedWithinText,
...testRendererOptions
...rest
} = options || {};

const testRendererOptions: TestRendererOptions = {
...rest,
// @ts-expect-error incomplete typing on RTR package
unstable_isConcurrent: concurrentRoot ?? getConfig().concurrentRoot,
};

if (detectHostComponentNames) {
configureHostComponentNamesIfNeeded();
}
Expand Down
5 changes: 5 additions & 0 deletions website/docs/12.x/docs/api/misc/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ type Config = {
asyncUtilTimeout: number;
defaultHidden: boolean;
defaultDebugOptions: Partial<DebugOptions>;
concurrentRoot: boolean;
};

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

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()`.

### `concurrentRoot` option

Set to `true` to enable concurrent rendering used in the React Native New Architecture. Otherwise `render` will default to legacy synchronous rendering.

## `resetToDefaults()`

```ts
Expand Down
4 changes: 4 additions & 0 deletions website/docs/12.x/docs/api/render.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ wrapper?: React.ComponentType<any>,

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.

#### `concurrentRoot` option

Set to `true` to enable concurrent rendering used in the React Native New Architecture. Otherwise `render` will default to legacy synchronous rendering.

#### `createNodeMock` option

```ts
Expand Down

0 comments on commit f231ea3

Please sign in to comment.