From fd410f43fcb771aa38215d17d7a487d1bc789d71 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 16 Jul 2018 22:47:37 +0100 Subject: [PATCH] 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. --- .../jest/matchers/__tests__/toWarnDev-test.js | 8 +++++ scripts/jest/matchers/toWarnDev.js | 30 ++++++++++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/scripts/jest/matchers/__tests__/toWarnDev-test.js b/scripts/jest/matchers/__tests__/toWarnDev-test.js index 67f4c99e97..d5ef59abe6 100644 --- a/scripts/jest/matchers/__tests__/toWarnDev-test.js +++ b/scripts/jest/matchers/__tests__/toWarnDev-test.js @@ -161,5 +161,13 @@ describe('toWarnDev', () => { }).toWarnDev('Hi', {withoutStack: true}); }).toThrow('Received 0 arguments for a message with 1 placeholders'); }); + + it('fails if stack is passed twice', () => { + expect(() => { + expect(() => { + console.error('Hi %s%s', '\n in div', '\n in div'); + }).toWarnDev('Hi'); + }).toThrow('Received more than one component stack for a warning'); + }); } }); diff --git a/scripts/jest/matchers/toWarnDev.js b/scripts/jest/matchers/toWarnDev.js index 6404a54c7b..187de91e54 100644 --- a/scripts/jest/matchers/toWarnDev.js +++ b/scripts/jest/matchers/toWarnDev.js @@ -24,7 +24,9 @@ const createMatcherFor = consoleMethod => const warningsWithoutComponentStack = []; const warningsWithComponentStack = []; const unexpectedWarnings = []; + let lastWarningWithMismatchingFormat = null; + let lastWarningWithExtraComponentStack = null; // Catch errors thrown by the callback, // But only rethrow them if all test expectations have been satisfied. @@ -32,6 +34,9 @@ const createMatcherFor = consoleMethod => // and result in a test that passes when it shouldn't. let caughtError; + const isLikelyAComponentStack = message => + typeof message === 'string' && message.includes('\n in '); + const consoleSpy = (format, ...args) => { const message = util.format(format, ...args); const normalizedMessage = normalizeCodeLocInfo(message); @@ -49,13 +54,25 @@ const createMatcherFor = consoleMethod => }; } + // Protect against accidentally passing a component stack + // to warning() which already injects the component stack. + if ( + args.length >= 2 && + isLikelyAComponentStack(args[args.length - 1]) && + isLikelyAComponentStack(args[args.length - 2]) + ) { + lastWarningWithExtraComponentStack = { + format, + }; + } + for (let index = 0; index < expectedMessages.length; index++) { const expectedMessage = expectedMessages[index]; if ( normalizedMessage === expectedMessage || normalizedMessage.includes(expectedMessage) ) { - if (normalizedMessage.includes('\n in ')) { + if (isLikelyAComponentStack(normalizedMessage)) { warningsWithComponentStack.push(normalizedMessage); } else { warningsWithoutComponentStack.push(normalizedMessage); @@ -191,6 +208,17 @@ const createMatcherFor = consoleMethod => }; } + if (lastWarningWithExtraComponentStack !== null) { + return { + message: () => + `Received more than one component stack for a warning:\n ${this.utils.printReceived( + lastWarningWithExtraComponentStack.format + )}\nDid you accidentally pass a stack to warning() as the last argument? ` + + `Don't forget warning() already injects the component stack automatically.`, + pass: false, + }; + } + return {pass: true}; } } else {