Skip to content

Commit

Permalink
chore: use versioned render in storeComponentFilters test (facebook#2…
Browse files Browse the repository at this point in the history
hoxyq authored and AndyPengc12 committed Apr 15, 2024
1 parent c12d307 commit 8716706
Showing 2 changed files with 164 additions and 73 deletions.
Original file line number Diff line number Diff line change
@@ -10,25 +10,18 @@
import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
import type Store from 'react-devtools-shared/src/devtools/store';

import {
getLegacyRenderImplementation,
getVersionedRenderImplementation,
} from './utils';

describe('Store component filters', () => {
let React;
let Types;
let bridge: FrontendBridge;
let legacyRender;
let store: Store;
let utils;

const act = async (callback: Function) => {
if (React.act != null) {
await React.act(callback);
} else if (React.unstable_act != null) {
await React.unstable_act(callback);
} else {
callback();
}

jest.runAllTimers(); // Flush Bridge operations
};
let actAsync;

beforeEach(() => {
bridge = global.bridge;
@@ -41,12 +34,14 @@ describe('Store component filters', () => {
Types = require('react-devtools-shared/src/frontend/types');
utils = require('./utils');

legacyRender = utils.legacyRender;
actAsync = utils.actAsync;
});

const {render} = getVersionedRenderImplementation();

// @reactVersion >= 16.0
it('should throw if filters are updated while profiling', async () => {
await act(async () => store.profilerStore.startProfiling());
await actAsync(async () => store.profilerStore.startProfiling());
expect(() => (store.componentFilters = [])).toThrow(
'Cannot modify filter preferences while profiling',
);
@@ -61,12 +56,11 @@ describe('Store component filters', () => {
}
const FunctionComponent = () => <div>Hi</div>;

await act(async () =>
legacyRender(
await actAsync(async () =>
render(
<ClassComponent>
<FunctionComponent />
</ClassComponent>,
document.createElement('div'),
),
);
expect(store).toMatchInlineSnapshot(`
@@ -77,7 +71,7 @@ describe('Store component filters', () => {
<div>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [
utils.createElementTypeFilter(Types.ElementTypeHostComponent),
@@ -89,7 +83,7 @@ describe('Store component filters', () => {
<FunctionComponent>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [
utils.createElementTypeFilter(Types.ElementTypeClass),
@@ -102,7 +96,7 @@ describe('Store component filters', () => {
<div>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [
utils.createElementTypeFilter(Types.ElementTypeClass),
@@ -115,7 +109,7 @@ describe('Store component filters', () => {
<div>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [
utils.createElementTypeFilter(Types.ElementTypeClass, false),
@@ -130,7 +124,7 @@ describe('Store component filters', () => {
<div>
`);

await act(async () => (store.componentFilters = []));
await actAsync(async () => (store.componentFilters = []));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <ClassComponent>
@@ -144,16 +138,14 @@ describe('Store component filters', () => {
it('should ignore invalid ElementTypeRoot filter', async () => {
const Component = () => <div>Hi</div>;

await act(async () =>
legacyRender(<Component />, document.createElement('div')),
);
await actAsync(async () => render(<Component />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Component>
<div>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [
utils.createElementTypeFilter(Types.ElementTypeRoot),
@@ -174,14 +166,13 @@ describe('Store component filters', () => {
const Bar = () => <Text label="bar" />;
const Baz = () => <Text label="baz" />;

await act(async () =>
legacyRender(
await actAsync(async () =>
render(
<React.Fragment>
<Foo />
<Bar />
<Baz />
</React.Fragment>,
document.createElement('div'),
),
);
expect(store).toMatchInlineSnapshot(`
@@ -194,7 +185,7 @@ describe('Store component filters', () => {
<Text>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Foo')]),
);
@@ -207,7 +198,7 @@ describe('Store component filters', () => {
<Text>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Ba')]),
);
@@ -219,7 +210,7 @@ describe('Store component filters', () => {
<Text>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('B.z')]),
);
@@ -237,16 +228,14 @@ describe('Store component filters', () => {
it('should filter by path', async () => {
const Component = () => <div>Hi</div>;

await act(async () =>
legacyRender(<Component />, document.createElement('div')),
);
await actAsync(async () => render(<Component />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Component>
<div>
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [
utils.createLocationFilter(__filename.replace(__dirname, '')),
@@ -255,7 +244,7 @@ describe('Store component filters', () => {

expect(store).toMatchInlineSnapshot(`[root]`);

await act(
await actAsync(
async () =>
(store.componentFilters = [
utils.createLocationFilter('this:is:a:made:up:path'),
@@ -277,7 +266,7 @@ describe('Store component filters', () => {
const Bar = () => <Foo />;
Bar.displayName = 'Bar(Foo(Component))';

await act(async () => legacyRender(<Bar />, document.createElement('div')));
await actAsync(async () => render(<Bar />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Component> [Bar][Foo]
@@ -286,7 +275,7 @@ describe('Store component filters', () => {
<div>
`);

await act(
await actAsync(
async () => (store.componentFilters = [utils.createHOCFilter(true)]),
);
expect(store).toMatchInlineSnapshot(`
@@ -295,7 +284,7 @@ describe('Store component filters', () => {
<div>
`);

await act(
await actAsync(
async () => (store.componentFilters = [utils.createHOCFilter(false)]),
);
expect(store).toMatchInlineSnapshot(`
@@ -309,29 +298,29 @@ 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)]),
);

bridge.addListener('updateComponentFilters', componentFilters => {
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,29 +352,22 @@ describe('Store component filters', () => {
utils.createElementTypeFilter(Types.ElementTypeSuspense),
];

const container = document.createElement('div');
await act(async () =>
legacyRender(<Wrapper shouldSuspend={true} />, container),
);
await actAsync(async () => render(<Wrapper shouldSuspend={true} />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Wrapper>
▾ <Loading>
<div>
`);

await act(async () =>
legacyRender(<Wrapper shouldSuspend={false} />, container),
);
await actAsync(async () => render(<Wrapper shouldSuspend={false} />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Wrapper>
<Component>
`);

await act(async () =>
legacyRender(<Wrapper shouldSuspend={true} />, container),
);
await actAsync(async () => render(<Wrapper shouldSuspend={true} />));
expect(store).toMatchInlineSnapshot(`
[root]
▾ <Wrapper>
@@ -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,15 +410,106 @@ describe('Store component filters', () => {
<ComponentWithWarning />
<ComponentWithWarningAndError />
</React.Fragment>,
container,
);
});

expect(store).toMatchInlineSnapshot(``);
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]
<ComponentWithError> ✕
<ComponentWithWarning> ⚠
<ComponentWithWarningAndError> ✕⚠
`);

await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Warning')]),
);
expect(store).toMatchInlineSnapshot(`
✕ 1, ⚠ 0
[root]
<ComponentWithError> ✕
`);

await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Error')]),
);
expect(store).toMatchInlineSnapshot(`
✕ 0, ⚠ 1
[root]
<ComponentWithWarning> ⚠
`);

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]
<ComponentWithError> ✕
<ComponentWithWarning> ⚠
<ComponentWithWarningAndError> ✕⚠
`);
});

// @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(
<React.Fragment>
<ComponentWithError />
<ComponentWithWarning />
<ComponentWithWarningAndError />
</React.Fragment>,
);
}),
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', () => {
<ComponentWithWarningAndError> ✕⚠
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Warning')]),
);
@@ -460,7 +528,7 @@ describe('Store component filters', () => {
<ComponentWithError> ✕
`);

await act(
await actAsync(
async () =>
(store.componentFilters = [utils.createDisplayNameFilter('Error')]),
);
@@ -470,7 +538,7 @@ describe('Store component filters', () => {
<ComponentWithWarning> ⚠
`);

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]
27 changes: 25 additions & 2 deletions packages/react-devtools-shared/src/__tests__/utils.js
Original file line number Diff line number Diff line change
@@ -21,6 +21,12 @@ import {ReactVersion} from '../../../../ReactVersions';

const requestedReactVersion = process.env.REACT_VERSION || ReactVersion;
export function getActDOMImplementation(): () => void | Promise<void> {
// 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<void> {
throw new Error("Couldn't find any available act implementation");
}

export function getActTestRendererImplementation(): () => void | Promise<void> {
// 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 RTR = require('react-test-renderer');
if (RTR.act) {
return RTR.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<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();

await actDOM(async () => {

0 comments on commit 8716706

Please sign in to comment.