@samitouri / QOS-React-1 / commits / 12d56fca3d

chore: use versioned render in storeComponentFilters test (#28241)

Ruslan Lesiutin committed Feb 6, 2024 at 16:25 UTC 12d56fca3da06328ac61d86976afa728b97b49d6
2 files changed +164 -73
packages/react-devtools-shared/src/__tests__/storeComponentFilters-test.js
+139 -71
@@ -10,25 +10,18 @@
10 import type {FrontendBridge} from 'react-devtools-shared/src/bridge';
11 import type Store from 'react-devtools-shared/src/devtools/store';
12
13 +import {
14 + getLegacyRenderImplementation,
15 + getVersionedRenderImplementation,
16 +} from './utils';
17 +
18 describe('Store component filters', () => {
19 let React;
20 let Types;
21 let bridge: FrontendBridge;
17 - let legacyRender;
22 let store: Store;
23 let utils;
20 -
21 - const act = async (callback: Function) => {
22 - if (React.act != null) {
23 - await React.act(callback);
24 - } else if (React.unstable_act != null) {
25 - await React.unstable_act(callback);
26 - } else {
27 - callback();
28 - }
29 -
30 - jest.runAllTimers(); // Flush Bridge operations
31 - };
24 + let actAsync;
25
26 beforeEach(() => {
27 bridge = global.bridge;
@@ -41,12 +34,14 @@ describe('Store component filters', () => {
34 Types = require('react-devtools-shared/src/frontend/types');
35 utils = require('./utils');
36
44 - legacyRender = utils.legacyRender;
37 + actAsync = utils.actAsync;
38 });
39
40 + const {render} = getVersionedRenderImplementation();
41 +
42 // @reactVersion >= 16.0
43 it('should throw if filters are updated while profiling', async () => {
49 - await act(async () => store.profilerStore.startProfiling());
44 + await actAsync(async () => store.profilerStore.startProfiling());
45 expect(() => (store.componentFilters = [])).toThrow(
46 'Cannot modify filter preferences while profiling',
47 );
@@ -61,12 +56,11 @@ describe('Store component filters', () => {
56 }
57 const FunctionComponent = () => <div>Hi</div>;
58
64 - await act(async () =>
65 - legacyRender(
59 + await actAsync(async () =>
60 + render(
61 <ClassComponent>
62 <FunctionComponent />
63 </ClassComponent>,
69 - document.createElement('div'),
64 ),
65 );
66 expect(store).toMatchInlineSnapshot(`
@@ -77,7 +71,7 @@ describe('Store component filters', () => {
71 <div>
72 `);
73
80 - await act(
74 + await actAsync(
75 async () =>
76 (store.componentFilters = [
77 utils.createElementTypeFilter(Types.ElementTypeHostComponent),
@@ -89,7 +83,7 @@ describe('Store component filters', () => {
83 <FunctionComponent>
84 `);
85
92 - await act(
86 + await actAsync(
87 async () =>
88 (store.componentFilters = [
89 utils.createElementTypeFilter(Types.ElementTypeClass),
@@ -102,7 +96,7 @@ describe('Store component filters', () => {
96 <div>
97 `);
98
105 - await act(
99 + await actAsync(
100 async () =>
101 (store.componentFilters = [
102 utils.createElementTypeFilter(Types.ElementTypeClass),
@@ -115,7 +109,7 @@ describe('Store component filters', () => {
109 <div>
110 `);
111
118 - await act(
112 + await actAsync(
113 async () =>
114 (store.componentFilters = [
115 utils.createElementTypeFilter(Types.ElementTypeClass, false),
@@ -130,7 +124,7 @@ describe('Store component filters', () => {
124 <div>
125 `);
126
133 - await act(async () => (store.componentFilters = []));
127 + await actAsync(async () => (store.componentFilters = []));
128 expect(store).toMatchInlineSnapshot(`
129 [root]
130 ▾ <ClassComponent>
@@ -144,16 +138,14 @@ describe('Store component filters', () => {
138 it('should ignore invalid ElementTypeRoot filter', async () => {
139 const Component = () => <div>Hi</div>;
140
147 - await act(async () =>
148 - legacyRender(<Component />, document.createElement('div')),
149 - );
141 + await actAsync(async () => render(<Component />));
142 expect(store).toMatchInlineSnapshot(`
143 [root]
144 ▾ <Component>
145 <div>
146 `);
147
156 - await act(
148 + await actAsync(
149 async () =>
150 (store.componentFilters = [
151 utils.createElementTypeFilter(Types.ElementTypeRoot),
@@ -174,14 +166,13 @@ describe('Store component filters', () => {
166 const Bar = () => <Text label="bar" />;
167 const Baz = () => <Text label="baz" />;
168
177 - await act(async () =>
178 - legacyRender(
169 + await actAsync(async () =>
170 + render(
171 <React.Fragment>
172 <Foo />
173 <Bar />
174 <Baz />
175 </React.Fragment>,
184 - document.createElement('div'),
176 ),
177 );
178 expect(store).toMatchInlineSnapshot(`
@@ -194,7 +185,7 @@ describe('Store component filters', () => {
185 <Text>
186 `);
187
197 - await act(
188 + await actAsync(
189 async () =>
190 (store.componentFilters = [utils.createDisplayNameFilter('Foo')]),
191 );
@@ -207,7 +198,7 @@ describe('Store component filters', () => {
198 <Text>
199 `);
200
210 - await act(
201 + await actAsync(
202 async () =>
203 (store.componentFilters = [utils.createDisplayNameFilter('Ba')]),
204 );
@@ -219,7 +210,7 @@ describe('Store component filters', () => {
210 <Text>
211 `);
212
222 - await act(
213 + await actAsync(
214 async () =>
215 (store.componentFilters = [utils.createDisplayNameFilter('B.z')]),
216 );
@@ -237,16 +228,14 @@ describe('Store component filters', () => {
228 it('should filter by path', async () => {
229 const Component = () => <div>Hi</div>;
230
240 - await act(async () =>
241 - legacyRender(<Component />, document.createElement('div')),
242 - );
231 + await actAsync(async () => render(<Component />));
232 expect(store).toMatchInlineSnapshot(`
233 [root]
234 ▾ <Component>
235 <div>
236 `);
237
249 - await act(
238 + await actAsync(
239 async () =>
240 (store.componentFilters = [
241 utils.createLocationFilter(__filename.replace(__dirname, '')),
@@ -255,7 +244,7 @@ describe('Store component filters', () => {
244
245 expect(store).toMatchInlineSnapshot(`[root]`);
246
258 - await act(
247 + await actAsync(
248 async () =>
249 (store.componentFilters = [
250 utils.createLocationFilter('this:is:a:made:up:path'),
@@ -277,7 +266,7 @@ describe('Store component filters', () => {
266 const Bar = () => <Foo />;
267 Bar.displayName = 'Bar(Foo(Component))';
268
280 - await act(async () => legacyRender(<Bar />, document.createElement('div')));
269 + await actAsync(async () => render(<Bar />));
270 expect(store).toMatchInlineSnapshot(`
271 [root]
272 ▾ <Component> [Bar][Foo]
@@ -286,7 +275,7 @@ describe('Store component filters', () => {
275 <div>
276 `);
277
289 - await act(
278 + await actAsync(
279 async () => (store.componentFilters = [utils.createHOCFilter(true)]),
280 );
281 expect(store).toMatchInlineSnapshot(`
@@ -295,7 +284,7 @@ describe('Store component filters', () => {
284 <div>
285 `);
286
298 - await act(
287 + await actAsync(
288 async () => (store.componentFilters = [utils.createHOCFilter(false)]),
289 );
290 expect(store).toMatchInlineSnapshot(`
@@ -309,7 +298,7 @@ describe('Store component filters', () => {
298
299 // @reactVersion >= 16.0
300 it('should not send a bridge update if the set of enabled filters has not changed', async () => {
312 - await act(
301 + await actAsync(
302 async () => (store.componentFilters = [utils.createHOCFilter(true)]),
303 );
304
@@ -317,21 +306,21 @@ describe('Store component filters', () => {
306 throw Error('Unexpected component update');
307 });
308
320 - await act(
309 + await actAsync(
310 async () =>
311 (store.componentFilters = [
312 utils.createHOCFilter(false),
313 utils.createHOCFilter(true),
314 ]),
315 );
327 - await act(
316 + await actAsync(
317 async () =>
318 (store.componentFilters = [
319 utils.createHOCFilter(true),
320 utils.createLocationFilter('abc', false),
321 ]),
322 );
334 - await act(
323 + await actAsync(
324 async () =>
325 (store.componentFilters = [
326 utils.createHOCFilter(true),
@@ -363,10 +352,7 @@ describe('Store component filters', () => {
352 utils.createElementTypeFilter(Types.ElementTypeSuspense),
353 ];
354
366 - const container = document.createElement('div');
367 - await act(async () =>
368 - legacyRender(<Wrapper shouldSuspend={true} />, container),
369 - );
355 + await actAsync(async () => render(<Wrapper shouldSuspend={true} />));
356 expect(store).toMatchInlineSnapshot(`
357 [root]
358 ▾ <Wrapper>
@@ -374,18 +360,14 @@ describe('Store component filters', () => {
360 <div>
361 `);
362
377 - await act(async () =>
378 - legacyRender(<Wrapper shouldSuspend={false} />, container),
379 - );
363 + await actAsync(async () => render(<Wrapper shouldSuspend={false} />));
364 expect(store).toMatchInlineSnapshot(`
365 [root]
366 ▾ <Wrapper>
367 <Component>
368 `);
369
386 - await act(async () =>
387 - legacyRender(<Wrapper shouldSuspend={true} />, container),
388 - );
370 + await actAsync(async () => render(<Wrapper shouldSuspend={true} />));
371 expect(store).toMatchInlineSnapshot(`
372 [root]
373 ▾ <Wrapper>
@@ -395,8 +377,11 @@ describe('Store component filters', () => {
377 });
378
379 describe('inline errors and warnings', () => {
380 + const {render: legacyRender} = getLegacyRenderImplementation();
381 +
382 // @reactVersion >= 17.0
399 - it('only counts for unfiltered components', async () => {
383 + // @reactVersion <= 18.2
384 + it('only counts for unfiltered components (legacy render)', async () => {
385 function ComponentWithWarning() {
386 console.warn('test-only: render warning');
387 return null;
@@ -411,15 +396,7 @@ describe('Store component filters', () => {
396 return null;
397 }
398
414 - // HACK This require() is needed (somewhere in the test) for this case to pass.
415 - // Without it, the legacyRender() call below causes this test to fail
416 - // because it requires "react-dom" for the first time,
417 - // which causes the console error() and warn() methods to be overridden again,
418 - // effectively disconnecting the DevTools override in 'react-devtools-shared/src/backend/console'.
419 - require('react-dom');
420 -
421 - const container = document.createElement('div');
422 - await act(
399 + await actAsync(
400 async () =>
401 (store.componentFilters = [
402 utils.createDisplayNameFilter('Warning'),
@@ -433,7 +410,6 @@ describe('Store component filters', () => {
410 <ComponentWithWarning />
411 <ComponentWithWarningAndError />
412 </React.Fragment>,
436 - container,
413 );
414 });
415
@@ -441,7 +417,99 @@ describe('Store component filters', () => {
417 expect(store.errorCount).toBe(0);
418 expect(store.warningCount).toBe(0);
419
444 - await act(async () => (store.componentFilters = []));
420 + await actAsync(async () => (store.componentFilters = []));
421 + expect(store).toMatchInlineSnapshot(`
422 + ✕ 2, ⚠ 2
423 + [root]
424 + <ComponentWithError> ✕
425 + <ComponentWithWarning> ⚠
426 + <ComponentWithWarningAndError> ✕⚠
427 + `);
428 +
429 + await actAsync(
430 + async () =>
431 + (store.componentFilters = [utils.createDisplayNameFilter('Warning')]),
432 + );
433 + expect(store).toMatchInlineSnapshot(`
434 + ✕ 1, ⚠ 0
435 + [root]
436 + <ComponentWithError> ✕
437 + `);
438 +
439 + await actAsync(
440 + async () =>
441 + (store.componentFilters = [utils.createDisplayNameFilter('Error')]),
442 + );
443 + expect(store).toMatchInlineSnapshot(`
444 + ✕ 0, ⚠ 1
445 + [root]
446 + <ComponentWithWarning> ⚠
447 + `);
448 +
449 + await actAsync(
450 + async () =>
451 + (store.componentFilters = [
452 + utils.createDisplayNameFilter('Warning'),
453 + utils.createDisplayNameFilter('Error'),
454 + ]),
455 + );
456 + expect(store).toMatchInlineSnapshot(`[root]`);
457 + expect(store.errorCount).toBe(0);
458 + expect(store.warningCount).toBe(0);
459 +
460 + await actAsync(async () => (store.componentFilters = []));
461 + expect(store).toMatchInlineSnapshot(`
462 + ✕ 2, ⚠ 2
463 + [root]
464 + <ComponentWithError> ✕
465 + <ComponentWithWarning> ⚠
466 + <ComponentWithWarningAndError> ✕⚠
467 + `);
468 + });
469 +
470 + // @reactVersion >= 18
471 + it('only counts for unfiltered components (createRoot)', async () => {
472 + function ComponentWithWarning() {
473 + console.warn('test-only: render warning');
474 + return null;
475 + }
476 + function ComponentWithError() {
477 + console.error('test-only: render error');
478 + return null;
479 + }
480 + function ComponentWithWarningAndError() {
481 + console.error('test-only: render error');
482 + console.warn('test-only: render warning');
483 + return null;
484 + }
485 +
486 + await actAsync(
487 + async () =>
488 + (store.componentFilters = [
489 + utils.createDisplayNameFilter('Warning'),
490 + utils.createDisplayNameFilter('Error'),
491 + ]),
492 + );
493 +
494 + utils.act(
495 + () =>
496 + utils.withErrorsOrWarningsIgnored(['test-only:'], () => {
497 + render(
498 + <React.Fragment>
499 + <ComponentWithError />
500 + <ComponentWithWarning />
501 + <ComponentWithWarningAndError />
502 + </React.Fragment>,
503 + );
504 + }),
505 + false,
506 + );
507 +
508 + expect(store).toMatchInlineSnapshot(``);
509 + expect(store.errorCount).toBe(0);
510 + expect(store.warningCount).toBe(0);
511 +
512 + await actAsync(async () => (store.componentFilters = []));
513 expect(store).toMatchInlineSnapshot(`
514 ✕ 2, ⚠ 2
515 [root]
@@ -450,7 +518,7 @@ describe('Store component filters', () => {
518 <ComponentWithWarningAndError> ✕⚠
519 `);
520
453 - await act(
521 + await actAsync(
522 async () =>
523 (store.componentFilters = [utils.createDisplayNameFilter('Warning')]),
524 );
@@ -460,7 +528,7 @@ describe('Store component filters', () => {
528 <ComponentWithError> ✕
529 `);
530
463 - await act(
531 + await actAsync(
532 async () =>
533 (store.componentFilters = [utils.createDisplayNameFilter('Error')]),
534 );
@@ -470,7 +538,7 @@ describe('Store component filters', () => {
538 <ComponentWithWarning> ⚠
539 `);
540
473 - await act(
541 + await actAsync(
542 async () =>
543 (store.componentFilters = [
544 utils.createDisplayNameFilter('Warning'),
@@ -481,7 +549,7 @@ describe('Store component filters', () => {
549 expect(store.errorCount).toBe(0);
550 expect(store.warningCount).toBe(0);
551
484 - await act(async () => (store.componentFilters = []));
552 + await actAsync(async () => (store.componentFilters = []));
553 expect(store).toMatchInlineSnapshot(`
554 ✕ 2, ⚠ 2
555 [root]
packages/react-devtools-shared/src/__tests__/utils.js
+25 -2
@@ -21,6 +21,12 @@ import {ReactVersion} from '../../../../ReactVersions';
21
22 const requestedReactVersion = process.env.REACT_VERSION || ReactVersion;
23 export function getActDOMImplementation(): () => void | Promise<void> {
24 + // This is for React < 17, where act wasn't shipped yet.
25 + if (semver.lt(requestedReactVersion, '17.0.0')) {
26 + require('react-dom/test-utils');
27 + return cb => cb();
28 + }
29 +
30 // This is for React < 18, where act was distributed in react-dom/test-utils.
31 if (semver.lt(requestedReactVersion, '18.0.0')) {
32 const ReactDOMTestUtils = require('react-dom/test-utils');
@@ -41,13 +47,30 @@ export function getActDOMImplementation(): () => void | Promise<void> {
47 throw new Error("Couldn't find any available act implementation");
48 }
49
50 +export function getActTestRendererImplementation(): () => void | Promise<void> {
51 + // This is for React < 17, where act wasn't shipped yet.
52 + if (semver.lt(requestedReactVersion, '17.0.0')) {
53 + require('react-test-renderer');
54 + return cb => cb();
55 + }
56 +
57 + const RTR = require('react-test-renderer');
58 + if (RTR.act) {
59 + return RTR.act;
60 + }
61 +
62 + throw new Error(
63 + "Couldn't find any available act implementation in react-test-renderer",
64 + );
65 +}
66 +
67 export function act(
68 callback: Function,
69 recursivelyFlush: boolean = true,
70 ): void {
71 // act from react-test-renderer has some side effects on React DevTools
72 // it injects the renderer for DevTools, see ReactTestRenderer.js
50 - const {act: actTestRenderer} = require('react-test-renderer');
73 + const actTestRenderer = getActTestRendererImplementation();
74 const actDOM = getActDOMImplementation();
75
76 actDOM(() => {
@@ -74,7 +97,7 @@ export async function actAsync(
97 ): Promise<void> {
98 // act from react-test-renderer has some side effects on React DevTools
99 // it injects the renderer for DevTools, see ReactTestRenderer.js
77 - const {act: actTestRenderer} = require('react-test-renderer');
100 + const actTestRenderer = getActTestRendererImplementation();
101 const actDOM = getActDOMImplementation();
102
103 await actDOM(async () => {