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 {