From 9822c2285143022373870e45b611edd8bb6d820f Mon Sep 17 00:00:00 2001 From: Rick Hanlon Date: Wed, 10 Apr 2024 17:58:28 -0400 Subject: [PATCH] Support local withoutStack options --- .../__tests__/ReactInternalTestUtils-test.js | 939 ++++++++++++------ packages/internal-test-utils/consoleMock.js | 180 ++-- 2 files changed, 752 insertions(+), 367 deletions(-) diff --git a/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js b/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js index f7b232171c02a..af2a447f49819 100644 --- a/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js +++ b/packages/internal-test-utils/__tests__/ReactInternalTestUtils-test.js @@ -309,7 +309,7 @@ describe('ReactInternalTestUtils console mocks', () => { }); // Helper method to capture assertion failure. -const expectToWarnAndToThrow = expectBlock => { +const expectToThrowFailure = expectBlock => { let caughtError; try { expectBlock(); @@ -321,7 +321,7 @@ const expectToWarnAndToThrow = expectBlock => { }; // Helper method to capture assertion failure with act. -const awaitExpectToWarnAndToThrow = async expectBlock => { +const awaitExpectToThrowFailure = async expectBlock => { let caughtError; try { await expectBlock(); @@ -372,7 +372,7 @@ describe('ReactInternalTestUtils console assertions', () => { await act(() => { root.render(); }); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await act(() => { root.render(); }); @@ -392,7 +392,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if first expected log is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Wow'); console.log('Bye'); assertConsoleLogDev(['Hi', 'Wow', 'Bye']); @@ -413,7 +413,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if middle expected log is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi'); console.log('Bye'); assertConsoleLogDev(['Hi', 'Wow', 'Bye']); @@ -434,7 +434,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if last expected log is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi'); console.log('Wow'); assertConsoleLogDev(['Hi', 'Wow', 'Bye']); @@ -455,7 +455,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if first received log is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi'); console.log('Wow'); console.log('Bye'); @@ -477,7 +477,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if middle received log is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi'); console.log('Wow'); console.log('Bye'); @@ -499,7 +499,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if last received log is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi'); console.log('Wow'); console.log('Bye'); @@ -521,7 +521,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if both expected and received mismatch', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi'); console.log('Wow'); console.log('Bye'); @@ -544,7 +544,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if both expected and received mismatch with multiple lines', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi\nFoo'); console.log('Wow\nBar'); console.log('Bye\nBaz'); @@ -566,8 +566,22 @@ describe('ReactInternalTestUtils console assertions', () => { }); // @gate __DEV__ - it('fails if withoutStack passed to assertConsoleLogDev', () => { - const message = expectToWarnAndToThrow(() => { + it('fails if local withoutStack passed to assertConsoleLogDev', () => { + const message = expectToThrowFailure(() => { + console.log('Hello'); + assertConsoleLogDev([['Hello', {withoutStack: true}]]); + }); + + expect(message).toMatchInlineSnapshot(` + "assertConsoleLogDev(expected) + + Do not pass withoutStack to assertConsoleLogDev logs, console.log does not have component stacks." + `); + }); + + // @gate __DEV__ + it('fails if global withoutStack passed to assertConsoleLogDev', () => { + const message = expectToThrowFailure(() => { console.log('Hello'); assertConsoleLogDev(['Hello'], {withoutStack: true}); }); @@ -583,7 +597,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the args is greater than %s argument number', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi %s', 'Sara', 'extra'); assertConsoleLogDev(['Hi']); }); @@ -597,7 +611,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the args is greater than %s argument number for multiple logs', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi %s', 'Sara', 'extra'); console.log('Bye %s', 'Sara', 'extra'); assertConsoleLogDev(['Hi', 'Bye']); @@ -615,7 +629,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the %s argument number is greater than args', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi %s'); assertConsoleLogDev(['Hi']); }); @@ -629,7 +643,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the %s argument number is greater than args for multiple logs', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi %s'); console.log('Bye %s'); assertConsoleLogDev(['Hi', 'Bye']); @@ -647,7 +661,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if first arg is not an array', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.log('Hi'); console.log('Bye'); assertConsoleLogDev('Hi', 'Bye'); @@ -680,7 +694,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.log('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitFor(['foo', 'bar']); }); expect(message).toMatchInlineSnapshot(` @@ -722,7 +736,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.log('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForThrow('Oh no!'); }); expect(message).toMatchInlineSnapshot(` @@ -756,7 +770,7 @@ describe('ReactInternalTestUtils console assertions', () => { root.render(); console.log('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForPaint(['Urgent: B, Deferred: A']); }); @@ -791,7 +805,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.log('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForAll(['foo', 'bar', 'baz']); }); expect(message).toMatchInlineSnapshot(` @@ -826,7 +840,7 @@ describe('ReactInternalTestUtils console assertions', () => { assertLog([]); await waitForAll(['foo', 'bar', 'baz']); - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { expect(root).toMatchRenderedOutput(
foobarbaz
); }); expect(message).toMatchInlineSnapshot(` @@ -846,7 +860,7 @@ describe('ReactInternalTestUtils console assertions', () => { describe('assertConsoleWarnDev', () => { // @gate __DEV__ - it('passes if a warning contains a stack', () => { + it('passes if an warning contains a stack', () => { console.warn('Hello\n in div'); assertConsoleWarnDev(['Hello']); }); @@ -859,29 +873,49 @@ describe('ReactInternalTestUtils console assertions', () => { assertConsoleWarnDev(['Hello', 'Good day', 'Bye']); }); - // @gate __DEV__ - it('passes if warnings without stack explicitly opt out', () => { - console.warn('Hello'); - assertConsoleWarnDev(['Hello'], {withoutStack: true}); + it('fails if act is called without assertConsoleWarnDev', async () => { + const Yield = ({id}) => { + console.warn(id); + return id; + }; - console.warn('Hello'); - console.warn('Good day'); - console.warn('Bye'); + function App() { + return ( +
+ + + +
+ ); + } - assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], {withoutStack: true}); - }); + const root = ReactNoop.createRoot(); + await act(() => { + root.render(); + }); + const message = await awaitExpectToThrowFailure(async () => { + await act(() => { + root.render(); + }); + }); - // @gate __DEV__ - it('passes when expected withoutStack number matches the actual one', () => { - console.warn('Hello\n in div'); - console.warn('Good day'); - console.warn('Bye\n in div'); - assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], {withoutStack: 1}); + expect(message).toMatchInlineSnapshot(` + "asserConsoleLogsCleared(expected) + + console.warn was called without assertConsoleWarnDev: + + A + + B + + C + + You must call one of the assertConsoleDev helpers between each act call." + `); }); - it('fails if act is called without assertConsoleWarnDev', async () => { + it('fails if act is called without any assertConsoleDev helpers', async () => { const Yield = ({id}) => { + console.log(id); console.warn(id); + console.error(id); return id; }; @@ -899,7 +933,7 @@ describe('ReactInternalTestUtils console assertions', () => { await act(() => { root.render(); }); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await act(() => { root.render(); }); @@ -908,18 +942,28 @@ describe('ReactInternalTestUtils console assertions', () => { expect(message).toMatchInlineSnapshot(` "asserConsoleLogsCleared(expected) + console.log was called without assertConsoleLogDev: + + A + + B + + C + console.warn was called without assertConsoleWarnDev: + A + B + C + console.error was called without assertConsoleErrorDev: + + A + + B + + C + You must call one of the assertConsoleDev helpers between each act call." `); }); // @gate __DEV__ it('fails if first expected warning is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Wow \n in div'); console.warn('Bye \n in div'); assertConsoleWarnDev(['Hi', 'Wow', 'Bye']); @@ -942,7 +986,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if middle expected warning is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Bye \n in div'); assertConsoleWarnDev(['Hi', 'Wow', 'Bye']); @@ -965,7 +1009,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if last expected warning is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Wow \n in div'); assertConsoleWarnDev(['Hi', 'Wow', 'Bye']); @@ -988,7 +1032,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if first received warning is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Wow \n in div'); console.warn('Bye \n in div'); @@ -1012,7 +1056,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if middle received warning is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Wow \n in div'); console.warn('Bye \n in div'); @@ -1036,7 +1080,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if last received warning is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Wow \n in div'); console.warn('Bye \n in div'); @@ -1060,7 +1104,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if only warning does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hello'); assertConsoleWarnDev(['Hello']); }); @@ -1070,13 +1114,14 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Hello" - If this warning intentionally omits the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." + If this warning should omit a component stack, pass [log, {withoutStack: true}]. + If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." `); }); // @gate __DEV__ it('fails if first warning does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hello'); console.warn('Good day\n in div'); console.warn('Bye\n in div'); @@ -1088,12 +1133,14 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Hello" - If this warning intentionally omits the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." + If this warning should omit a component stack, pass [log, {withoutStack: true}]. + If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." `); }); + // @gate __DEV__ it('fails if middle warning does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hello\n in div'); console.warn('Good day'); console.warn('Bye\n in div'); @@ -1105,13 +1152,14 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Good day" - If this warning intentionally omits the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." + If this warning should omit a component stack, pass [log, {withoutStack: true}]. + If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." `); }); // @gate __DEV__ it('fails if last warning does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hello\n in div'); console.warn('Good day\n in div'); console.warn('Bye'); @@ -1123,13 +1171,14 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Bye" - If this warning intentionally omits the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." + If this warning should omit a component stack, pass [log, {withoutStack: true}]. + If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." `); }); // @gate __DEV__ it('fails if all warnings do not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hello'); console.warn('Good day'); console.warn('Bye'); @@ -1147,124 +1196,278 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Bye" - If this warning intentionally omits the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." + If this warning should omit a component stack, pass [log, {withoutStack: true}]. + If all warnings should omit the component stack, add {withoutStack: true} to the assertConsoleWarnDev call." `); }); - // @gate __DEV__ - it('fails if only warning is not expected to have a stack, but does', () => { - const message = expectToWarnAndToThrow(() => { - console.warn('Hello\n in div'); + describe('global withoutStack', () => { + // @gate __DEV__ + it('passes if warnings without stack explicitly opt out', () => { + console.warn('Hello'); assertConsoleWarnDev(['Hello'], {withoutStack: true}); - }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleWarnDev(expected) - Unexpected component stack for: - "Hello " - - If this warning intentionally includes the component stack, remove {withoutStack: true} from the assertConsoleWarnDev() call. - If you have a mix of warnings with and without stack in one assertConsoleWarnDev() call, pass {withoutStack: N} where N is the number of warnings without stacks." - `); - }); - - // @gate __DEV__ - it('fails if warnings are not expected to have a stack, but some do', () => { - const message = expectToWarnAndToThrow(() => { - console.warn('Hello\n in div'); + console.warn('Hello'); console.warn('Good day'); - console.warn('Bye\n in div'); + console.warn('Bye'); + assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], { withoutStack: true, }); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleWarnDev(expected) - Unexpected component stack for: - "Hello " + // @gate __DEV__ + it('fails if withoutStack is invalid null value', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi'); + assertConsoleWarnDev(['Hi'], {withoutStack: null}); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) - Unexpected component stack for: - "Bye " + The second argument must be {withoutStack: true}. - If this warning intentionally includes the component stack, remove {withoutStack: true} from the assertConsoleWarnDev() call. - If you have a mix of warnings with and without stack in one assertConsoleWarnDev() call, pass {withoutStack: N} where N is the number of warnings without stacks." - `); - }); + Instead received {"withoutStack":null}." + `); + assertConsoleWarnDev(['Hi'], {withoutStack: true}); + }); - // @gate __DEV__ - it('fails if expected withoutStack number does not match the actual one', () => { - const message = expectToWarnAndToThrow(() => { + // @gate __DEV__ + it('fails if withoutStack is invalid {} value', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi'); + assertConsoleWarnDev(['Hi'], {withoutStack: {}}); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + The second argument must be {withoutStack: true}. + + Instead received {"withoutStack":{}}." + `); + assertConsoleWarnDev(['Hi'], {withoutStack: true}); + }); + + // @gate __DEV__ + it('fails if withoutStack is invalid string value', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi'); + assertConsoleWarnDev(['Hi'], {withoutStack: 'haha'}); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + The second argument must be {withoutStack: true}. + + Instead received {"withoutStack":"haha"}." + `); + assertConsoleWarnDev(['Hi'], {withoutStack: true}); + }); + + // @gate __DEV__ + it('fails if only warning is not expected to have a stack, but does', () => { + const message = expectToThrowFailure(() => { + console.warn('Hello\n in div'); + assertConsoleWarnDev(['Hello'], {withoutStack: true}); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Unexpected component stack for: + "Hello " + + If this warning should include a component stack, remove {withoutStack: true} from this warning. + If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call." + `); + }); + + // @gate __DEV__ + it('fails if warnings are not expected to have a stack, but some do', () => { + const message = expectToThrowFailure(() => { + console.warn('Hello\n in div'); + console.warn('Good day'); + console.warn('Bye\n in div'); + assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], { + withoutStack: true, + }); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Unexpected component stack for: + "Hello " + + Unexpected component stack for: + "Bye " + + If this warning should include a component stack, remove {withoutStack: true} from this warning. + If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call." + `); + }); + }); + describe('local withoutStack', () => { + // @gate __DEV__ + it('passes when expected withoutStack logs matches the actual logs', () => { console.warn('Hello\n in div'); console.warn('Good day'); console.warn('Bye\n in div'); - assertConsoleWarnDev(['Hello', 'Good day', 'Bye'], { - withoutStack: 4, + assertConsoleWarnDev([ + 'Hello', + ['Good day', {withoutStack: true}], + 'Bye', + ]); + }); + + // @gate __DEV__ + it('fails if withoutStack is invalid null value', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi'); + assertConsoleWarnDev([['Hi', {withoutStack: null}]]); }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Log entries that are arrays must be of the form [string, {withoutStack: true}] + + Instead received [string, {"withoutStack":null}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleWarnDev(expected) - Expected 4 warnings without a component stack but received 1: - - Expected warnings - + Received warnings + // @gate __DEV__ + it('fails if withoutStack is invalid {} value', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi'); + assertConsoleWarnDev([['Hi', {withoutStack: {}}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) - - Hello - + Hello - Good day - - Bye - + Bye " - `); - }); + Log entries that are arrays must be of the form [string, {withoutStack: true}] - // @gate __DEV__ - it('fails if withoutStack is invalid null value', () => { - const message = expectToWarnAndToThrow(() => { - console.warn('Hi'); - assertConsoleWarnDev(['Hi'], {withoutStack: null}); + Instead received [string, {"withoutStack":{}}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleWarnDev(expected) - The second argument for assertConsoleWarnDev(), when specified, must be an object. It may have a property called "withoutStack" whose value may be a boolean or number. Instead received object." - `); + // @gate __DEV__ + it('fails if withoutStack is invalid string value', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi'); + assertConsoleWarnDev([['Hi', {withoutStack: 'haha'}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) - assertConsoleWarnDev(['Hi'], {withoutStack: true}); - }); + Log entries that are arrays must be of the form [string, {withoutStack: true}] - // @gate __DEV__ - it('fails if withoutStack is invalid {} value', () => { - const message = expectToWarnAndToThrow(() => { - console.warn('Hi'); - assertConsoleWarnDev(['Hi'], {withoutStack: {}}); + Instead received [string, {"withoutStack":"haha"}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleWarnDev(expected) - The second argument for assertConsoleWarnDev(), when specified, must be an object. It may have a property called "withoutStack" whose value may be a boolean or number. Instead received object." - `); + // @gate __DEV__ + it('fails if withoutStack is invalid number value', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi'); + assertConsoleWarnDev([['Hi', {withoutStack: 4}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) - assertConsoleWarnDev(['Hi'], {withoutStack: true}); - }); + Log entries that are arrays must be of the form [string, {withoutStack: true}] - // @gate __DEV__ - it('fails if withoutStack is invalid string value', () => { - const message = expectToWarnAndToThrow(() => { - console.warn('Hi'); - assertConsoleWarnDev(['Hi'], {withoutStack: 'haha'}); + Instead received [string, {"withoutStack":4}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleWarnDev(expected) - The second argument for assertConsoleWarnDev(), when specified, must be an object. It may have a property called "withoutStack" whose value may be a boolean or number. Instead received string." - `); + // @gate __DEV__ + it('fails if you forget to wrap local withoutStack in array', () => { + const message = expectToThrowFailure(() => { + console.warn('Hello\n in div'); + console.warn('Bye\n in div'); + assertConsoleWarnDev(['Hello', {withoutStack: true}, 'Bye']); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Did you forget to wrap a log with withoutStack in an array? + + The expected message for assertConsoleWarnDev() must be a string or an array of length 2. - assertConsoleWarnDev(['Hi'], {withoutStack: true}); + Instead received {"withoutStack":true}." + `); + }); + + // @gate __DEV__ + it('fails if you wrap in an array unnecessarily', () => { + const message = expectToThrowFailure(() => { + console.warn('Hello'); + assertConsoleWarnDev([['Hello']]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Did you forget to remove the array around the log? + + The expected message for assertConsoleWarnDev() must be a string or an array of length 2, but there's only one item in the array. If this is intentional, remove the extra array." + `); + }); + + // @gate __DEV__ + it('fails if only warning is not expected to have a stack, but does', () => { + const message = expectToThrowFailure(() => { + console.warn('Hello\n in div'); + assertConsoleWarnDev([['Hello', {withoutStack: true}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Unexpected component stack for: + "Hello " + + If this warning should include a component stack, remove {withoutStack: true} from this warning. + If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call." + `); + }); + + // @gate __DEV__ + it('fails if warnings are not expected to have a stack, but some do', () => { + const message = expectToThrowFailure(() => { + console.warn('Hello\n in div'); + console.warn('Good day'); + console.warn('Bye\n in div'); + assertConsoleWarnDev([ + [ + 'Hello', + { + withoutStack: true, + }, + ], + 'Good day', + [ + 'Bye', + { + withoutStack: true, + }, + ], + ]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Unexpected component stack for: + "Hello " + + Unexpected component stack for: + "Bye " + + If this warning should include a component stack, remove {withoutStack: true} from this warning. + If all warnings should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleWarnDev call." + `); + }); }); // @gate __DEV__ it('fails if the args is greater than %s argument number', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi %s', 'Sara', 'extra'); assertConsoleWarnDev(['Hi'], {withoutStack: true}); }); @@ -1278,7 +1481,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the args is greater than %s argument number for multiple warnings', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi %s', 'Sara', 'extra'); console.warn('Bye %s', 'Sara', 'extra'); assertConsoleWarnDev(['Hi', 'Bye'], {withoutStack: true}); @@ -1296,7 +1499,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the %s argument number is greater than args', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi %s'); assertConsoleWarnDev(['Hi'], {withoutStack: true}); }); @@ -1310,7 +1513,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the %s argument number is greater than args for multiple warnings', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi %s'); console.warn('Bye %s'); assertConsoleWarnDev(['Hi', 'Bye'], {withoutStack: true}); @@ -1328,7 +1531,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if component stack is passed twice', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi %s%s', '\n in div', '\n in div'); assertConsoleWarnDev(['Hi']); }); @@ -1340,9 +1543,27 @@ describe('ReactInternalTestUtils console assertions', () => { `); }); + // @gate __DEV__ + it('fails if multiple logs pass component stack twice', () => { + const message = expectToThrowFailure(() => { + console.warn('Hi %s%s', '\n in div', '\n in div'); + console.warn('Bye %s%s', '\n in div', '\n in div'); + assertConsoleWarnDev(['Hi', 'Bye']); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleWarnDev(expected) + + Received more than one component stack for a warning: + "Hi %s%s" + + Received more than one component stack for a warning: + "Bye %s%s"" + `); + }); + // @gate __DEV__ it('fails if multiple strings are passed without an array wrapper for single log', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Bye \n in div'); assertConsoleWarnDev('Hi', 'Bye'); @@ -1357,7 +1578,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if multiple strings are passed without an array wrapper for multiple logs', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Bye \n in div'); assertConsoleWarnDev('Hi', 'Bye'); @@ -1372,7 +1593,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails on more than two arguments', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.warn('Hi \n in div'); console.warn('Wow \n in div'); console.warn('Bye \n in div'); @@ -1405,7 +1626,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.warn('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitFor(['foo', 'bar']); }); expect(message).toMatchInlineSnapshot(` @@ -1447,7 +1668,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.warn('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForThrow('Oh no!'); }); expect(message).toMatchInlineSnapshot(` @@ -1481,7 +1702,7 @@ describe('ReactInternalTestUtils console assertions', () => { root.render(); console.warn('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForPaint(['Urgent: B, Deferred: A']); }); @@ -1516,7 +1737,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.warn('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForAll(['foo', 'bar', 'baz']); }); expect(message).toMatchInlineSnapshot(` @@ -1551,7 +1772,7 @@ describe('ReactInternalTestUtils console assertions', () => { assertLog([]); await waitForAll(['foo', 'bar', 'baz']); - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { expect(root).toMatchRenderedOutput(
foobarbaz
); }); expect(message).toMatchInlineSnapshot(` @@ -1584,26 +1805,6 @@ describe('ReactInternalTestUtils console assertions', () => { assertConsoleErrorDev(['Hello', 'Good day', 'Bye']); }); - // @gate __DEV__ - it('passes if errors without stack explicitly opt out', () => { - console.error('Hello'); - assertConsoleErrorDev(['Hello'], {withoutStack: true}); - - console.error('Hello'); - console.error('Good day'); - console.error('Bye'); - - assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], {withoutStack: true}); - }); - - // @gate __DEV__ - it('passes when expected withoutStack number matches the actual one', () => { - console.error('Hello\n in div'); - console.error('Good day'); - console.error('Bye\n in div'); - assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], {withoutStack: 1}); - }); - it('fails if act is called without assertConsoleErrorDev', async () => { const Yield = ({id}) => { console.error(id); @@ -1624,7 +1825,7 @@ describe('ReactInternalTestUtils console assertions', () => { await act(() => { root.render(); }); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await act(() => { root.render(); }); @@ -1664,7 +1865,7 @@ describe('ReactInternalTestUtils console assertions', () => { await act(() => { root.render(); }); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await act(() => { root.render(); }); @@ -1694,7 +1895,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if first expected error is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Wow \n in div'); console.error('Bye \n in div'); assertConsoleErrorDev(['Hi', 'Wow', 'Bye']); @@ -1717,7 +1918,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if middle expected error is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Bye \n in div'); assertConsoleErrorDev(['Hi', 'Wow', 'Bye']); @@ -1740,7 +1941,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if last expected error is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Wow \n in div'); assertConsoleErrorDev(['Hi', 'Wow', 'Bye']); @@ -1763,7 +1964,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if first received error is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Wow \n in div'); console.error('Bye \n in div'); @@ -1787,7 +1988,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if middle received error is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Wow \n in div'); console.error('Bye \n in div'); @@ -1811,7 +2012,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if last received error is not included', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Wow \n in div'); console.error('Bye \n in div'); @@ -1834,7 +2035,7 @@ describe('ReactInternalTestUtils console assertions', () => { }); // @gate __DEV__ it('fails if only error does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hello'); assertConsoleErrorDev(['Hello']); }); @@ -1844,13 +2045,14 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Hello" - If this error intentionally omits the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." + If this error should omit a component stack, pass [log, {withoutStack: true}]. + If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." `); }); // @gate __DEV__ it('fails if first error does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hello\n in div'); console.error('Good day\n in div'); console.error('Bye'); @@ -1862,12 +2064,13 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Bye" - If this error intentionally omits the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." + If this error should omit a component stack, pass [log, {withoutStack: true}]. + If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." `); }); // @gate __DEV__ it('fails if last error does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hello'); console.error('Good day\n in div'); console.error('Bye\n in div'); @@ -1879,12 +2082,13 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Hello" - If this error intentionally omits the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." + If this error should omit a component stack, pass [log, {withoutStack: true}]. + If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." `); }); // @gate __DEV__ it('fails if middle error does not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hello\n in div'); console.error('Good day'); console.error('Bye\n in div'); @@ -1896,12 +2100,13 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Good day" - If this error intentionally omits the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." + If this error should omit a component stack, pass [log, {withoutStack: true}]. + If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." `); }); // @gate __DEV__ it('fails if all errors do not contain a stack', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hello'); console.error('Good day'); console.error('Bye'); @@ -1919,148 +2124,278 @@ describe('ReactInternalTestUtils console assertions', () => { Missing component stack for: "Bye" - If this error intentionally omits the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." + If this error should omit a component stack, pass [log, {withoutStack: true}]. + If all errors should omit the component stack, add {withoutStack: true} to the assertConsoleErrorDev call." `); }); - // @gate __DEV__ - it('fails if only error is not expected to have a stack, but does', () => { - const message = expectToWarnAndToThrow(() => { - console.error('Hello\n in div'); + describe('global withoutStack', () => { + // @gate __DEV__ + it('passes if errors without stack explicitly opt out', () => { + console.error('Hello'); assertConsoleErrorDev(['Hello'], {withoutStack: true}); - }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleErrorDev(expected) - - Unexpected component stack for: - "Hello " - - If this error intentionally includes the component stack, remove {withoutStack: true} from the assertConsoleErrorDev() call. - If you have a mix of errors with and without stack in one assertConsoleErrorDev() call, pass {withoutStack: N} where N is the number of errors without stacks." - `); - }); - // @gate __DEV__ - it('fails if errors are not expected to have a stack, but some do', () => { - const message = expectToWarnAndToThrow(() => { - console.error('Hello\n in div'); + console.error('Hello'); console.error('Good day'); - console.error('Bye\n in div'); + console.error('Bye'); + assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], { withoutStack: true, }); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleErrorDev(expected) - Unexpected component stack for: - "Hello " + // @gate __DEV__ + it('fails if withoutStack is invalid null value', () => { + const message = expectToThrowFailure(() => { + console.error('Hi'); + assertConsoleErrorDev(['Hi'], {withoutStack: null}); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) - Unexpected component stack for: - "Bye " + The second argument must be {withoutStack: true}. - If this error intentionally includes the component stack, remove {withoutStack: true} from the assertConsoleErrorDev() call. - If you have a mix of errors with and without stack in one assertConsoleErrorDev() call, pass {withoutStack: N} where N is the number of errors without stacks." - `); - }); + Instead received {"withoutStack":null}." + `); + assertConsoleErrorDev(['Hi'], {withoutStack: true}); + }); - // @gate __DEV__ - it('fails if expected withoutStack number does not match the actual one', () => { - const message = expectToWarnAndToThrow(() => { - console.error('Hello\n in div'); - console.error('Good day'); - console.error('Bye\n in div'); - assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], { - withoutStack: 4, + // @gate __DEV__ + it('fails if withoutStack is invalid {} value', () => { + const message = expectToThrowFailure(() => { + console.error('Hi'); + assertConsoleErrorDev(['Hi'], {withoutStack: {}}); }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + The second argument must be {withoutStack: true}. + + Instead received {"withoutStack":{}}." + `); + assertConsoleErrorDev(['Hi'], {withoutStack: true}); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleErrorDev(expected) - Expected 4 errors without a component stack but received 1: - - Expected errors - + Received errors + // @gate __DEV__ + it('fails if withoutStack is invalid string value', () => { + const message = expectToThrowFailure(() => { + console.error('Hi'); + assertConsoleErrorDev(['Hi'], {withoutStack: 'haha'}); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) - - Hello - + Hello - Good day - - Bye - + Bye " - `); - }); + The second argument must be {withoutStack: true}. - // @gate __DEV__ - it('fails if multiple expected withoutStack number does not match the actual one', () => { - const message = expectToWarnAndToThrow(() => { + Instead received {"withoutStack":"haha"}." + `); + assertConsoleErrorDev(['Hi'], {withoutStack: true}); + }); + + // @gate __DEV__ + it('fails if only error is not expected to have a stack, but does', () => { + const message = expectToThrowFailure(() => { + console.error('Hello\n in div'); + assertConsoleErrorDev(['Hello'], {withoutStack: true}); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + Unexpected component stack for: + "Hello " + + If this error should include a component stack, remove {withoutStack: true} from this error. + If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call." + `); + }); + + // @gate __DEV__ + it('fails if errors are not expected to have a stack, but some do', () => { + const message = expectToThrowFailure(() => { + console.error('Hello\n in div'); + console.error('Good day'); + console.error('Bye\n in div'); + assertConsoleErrorDev(['Hello', 'Good day', 'Bye'], { + withoutStack: true, + }); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + Unexpected component stack for: + "Hello " + + Unexpected component stack for: + "Bye " + + If this error should include a component stack, remove {withoutStack: true} from this error. + If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call." + `); + }); + }); + describe('local withoutStack', () => { + // @gate __DEV__ + it('passes when expected withoutStack logs matches the actual logs', () => { console.error('Hello\n in div'); console.error('Good day'); - console.error('Good night'); console.error('Bye\n in div'); - assertConsoleErrorDev(['Hello', 'Good day', 'Good night', 'Bye'], { - withoutStack: 4, + assertConsoleErrorDev([ + 'Hello', + ['Good day', {withoutStack: true}], + 'Bye', + ]); + }); + + // @gate __DEV__ + it('fails if withoutStack is invalid null value', () => { + const message = expectToThrowFailure(() => { + console.error('Hi'); + assertConsoleErrorDev([['Hi', {withoutStack: null}]]); }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + Log entries that are arrays must be of the form [string, {withoutStack: true}] + + Instead received [string, {"withoutStack":null}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleErrorDev(expected) - Expected 4 errors without a component stack but received 2: - - Expected errors - + Received errors + // @gate __DEV__ + it('fails if withoutStack is invalid {} value', () => { + const message = expectToThrowFailure(() => { + console.error('Hi'); + assertConsoleErrorDev([['Hi', {withoutStack: {}}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) - - Hello - + Hello - Good day - Good night - - Bye - + Bye " - `); - }); + Log entries that are arrays must be of the form [string, {withoutStack: true}] - // @gate __DEV__ - it('fails if withoutStack is invalid null value', () => { - const message = expectToWarnAndToThrow(() => { - console.error('Hi'); - assertConsoleErrorDev(['Hi'], {withoutStack: null}); + Instead received [string, {"withoutStack":{}}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleErrorDev(expected) - The second argument for assertConsoleErrorDev(), when specified, must be an object. It may have a property called "withoutStack" whose value may be a boolean or number. Instead received object." - `); - assertConsoleErrorDev(['Hi'], {withoutStack: true}); - }); + // @gate __DEV__ + it('fails if withoutStack is invalid string value', () => { + const message = expectToThrowFailure(() => { + console.error('Hi'); + assertConsoleErrorDev([['Hi', {withoutStack: 'haha'}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) - // @gate __DEV__ - it('fails if withoutStack is invalid {} value', () => { - const message = expectToWarnAndToThrow(() => { - console.error('Hi'); - assertConsoleErrorDev(['Hi'], {withoutStack: {}}); + Log entries that are arrays must be of the form [string, {withoutStack: true}] + + Instead received [string, {"withoutStack":"haha"}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleErrorDev(expected) - The second argument for assertConsoleErrorDev(), when specified, must be an object. It may have a property called "withoutStack" whose value may be a boolean or number. Instead received object." - `); - assertConsoleErrorDev(['Hi'], {withoutStack: true}); - }); + // @gate __DEV__ + it('fails if withoutStack is invalid number value', () => { + const message = expectToThrowFailure(() => { + console.error('Hi'); + assertConsoleErrorDev([['Hi', {withoutStack: 4}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) - // @gate __DEV__ - it('fails if withoutStack is invalid string value', () => { - const message = expectToWarnAndToThrow(() => { - console.error('Hi'); - assertConsoleErrorDev(['Hi'], {withoutStack: 'haha'}); + Log entries that are arrays must be of the form [string, {withoutStack: true}] + + Instead received [string, {"withoutStack":4}]." + `); }); - expect(message).toMatchInlineSnapshot(` - "assertConsoleErrorDev(expected) - The second argument for assertConsoleErrorDev(), when specified, must be an object. It may have a property called "withoutStack" whose value may be a boolean or number. Instead received string." - `); - assertConsoleErrorDev(['Hi'], {withoutStack: true}); + // @gate __DEV__ + it('fails if you forget to wrap local withoutStack in array', () => { + const message = expectToThrowFailure(() => { + console.error('Hello\n in div'); + console.error('Bye\n in div'); + assertConsoleErrorDev(['Hello', {withoutStack: true}, 'Bye']); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + Did you forget to wrap a log with withoutStack in an array? + + The expected message for assertConsoleErrorDev() must be a string or an array of length 2. + + Instead received {"withoutStack":true}." + `); + }); + + // @gate __DEV__ + it('fails if you wrap in an array unnecessarily', () => { + const message = expectToThrowFailure(() => { + console.error('Hello'); + assertConsoleErrorDev([['Hello']]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + Did you forget to remove the array around the log? + + The expected message for assertConsoleErrorDev() must be a string or an array of length 2, but there's only one item in the array. If this is intentional, remove the extra array." + `); + }); + + // @gate __DEV__ + it('fails if only error is not expected to have a stack, but does', () => { + const message = expectToThrowFailure(() => { + console.error('Hello\n in div'); + assertConsoleErrorDev([['Hello', {withoutStack: true}]]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + Unexpected component stack for: + "Hello " + + If this error should include a component stack, remove {withoutStack: true} from this error. + If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call." + `); + }); + + // @gate __DEV__ + it('fails if errors are not expected to have a stack, but some do', () => { + const message = expectToThrowFailure(() => { + console.error('Hello\n in div'); + console.error('Good day'); + console.error('Bye\n in div'); + assertConsoleErrorDev([ + [ + 'Hello', + { + withoutStack: true, + }, + ], + 'Good day', + [ + 'Bye', + { + withoutStack: true, + }, + ], + ]); + }); + expect(message).toMatchInlineSnapshot(` + "assertConsoleErrorDev(expected) + + Unexpected component stack for: + "Hello " + + Unexpected component stack for: + "Bye " + + If this error should include a component stack, remove {withoutStack: true} from this error. + If all errors should include the component stack, you may need to remove {withoutStack: true} from the assertConsoleErrorDev call." + `); + }); }); // @gate __DEV__ it('fails if the args is greater than %s argument number', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi %s', 'Sara', 'extra'); assertConsoleErrorDev(['Hi'], {withoutStack: true}); }); @@ -2074,7 +2409,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the args is greater than %s argument number for multiple errors', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi %s', 'Sara', 'extra'); console.error('Bye %s', 'Sara', 'extra'); assertConsoleErrorDev(['Hi', 'Bye'], {withoutStack: true}); @@ -2092,7 +2427,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the %s argument number is greater than args', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi %s'); assertConsoleErrorDev(['Hi'], {withoutStack: true}); }); @@ -2106,7 +2441,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if the %s argument number is greater than args for multiple errors', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi %s'); console.error('Bye %s'); assertConsoleErrorDev(['Hi', 'Bye'], {withoutStack: true}); @@ -2124,7 +2459,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if component stack is passed twice', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi %s%s', '\n in div', '\n in div'); assertConsoleErrorDev(['Hi']); }); @@ -2138,7 +2473,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if multiple logs pass component stack twice', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi %s%s', '\n in div', '\n in div'); console.error('Bye %s%s', '\n in div', '\n in div'); assertConsoleErrorDev(['Hi', 'Bye']); @@ -2156,7 +2491,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if multiple strings are passed without an array wrapper for single log', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Bye \n in div'); assertConsoleErrorDev('Hi', 'Bye'); @@ -2171,7 +2506,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails if multiple strings are passed without an array wrapper for multiple logs', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Bye \n in div'); assertConsoleErrorDev('Hi', 'Bye'); @@ -2186,7 +2521,7 @@ describe('ReactInternalTestUtils console assertions', () => { // @gate __DEV__ it('fails on more than two arguments', () => { - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { console.error('Hi \n in div'); console.error('Wow \n in div'); console.error('Bye \n in div'); @@ -2219,7 +2554,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.error('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitFor(['foo', 'bar']); }); expect(message).toMatchInlineSnapshot(` @@ -2261,7 +2596,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.error('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForThrow('Oh no!'); }); expect(message).toMatchInlineSnapshot(` @@ -2295,7 +2630,7 @@ describe('ReactInternalTestUtils console assertions', () => { root.render(); console.error('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForPaint(['Urgent: B, Deferred: A']); }); @@ -2330,7 +2665,7 @@ describe('ReactInternalTestUtils console assertions', () => { console.error('Not asserted'); - const message = await awaitExpectToWarnAndToThrow(async () => { + const message = await awaitExpectToThrowFailure(async () => { await waitForAll(['foo', 'bar', 'baz']); }); expect(message).toMatchInlineSnapshot(` @@ -2365,7 +2700,7 @@ describe('ReactInternalTestUtils console assertions', () => { assertLog([]); await waitForAll(['foo', 'bar', 'baz']); - const message = expectToWarnAndToThrow(() => { + const message = expectToThrowFailure(() => { expect(root).toMatchRenderedOutput(
foobarbaz
); }); expect(message).toMatchInlineSnapshot(` diff --git a/packages/internal-test-utils/consoleMock.js b/packages/internal-test-utils/consoleMock.js index e29a8d6d4ad5f..4601335f40dbe 100644 --- a/packages/internal-test-utils/consoleMock.js +++ b/packages/internal-test-utils/consoleMock.js @@ -299,31 +299,28 @@ export function createLogAssertion( const withoutStack = options.withoutStack; + // Warn about invalid global withoutStack values. if (consoleMethod === 'log' && withoutStack !== undefined) { - // We don't expect any console.log calls to have a stack. throwFormattedError( `Do not pass withoutStack to assertConsoleLogDev, console.log does not have component stacks.`, ); - } else if ( - withoutStack !== undefined && - typeof withoutStack !== 'number' && - withoutStack !== true - ) { + } else if (withoutStack !== undefined && withoutStack !== true) { + // withoutStack can only have a value true. throwFormattedError( - `The second argument for ${matcherName}(), when specified, must be an object. It may have a ` + - `property called "withoutStack" whose value may be a boolean or number. ` + - `Instead received ${typeof withoutStack}.`, + `The second argument must be {withoutStack: true}.` + + `\n\nInstead received ${JSON.stringify(options)}.`, ); } const observedLogs = clearObservedErrors(); - const unexpectedLogs = []; const receivedLogs = []; - const logsWithoutComponentStack = []; - const logsWithComponentStack = []; + const missingExpectedLogs = Array.from(expectedMessages); + + const unexpectedLogs = []; + const unexpectedMissingComponentStack = []; + const unexpectedIncludingComponentStack = []; const logsMismatchingFormat = []; const logsWithExtraComponentStack = []; - const missingExpectedLogs = Array.from(expectedMessages); // Loop over all the observed logs to determine: // - Which expected logs are missing @@ -342,7 +339,77 @@ export function createLogAssertion( return; } - const expectedMessage = replaceComponentStack(expectedMessages[index]); + let expectedMessage; + let expectedWithoutStack; + const expectedMessageOrArray = expectedMessages[index]; + if ( + expectedMessageOrArray != null && + Array.isArray(expectedMessageOrArray) + ) { + // Should be in the local form assert([['log', {withoutStack: true}]]) + + // Some validations for common mistakes. + if (expectedMessageOrArray.length === 1) { + throwFormattedError( + `Did you forget to remove the array around the log?` + + `\n\nThe expected message for ${matcherName}() must be a string or an array of length 2, but there's only one item in the array. If this is intentional, remove the extra array.`, + ); + } else if (expectedMessageOrArray.length !== 2) { + throwFormattedError( + `The expected message for ${matcherName}() must be a string or an array of length 2. ` + + `Instead received ${expectedMessageOrArray}.`, + ); + } else if (consoleMethod === 'log') { + // We don't expect any console.log calls to have a stack. + throwFormattedError( + `Do not pass withoutStack to assertConsoleLogDev logs, console.log does not have component stacks.`, + ); + } + + // Format is correct, check the values. + const currentExpectedMessage = expectedMessageOrArray[0]; + const currentExpectedOptions = expectedMessageOrArray[1]; + if ( + typeof currentExpectedMessage !== 'string' || + typeof currentExpectedOptions !== 'object' || + currentExpectedOptions.withoutStack !== true + ) { + throwFormattedError( + `Log entries that are arrays must be of the form [string, {withoutStack: true}]` + + `\n\nInstead received [${typeof currentExpectedMessage}, ${JSON.stringify( + currentExpectedOptions, + )}].`, + ); + } + + expectedMessage = replaceComponentStack(currentExpectedMessage); + expectedWithoutStack = expectedMessageOrArray[1].withoutStack; + } else if (typeof expectedMessageOrArray === 'string') { + // Should be in the form assert(['log']) or assert(['log'], {withoutStack: true}) + expectedMessage = replaceComponentStack(expectedMessageOrArray[0]); + if (consoleMethod === 'log') { + expectedWithoutStack = true; + } else { + expectedWithoutStack = withoutStack; + } + } else if ( + typeof expectedMessageOrArray === 'object' && + expectedMessageOrArray != null && + expectedMessageOrArray.withoutStack != null + ) { + // Special case for common case of a wrong withoutStack value. + throwFormattedError( + `Did you forget to wrap a log with withoutStack in an array?` + + `\n\nThe expected message for ${matcherName}() must be a string or an array of length 2.` + + `\n\nInstead received ${JSON.stringify(expectedMessageOrArray)}.`, + ); + } else if (expectedMessageOrArray != null) { + throwFormattedError( + `The expected message for ${matcherName}() must be a string or an array of length 2. ` + + `Instead received ${JSON.stringify(expectedMessageOrArray)}.`, + ); + } + const normalizedMessage = replaceComponentStack(message); receivedLogs.push(normalizedMessage); @@ -371,15 +438,17 @@ export function createLogAssertion( }); } - // Check if log is expected, and if it has a component stack. + // Main logic to check if log is expected, with the component stack. if ( normalizedMessage === expectedMessage || normalizedMessage.includes(expectedMessage) ) { if (isLikelyAComponentStack(normalizedMessage)) { - logsWithComponentStack.push(normalizedMessage); - } else { - logsWithoutComponentStack.push(normalizedMessage); + if (expectedWithoutStack === true) { + unexpectedIncludingComponentStack.push(normalizedMessage); + } + } else if (expectedWithoutStack !== true) { + unexpectedMissingComponentStack.push(normalizedMessage); } // Found expected log, remove it from missing. @@ -419,53 +488,34 @@ export function createLogAssertion( ); } - // Any unexpected component stacks are a failure. - if (consoleMethod !== 'log') { - if (typeof withoutStack === 'number') { - // We're expecting a particular number of warnings without stacks. - if (withoutStack !== logsWithoutComponentStack.length) { - throwFormattedError( - `Expected ${withoutStack} ${logName()}s without a component stack but received ${ - logsWithoutComponentStack.length - }:\n${printDiff()}`, - ); - } - } else if (withoutStack === true) { - // We're expecting that all warnings won't have the stack. - // If some warnings have it, it's an error. - if (logsWithComponentStack.length > 0) { - throwFormattedError( - `${logsWithComponentStack - .map( - stack => - `Unexpected component stack for:\n ${printReceived( - stack, - )}`, - ) - .join( - '\n\n', - )}\n\nIf this ${logName()} intentionally includes the component stack, remove ` + - `{withoutStack: true} from the ${matcherName}() call.\nIf you have a mix of ` + - `${logName()}s with and without stack in one ${matcherName}() call, pass ` + - `{withoutStack: N} where N is the number of ${logName()}s without stacks.`, - ); - } - } else if (withoutStack === undefined) { - // We're expecting that all warnings *do* have the stack (default). - // If some warnings don't have it, it's an error. - if (logsWithoutComponentStack.length > 0) { - throwFormattedError( - `${logsWithoutComponentStack - .map( - stack => - `Missing component stack for:\n ${printReceived(stack)}`, - ) - .join( - '\n\n', - )}\n\nIf this ${logName()} intentionally omits the component stack, add {withoutStack: true} to the ${matcherName} call.`, - ); - } - } + // Any logs that include a component stack but shouldn't. + if (unexpectedIncludingComponentStack.length > 0) { + throwFormattedError( + `${unexpectedIncludingComponentStack + .map( + stack => + `Unexpected component stack for:\n ${printReceived(stack)}`, + ) + .join( + '\n\n', + )}\n\nIf this ${logName()} should include a component stack, remove {withoutStack: true} from this ${logName()}.` + + `\nIf all ${logName()}s should include the component stack, you may need to remove {withoutStack: true} from the ${matcherName} call.`, + ); + } + + // Any logs that are missing a component stack without withoutStack. + if (unexpectedMissingComponentStack.length > 0) { + throwFormattedError( + `${unexpectedMissingComponentStack + .map( + stack => + `Missing component stack for:\n ${printReceived(stack)}`, + ) + .join( + '\n\n', + )}\n\nIf this ${logName()} should omit a component stack, pass [log, {withoutStack: true}].` + + `\nIf all ${logName()}s should omit the component stack, add {withoutStack: true} to the ${matcherName} call.`, + ); } // Wrong %s formatting is a failure.