`);
- await act(
+ await actAsync(
async () => (store.componentFilters = [utils.createHOCFilter(false)]),
);
expect(store).toMatchInlineSnapshot(`
@@ -309,7 +298,7 @@ describe('Store component filters', () => {
// @reactVersion >= 16.0
it('should not send a bridge update if the set of enabled filters has not changed', async () => {
- await act(
+ await actAsync(
async () => (store.componentFilters = [utils.createHOCFilter(true)]),
);
@@ -317,21 +306,21 @@ describe('Store component filters', () => {
throw Error('Unexpected component update');
});
- await act(
+ await actAsync(
async () =>
(store.componentFilters = [
utils.createHOCFilter(false),
utils.createHOCFilter(true),
]),
);
- await act(
+ await actAsync(
async () =>
(store.componentFilters = [
utils.createHOCFilter(true),
utils.createLocationFilter('abc', false),
]),
);
- await act(
+ await actAsync(
async () =>
(store.componentFilters = [
utils.createHOCFilter(true),
@@ -363,10 +352,7 @@ describe('Store component filters', () => {
utils.createElementTypeFilter(Types.ElementTypeSuspense),
];
- const container = document.createElement('div');
- await act(async () =>
- legacyRender(
@@ -374,18 +360,14 @@ describe('Store component filters', () => {
`);
- await act(async () =>
- legacyRender(, container),
- );
+ await actAsync(async () => render());
expect(store).toMatchInlineSnapshot(`
[root]
▾
`);
- await act(async () =>
- legacyRender(, container),
- );
+ await actAsync(async () => render());
expect(store).toMatchInlineSnapshot(`
[root]
▾
@@ -395,8 +377,11 @@ describe('Store component filters', () => {
});
describe('inline errors and warnings', () => {
+ const {render: legacyRender} = getLegacyRenderImplementation();
+
// @reactVersion >= 17.0
- it('only counts for unfiltered components', async () => {
+ // @reactVersion <= 18.2
+ it('only counts for unfiltered components (legacy render)', async () => {
function ComponentWithWarning() {
console.warn('test-only: render warning');
return null;
@@ -411,15 +396,7 @@ describe('Store component filters', () => {
return null;
}
- // HACK This require() is needed (somewhere in the test) for this case to pass.
- // Without it, the legacyRender() call below causes this test to fail
- // because it requires "react-dom" for the first time,
- // which causes the console error() and warn() methods to be overridden again,
- // effectively disconnecting the DevTools override in 'react-devtools-shared/src/backend/console'.
- require('react-dom');
-
- const container = document.createElement('div');
- await act(
+ await actAsync(
async () =>
(store.componentFilters = [
utils.createDisplayNameFilter('Warning'),
@@ -433,7 +410,6 @@ describe('Store component filters', () => {
,
- container,
);
});
@@ -441,7 +417,99 @@ describe('Store component filters', () => {
expect(store.errorCount).toBe(0);
expect(store.warningCount).toBe(0);
- await act(async () => (store.componentFilters = []));
+ await actAsync(async () => (store.componentFilters = []));
+ expect(store).toMatchInlineSnapshot(`
+ ✕ 2, ⚠ 2
+ [root]
+ ✕
+ ⚠
+ ✕⚠
+ `);
+
+ await actAsync(
+ async () =>
+ (store.componentFilters = [utils.createDisplayNameFilter('Warning')]),
+ );
+ expect(store).toMatchInlineSnapshot(`
+ ✕ 1, ⚠ 0
+ [root]
+ ✕
+ `);
+
+ await actAsync(
+ async () =>
+ (store.componentFilters = [utils.createDisplayNameFilter('Error')]),
+ );
+ expect(store).toMatchInlineSnapshot(`
+ ✕ 0, ⚠ 1
+ [root]
+ ⚠
+ `);
+
+ await actAsync(
+ async () =>
+ (store.componentFilters = [
+ utils.createDisplayNameFilter('Warning'),
+ utils.createDisplayNameFilter('Error'),
+ ]),
+ );
+ expect(store).toMatchInlineSnapshot(`[root]`);
+ expect(store.errorCount).toBe(0);
+ expect(store.warningCount).toBe(0);
+
+ await actAsync(async () => (store.componentFilters = []));
+ expect(store).toMatchInlineSnapshot(`
+ ✕ 2, ⚠ 2
+ [root]
+ ✕
+ ⚠
+ ✕⚠
+ `);
+ });
+
+ // @reactVersion >= 18
+ it('only counts for unfiltered components (createRoot)', async () => {
+ function ComponentWithWarning() {
+ console.warn('test-only: render warning');
+ return null;
+ }
+ function ComponentWithError() {
+ console.error('test-only: render error');
+ return null;
+ }
+ function ComponentWithWarningAndError() {
+ console.error('test-only: render error');
+ console.warn('test-only: render warning');
+ return null;
+ }
+
+ await actAsync(
+ async () =>
+ (store.componentFilters = [
+ utils.createDisplayNameFilter('Warning'),
+ utils.createDisplayNameFilter('Error'),
+ ]),
+ );
+
+ utils.act(
+ () =>
+ utils.withErrorsOrWarningsIgnored(['test-only:'], () => {
+ render(
+
+
+
+
+ ,
+ );
+ }),
+ false,
+ );
+
+ expect(store).toMatchInlineSnapshot(``);
+ expect(store.errorCount).toBe(0);
+ expect(store.warningCount).toBe(0);
+
+ await actAsync(async () => (store.componentFilters = []));
expect(store).toMatchInlineSnapshot(`
✕ 2, ⚠ 2
[root]
@@ -450,7 +518,7 @@ describe('Store component filters', () => {
✕⚠
`);
- await act(
+ await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Warning')]),
);
@@ -460,7 +528,7 @@ describe('Store component filters', () => {
✕
`);
- await act(
+ await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Error')]),
);
@@ -470,7 +538,7 @@ describe('Store component filters', () => {
⚠
`);
- await act(
+ await actAsync(
async () =>
(store.componentFilters = [
utils.createDisplayNameFilter('Warning'),
@@ -481,7 +549,7 @@ describe('Store component filters', () => {
expect(store.errorCount).toBe(0);
expect(store.warningCount).toBe(0);
- await act(async () => (store.componentFilters = []));
+ await actAsync(async () => (store.componentFilters = []));
expect(store).toMatchInlineSnapshot(`
✕ 2, ⚠ 2
[root]
diff --git a/packages/react-devtools-shared/src/__tests__/utils.js b/packages/react-devtools-shared/src/__tests__/utils.js
index f00e66d98396e..95d2c3c6f860a 100644
--- a/packages/react-devtools-shared/src/__tests__/utils.js
+++ b/packages/react-devtools-shared/src/__tests__/utils.js
@@ -21,6 +21,12 @@ import {ReactVersion} from '../../../../ReactVersions';
const requestedReactVersion = process.env.REACT_VERSION || ReactVersion;
export function getActDOMImplementation(): () => void | Promise {
+ // This is for React < 17, where act wasn't shipped yet.
+ if (semver.lt(requestedReactVersion, '17.0.0')) {
+ require('react-dom/test-utils');
+ return cb => cb();
+ }
+
// This is for React < 18, where act was distributed in react-dom/test-utils.
if (semver.lt(requestedReactVersion, '18.0.0')) {
const ReactDOMTestUtils = require('react-dom/test-utils');
@@ -41,13 +47,30 @@ export function getActDOMImplementation(): () => void | Promise {
throw new Error("Couldn't find any available act implementation");
}
+export function getActTestRendererImplementation(): () => void | Promise {
+ // This is for React < 17, where act wasn't shipped yet.
+ if (semver.lt(requestedReactVersion, '17.0.0')) {
+ require('react-test-renderer');
+ return cb => cb();
+ }
+
+ const ReactTestRenderer = require('react-test-renderer');
+ if (ReactTestRenderer.act) {
+ return ReactTestRenderer.act;
+ }
+
+ throw new Error(
+ "Couldn't find any available act implementation in react-test-renderer",
+ );
+}
+
export function act(
callback: Function,
recursivelyFlush: boolean = true,
): void {
// act from react-test-renderer has some side effects on React DevTools
// it injects the renderer for DevTools, see ReactTestRenderer.js
- const {act: actTestRenderer} = require('react-test-renderer');
+ const actTestRenderer = getActTestRendererImplementation();
const actDOM = getActDOMImplementation();
actDOM(() => {
@@ -74,7 +97,7 @@ export async function actAsync(
): Promise {
// act from react-test-renderer has some side effects on React DevTools
// it injects the renderer for DevTools, see ReactTestRenderer.js
- const {act: actTestRenderer} = require('react-test-renderer');
+ const actTestRenderer = getActTestRendererImplementation();
const actDOM = getActDOMImplementation();
await actDOM(async () => {