@samitouri / QOS-React-2 / commits / 69fd78fe37

Update Float tests to check for specific errors (#26367)

I updated some of the Float tests that intentionally trigger an error to assert on the specific error message, rather than swallow any errors that may or may not happen.

Andrew Clark committed Mar 10, 2023 at 18:37 UTC 69fd78fe3783579dbea565987445e0646fafdd9f
2 files changed +109 -95
packages/internal-test-utils/ReactInternalTestUtils.js
+9 -3
@@ -104,7 +104,7 @@ ${diff(expectedLog, actualLog)}
104 throw error;
105 }
106
107 -export async function waitForThrow(expectedError: mixed) {
107 +export async function waitForThrow(expectedError: mixed): mixed {
108 assertYieldsWereCleared(SchedulerMock);
109
110 // Create the error object before doing any async work, to get a better
@@ -123,8 +123,14 @@ export async function waitForThrow(expectedError: mixed) {
123 try {
124 SchedulerMock.unstable_flushAllWithoutAsserting();
125 } catch (x) {
126 + if (expectedError === undefined) {
127 + // If no expected error was provided, then assume the caller is OK with
128 + // any error being thrown. We're returning the error so they can do
129 + // their own checks, if they wish.
130 + return x;
131 + }
132 if (equals(x, expectedError)) {
127 - return;
133 + return x;
134 }
135 if (
136 typeof expectedError === 'string' &&
@@ -133,7 +139,7 @@ export async function waitForThrow(expectedError: mixed) {
139 typeof x.message === 'string' &&
140 x.message.includes(expectedError)
141 ) {
136 - return;
142 + return x;
143 }
144 error.message = `
145 Expected error was not thrown.
packages/react-dom/src/__tests__/ReactDOMFloat-test.js
+100 -92
@@ -17,7 +17,6 @@ import {
17
18 let JSDOM;
19 let Stream;
20 -let Scheduler;
20 let React;
21 let ReactDOM;
22 let ReactDOMClient;
@@ -34,7 +33,7 @@ let hasErrored = false;
33 let fatalError = undefined;
34 let renderOptions;
35 let waitForAll;
37 -let assertLog;
36 +let waitForThrow;
37
38 function resetJSDOM(markup) {
39 // Test Environment
@@ -57,7 +56,6 @@ describe('ReactDOMFloat', () => {
56 beforeEach(() => {
57 jest.resetModules();
58 JSDOM = require('jsdom').JSDOM;
60 - Scheduler = require('scheduler');
59 React = require('react');
60 ReactDOM = require('react-dom');
61 ReactDOMClient = require('react-dom/client');
@@ -67,7 +65,7 @@ describe('ReactDOMFloat', () => {
65
66 const InternalTestUtils = require('internal-test-utils');
67 waitForAll = InternalTestUtils.waitForAll;
70 - assertLog = InternalTestUtils.assertLog;
68 + waitForThrow = InternalTestUtils.waitForThrow;
69
70 textCache = new Map();
71
@@ -261,23 +259,6 @@ describe('ReactDOMFloat', () => {
259 );
260 }
261
264 - function renderSafelyAndExpect(root, children) {
265 - root.render(children);
266 - return expect(() => {
267 - try {
268 - // TODO: Migrate this to waitForAll()
269 - Scheduler.unstable_flushAll();
270 - assertLog([]);
271 - } catch (e) {
272 - try {
273 - // TODO: Migrate this to waitForAll()
274 - Scheduler.unstable_flushAll();
275 - assertLog([]);
276 - } catch (f) {}
277 - }
278 - });
279 - }
280 -
262 // @gate enableFloat
263 it('can render resources before singletons', async () => {
264 const root = ReactDOMClient.createRoot(document);
@@ -382,15 +363,24 @@ describe('ReactDOMFloat', () => {
363 it('warns if you render resource-like elements above <head> or <body>', async () => {
364 const root = ReactDOMClient.createRoot(document);
365
385 - renderSafelyAndExpect(
386 - root,
387 - <>
388 - <noscript>foo</noscript>
389 - <html>
390 - <body>foo</body>
391 - </html>
392 - </>,
393 - ).toErrorDev(
366 + await expect(async () => {
367 + root.render(
368 + <>
369 + <noscript>foo</noscript>
370 + <html>
371 + <body>foo</body>
372 + </html>
373 + </>,
374 + );
375 + const aggregateError = await waitForThrow();
376 + expect(aggregateError.errors.length).toBe(2);
377 + expect(aggregateError.errors[0].message).toContain(
378 + 'Invalid insertion of NOSCRIPT',
379 + );
380 + expect(aggregateError.errors[1].message).toContain(
381 + 'The node to be removed is not a child of this node',
382 + );
383 + }).toErrorDev(
384 [
385 'Cannot render <noscript> outside the main document. Try moving it into the root <head> tag.',
386 'Warning: validateDOMNesting(...): <noscript> cannot appear as a child of <#document>.',
@@ -398,37 +388,50 @@ describe('ReactDOMFloat', () => {
388 {withoutStack: 1},
389 );
390
401 - renderSafelyAndExpect(
402 - root,
403 - <html>
404 - <template>foo</template>
405 - <body>foo</body>
406 - </html>,
407 - ).toErrorDev([
391 + await expect(async () => {
392 + root.render(
393 + <html>
394 + <template>foo</template>
395 + <body>foo</body>
396 + </html>,
397 + );
398 + await waitForAll([]);
399 + }).toErrorDev([
400 'Cannot render <template> outside the main document. Try moving it into the root <head> tag.',
401 'Warning: validateDOMNesting(...): <template> cannot appear as a child of <html>.',
402 ]);
403
412 - renderSafelyAndExpect(
413 - root,
414 - <html>
415 - <body>foo</body>
416 - <style>foo</style>
417 - </html>,
418 - ).toErrorDev([
404 + await expect(async () => {
405 + root.render(
406 + <html>
407 + <body>foo</body>
408 + <style>foo</style>
409 + </html>,
410 + );
411 + await waitForAll([]);
412 + }).toErrorDev([
413 'Cannot render a <style> outside the main document without knowing its precedence and a unique href key. React can hoist and deduplicate <style> tags if you provide a `precedence` prop along with an `href` prop that does not conflic with the `href` values used in any other hoisted <style> or <link rel="stylesheet" ...> tags. Note that hoisting <style> tags is considered an advanced feature that most will not use directly. Consider moving the <style> tag to the <head> or consider adding a `precedence="default"` and `href="some unique resource identifier"`, or move the <style> to the <style> tag.',
414 'Warning: validateDOMNesting(...): <style> cannot appear as a child of <html>.',
415 ]);
416
423 - renderSafelyAndExpect(
424 - root,
425 - <>
426 - <html>
427 - <body>foo</body>
428 - </html>
429 - <link rel="stylesheet" href="foo" />
430 - </>,
431 - ).toErrorDev(
417 + await expect(async () => {
418 + root.render(
419 + <>
420 + <html>
421 + <body>foo</body>
422 + </html>
423 + <link rel="stylesheet" href="foo" />
424 + </>,
425 + );
426 + const aggregateError = await waitForThrow();
427 + expect(aggregateError.errors.length).toBe(2);
428 + expect(aggregateError.errors[0].message).toContain(
429 + 'Invalid insertion of LINK',
430 + );
431 + expect(aggregateError.errors[1].message).toContain(
432 + 'The node to be removed is not a child of this node',
433 + );
434 + }).toErrorDev(
435 [
436 'Cannot render a <link rel="stylesheet" /> outside the main document without knowing its precedence. Consider adding precedence="default" or moving it into the root <head> tag.',
437 'Warning: validateDOMNesting(...): <link> cannot appear as a child of <#document>.',
@@ -436,40 +439,51 @@ describe('ReactDOMFloat', () => {
439 {withoutStack: 1},
440 );
441
439 - renderSafelyAndExpect(
440 - root,
441 - <>
442 - <html>
443 - <body>foo</body>
444 - <script href="foo" />
445 - </html>
446 - </>,
447 - ).toErrorDev([
442 + await expect(async () => {
443 + root.render(
444 + <>
445 + <html>
446 + <body>foo</body>
447 + <script href="foo" />
448 + </html>
449 + </>,
450 + );
451 + await waitForAll([]);
452 + }).toErrorDev([
453 'Cannot render a sync or defer <script> outside the main document without knowing its order. Try adding async="" or moving it into the root <head> tag.',
454 'Warning: validateDOMNesting(...): <script> cannot appear as a child of <html>.',
455 ]);
456
452 - renderSafelyAndExpect(
453 - root,
454 - <>
457 + await expect(async () => {
458 + root.render(
459 <html>
460 <script async={true} onLoad={() => {}} href="bar" />
461 <body>foo</body>
458 - </html>
459 - </>,
460 - ).toErrorDev([
462 + </html>,
463 + );
464 + await waitForAll([]);
465 + }).toErrorDev([
466 'Cannot render a <script> with onLoad or onError listeners outside the main document. Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or somewhere in the <body>.',
467 ]);
468
464 - renderSafelyAndExpect(
465 - root,
466 - <>
467 - <link rel="foo" onLoad={() => {}} href="bar" />
468 - <html>
469 - <body>foo</body>
470 - </html>
471 - </>,
472 - ).toErrorDev(
469 + await expect(async () => {
470 + root.render(
471 + <>
472 + <link rel="foo" onLoad={() => {}} href="bar" />
473 + <html>
474 + <body>foo</body>
475 + </html>
476 + </>,
477 + );
478 + const aggregateError = await waitForThrow();
479 + expect(aggregateError.errors.length).toBe(2);
480 + expect(aggregateError.errors[0].message).toContain(
481 + 'Invalid insertion of LINK',
482 + );
483 + expect(aggregateError.errors[1].message).toContain(
484 + 'The node to be removed is not a child of this node',
485 + );
486 + }).toErrorDev(
487 [
488 'Cannot render a <link> with onLoad or onError listeners outside the main document. Try removing onLoad={...} and onError={...} or moving it into the root <head> tag or somewhere in the <body>.',
489 ],
@@ -5638,22 +5652,16 @@ background-color: green;
5652 },
5653 },
5654 );
5641 - try {
5642 - await expect(async () => {
5643 - await waitForAll([]);
5644 - }).toErrorDev(
5645 - [
5646 - 'Warning: Text content did not match. Server: "server" Client: "client"',
5647 - 'Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.',
5648 - ],
5649 - {withoutStack: 1},
5650 - );
5651 - } catch (e) {
5652 - // When gates are false this test fails on a DOMException if you don't clear the scheduler after catching.
5653 - // When gates are true this branch should not be hit
5655 +
5656 + await expect(async () => {
5657 await waitForAll([]);
5655 - throw e;
5656 - }
5658 + }).toErrorDev(
5659 + [
5660 + 'Warning: Text content did not match. Server: "server" Client: "client"',
5661 + 'Warning: An error occurred during hydration. The server HTML was replaced with client content in <#document>.',
5662 + ],
5663 + {withoutStack: 1},
5664 + );
5665 expect(getMeaningfulChildren(document)).toEqual(
5666 <html>
5667 <head>