@samitouri / QOS-React / commits / fd410f43fc

Protect against passing component stack twice

This is a leftover from #13161 that I forgot to include. It ensures we don't accidentally write code in the old way and end up passing the stack twice.

Dan Abramov committed Jul 16, 2018 at 22:47 UTC fd410f43fcb771aa38215d17d7a487d1bc789d71
2 files changed +37 -1
scripts/jest/matchers/__tests__/toWarnDev-test.js
+8
@@ -161,5 +161,13 @@ describe('toWarnDev', () => {
161 }).toWarnDev('Hi', {withoutStack: true});
162 }).toThrow('Received 0 arguments for a message with 1 placeholders');
163 });
164 +
165 + it('fails if stack is passed twice', () => {
166 + expect(() => {
167 + expect(() => {
168 + console.error('Hi %s%s', '\n in div', '\n in div');
169 + }).toWarnDev('Hi');
170 + }).toThrow('Received more than one component stack for a warning');
171 + });
172 }
173 });
scripts/jest/matchers/toWarnDev.js
+29 -1
@@ -24,7 +24,9 @@ const createMatcherFor = consoleMethod =>
24 const warningsWithoutComponentStack = [];
25 const warningsWithComponentStack = [];
26 const unexpectedWarnings = [];
27 +
28 let lastWarningWithMismatchingFormat = null;
29 + let lastWarningWithExtraComponentStack = null;
30
31 // Catch errors thrown by the callback,
32 // But only rethrow them if all test expectations have been satisfied.
@@ -32,6 +34,9 @@ const createMatcherFor = consoleMethod =>
34 // and result in a test that passes when it shouldn't.
35 let caughtError;
36
37 + const isLikelyAComponentStack = message =>
38 + typeof message === 'string' && message.includes('\n in ');
39 +
40 const consoleSpy = (format, ...args) => {
41 const message = util.format(format, ...args);
42 const normalizedMessage = normalizeCodeLocInfo(message);
@@ -49,13 +54,25 @@ const createMatcherFor = consoleMethod =>
54 };
55 }
56
57 + // Protect against accidentally passing a component stack
58 + // to warning() which already injects the component stack.
59 + if (
60 + args.length >= 2 &&
61 + isLikelyAComponentStack(args[args.length - 1]) &&
62 + isLikelyAComponentStack(args[args.length - 2])
63 + ) {
64 + lastWarningWithExtraComponentStack = {
65 + format,
66 + };
67 + }
68 +
69 for (let index = 0; index < expectedMessages.length; index++) {
70 const expectedMessage = expectedMessages[index];
71 if (
72 normalizedMessage === expectedMessage ||
73 normalizedMessage.includes(expectedMessage)
74 ) {
58 - if (normalizedMessage.includes('\n in ')) {
75 + if (isLikelyAComponentStack(normalizedMessage)) {
76 warningsWithComponentStack.push(normalizedMessage);
77 } else {
78 warningsWithoutComponentStack.push(normalizedMessage);
@@ -191,6 +208,17 @@ const createMatcherFor = consoleMethod =>
208 };
209 }
210
211 + if (lastWarningWithExtraComponentStack !== null) {
212 + return {
213 + message: () =>
214 + `Received more than one component stack for a warning:\n ${this.utils.printReceived(
215 + lastWarningWithExtraComponentStack.format
216 + )}\nDid you accidentally pass a stack to warning() as the last argument? ` +
217 + `Don't forget warning() already injects the component stack automatically.`,
218 + pass: false,
219 + };
220 + }
221 +
222 return {pass: true};
223 }
224 } else {