mirror of
https://github.com/facebook/react.git
synced 2026-02-26 07:15:09 +00:00
[tests] Require exact error messages in assertConsole helpers (#35497)
Requires full error message in assert helpers. Some of the error messages we asset on add a native javascript stack trace, which would be a pain to add to the messages and maintain. This PR allows you to just add `\n in <stack>` placeholder to the error message to denote a native stack trace is present in the message. --- Note: i vibe coded this so it was a pain to backtrack this to break this into a stack, I tried and gave up, sorry.
This commit is contained in:
3
.github/workflows/runtime_build_and_test.yml
vendored
3
.github/workflows/runtime_build_and_test.yml
vendored
@@ -278,6 +278,7 @@ jobs:
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: yarn --cwd compiler install --frozen-lockfile
|
||||
if: steps.node_modules.outputs.cache-hit != 'true'
|
||||
- run: node --version
|
||||
- run: yarn test ${{ matrix.params }} --ci --shard=${{ matrix.shard }}
|
||||
|
||||
# Hardcoded to improve parallelism
|
||||
@@ -445,6 +446,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: node --version
|
||||
- run: yarn test --build ${{ matrix.test_params }} --shard=${{ matrix.shard }} --ci
|
||||
|
||||
test_build_devtools:
|
||||
@@ -489,6 +491,7 @@ jobs:
|
||||
merge-multiple: true
|
||||
- name: Display structure of build
|
||||
run: ls -R build
|
||||
- run: node --version
|
||||
- run: yarn test --build --project=devtools -r=experimental --shard=${{ matrix.shard }} --ci
|
||||
|
||||
process_artifacts_combined:
|
||||
|
||||
@@ -879,7 +879,7 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
if (__DEV__) {
|
||||
console.warn('Hello\n in div');
|
||||
}
|
||||
assertConsoleWarnDev(['Hello']);
|
||||
assertConsoleWarnDev(['Hello\n in div']);
|
||||
});
|
||||
|
||||
it('passes if all warnings contain a stack', () => {
|
||||
@@ -888,7 +888,11 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
console.warn('Good day\n in div');
|
||||
console.warn('Bye\n in div');
|
||||
}
|
||||
assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
|
||||
assertConsoleWarnDev([
|
||||
'Hello\n in div',
|
||||
'Good day\n in div',
|
||||
'Bye\n in div',
|
||||
]);
|
||||
});
|
||||
|
||||
it('fails if act is called without assertConsoleWarnDev', async () => {
|
||||
@@ -1075,7 +1079,11 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.warn('Hi \n in div');
|
||||
console.warn('Wow \n in div');
|
||||
assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
|
||||
assertConsoleWarnDev([
|
||||
'Hi \n in div',
|
||||
'Wow \n in div',
|
||||
'Bye \n in div',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
@@ -1085,9 +1093,9 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
- Hi
|
||||
- Wow
|
||||
- Bye
|
||||
- Hi in div
|
||||
- Wow in div
|
||||
- Bye in div
|
||||
+ Hi in div (at **)
|
||||
+ Wow in div (at **)"
|
||||
`);
|
||||
@@ -1188,16 +1196,26 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
console.warn('Hello');
|
||||
console.warn('Good day\n in div');
|
||||
console.warn('Bye\n in div');
|
||||
assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
|
||||
assertConsoleWarnDev([
|
||||
'Hello\n in div',
|
||||
'Good day\n in div',
|
||||
'Bye\n in div',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Hello"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
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."
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
- Hello in div
|
||||
- Good day in div
|
||||
- Bye in div
|
||||
+ Hello
|
||||
+ Good day in div (at **)
|
||||
+ Bye in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1207,16 +1225,26 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
console.warn('Hello\n in div');
|
||||
console.warn('Good day');
|
||||
console.warn('Bye\n in div');
|
||||
assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
|
||||
assertConsoleWarnDev([
|
||||
'Hello\n in div',
|
||||
'Good day\n in div',
|
||||
'Bye\n in div',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Good day"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
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."
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
- Hello in div
|
||||
- Good day in div
|
||||
- Bye in div
|
||||
+ Hello in div (at **)
|
||||
+ Good day
|
||||
+ Bye in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1226,41 +1254,26 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
console.warn('Hello\n in div');
|
||||
console.warn('Good day\n in div');
|
||||
console.warn('Bye');
|
||||
assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
|
||||
assertConsoleWarnDev([
|
||||
'Hello\n in div',
|
||||
'Good day\n in div',
|
||||
'Bye\n in div',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Bye"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
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."
|
||||
`);
|
||||
});
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
// @gate __DEV__
|
||||
it('fails if all warnings do not contain a stack', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.warn('Hello');
|
||||
console.warn('Good day');
|
||||
console.warn('Bye');
|
||||
assertConsoleWarnDev(['Hello', 'Good day', 'Bye']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Hello"
|
||||
|
||||
Missing component stack for:
|
||||
"Good day"
|
||||
|
||||
Missing component stack for:
|
||||
"Bye"
|
||||
|
||||
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."
|
||||
- Hello in div
|
||||
- Good day in div
|
||||
- Bye in div
|
||||
+ Hello in div (at **)
|
||||
+ Good day in div (at **)
|
||||
+ Bye"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1339,12 +1352,13 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
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."
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
- Hello
|
||||
+ Hello in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1361,16 +1375,16 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
Unexpected component stack for:
|
||||
"Bye
|
||||
in div (at **)"
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
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."
|
||||
- Hello
|
||||
+ Hello in div (at **)
|
||||
Good day
|
||||
- Bye
|
||||
+ Bye in div (at **)"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -1382,9 +1396,9 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
console.warn('Bye\n in div');
|
||||
}
|
||||
assertConsoleWarnDev([
|
||||
'Hello',
|
||||
'Hello\n in div',
|
||||
['Good day', {withoutStack: true}],
|
||||
'Bye',
|
||||
'Bye\n in div',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1490,12 +1504,13 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
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."
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
- Hello
|
||||
+ Hello in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1524,16 +1539,16 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
Unexpected component stack for:
|
||||
"Bye
|
||||
in div (at **)"
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
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."
|
||||
- Hello
|
||||
+ Hello in div (at **)
|
||||
Good day
|
||||
- Bye
|
||||
+ Bye in div (at **)"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -1606,13 +1621,18 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
it('fails if component stack is passed twice', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.warn('Hi %s%s', '\n in div', '\n in div');
|
||||
assertConsoleWarnDev(['Hi']);
|
||||
assertConsoleWarnDev(['Hi \n in div (at **)']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Received more than one component stack for a warning:
|
||||
"Hi %s%s""
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
Hi in div (at **)
|
||||
+ in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1621,16 +1641,23 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
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']);
|
||||
assertConsoleWarnDev([
|
||||
'Hi \n in div (at **)',
|
||||
'Bye \n in div (at **)',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleWarnDev(expected)
|
||||
|
||||
Received more than one component stack for a warning:
|
||||
"Hi %s%s"
|
||||
Unexpected warning(s) recorded.
|
||||
|
||||
Received more than one component stack for a warning:
|
||||
"Bye %s%s""
|
||||
- Expected warnings
|
||||
+ Received warnings
|
||||
|
||||
Hi in div (at **)
|
||||
+ in div (at **)
|
||||
Bye in div (at **)
|
||||
+ in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -1646,7 +1673,7 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
|
||||
Expected messages should be an array of strings but was given type "string"."
|
||||
`);
|
||||
assertConsoleWarnDev(['Hi', 'Bye']);
|
||||
assertConsoleWarnDev(['Hi \n in div', 'Bye \n in div']);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
@@ -1661,7 +1688,7 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
|
||||
Expected messages should be an array of strings but was given type "string"."
|
||||
`);
|
||||
assertConsoleWarnDev(['Hi', 'Bye']);
|
||||
assertConsoleWarnDev(['Hi \n in div', 'Bye \n in div']);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
@@ -1677,7 +1704,11 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
|
||||
Expected messages should be an array of strings but was given type "string"."
|
||||
`);
|
||||
assertConsoleWarnDev(['Hi', 'Wow', 'Bye']);
|
||||
assertConsoleWarnDev([
|
||||
'Hi \n in div',
|
||||
'Wow \n in div',
|
||||
'Bye \n in div',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should fail if waitFor is called before asserting', async () => {
|
||||
@@ -1884,7 +1915,7 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
if (__DEV__) {
|
||||
console.error('Hello\n in div');
|
||||
}
|
||||
assertConsoleErrorDev(['Hello']);
|
||||
assertConsoleErrorDev(['Hello\n in div']);
|
||||
});
|
||||
|
||||
it('passes if all errors contain a stack', () => {
|
||||
@@ -1893,7 +1924,11 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
console.error('Good day\n in div');
|
||||
console.error('Bye\n in div');
|
||||
}
|
||||
assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
|
||||
assertConsoleErrorDev([
|
||||
'Hello\n in div',
|
||||
'Good day\n in div',
|
||||
'Bye\n in div',
|
||||
]);
|
||||
});
|
||||
|
||||
it('fails if act is called without assertConsoleErrorDev', async () => {
|
||||
@@ -2080,7 +2115,11 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error('Hi \n in div');
|
||||
console.error('Wow \n in div');
|
||||
assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
|
||||
assertConsoleErrorDev([
|
||||
'Hi \n in div',
|
||||
'Wow \n in div',
|
||||
'Bye \n in div',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
@@ -2090,9 +2129,9 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
- Expected errors
|
||||
+ Received errors
|
||||
|
||||
- Hi
|
||||
- Wow
|
||||
- Bye
|
||||
- Hi in div
|
||||
- Wow in div
|
||||
- Bye in div
|
||||
+ Hi in div (at **)
|
||||
+ Wow in div (at **)"
|
||||
`);
|
||||
@@ -2192,101 +2231,6 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
+ TypeError: Cannot read properties of undefined (reading 'stack') in Foo (at **)"
|
||||
`);
|
||||
});
|
||||
// @gate __DEV__
|
||||
it('fails if only error does not contain a stack', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error('Hello');
|
||||
assertConsoleErrorDev(['Hello']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Hello"
|
||||
|
||||
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 = expectToThrowFailure(() => {
|
||||
console.error('Hello\n in div');
|
||||
console.error('Good day\n in div');
|
||||
console.error('Bye');
|
||||
assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Bye"
|
||||
|
||||
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 = expectToThrowFailure(() => {
|
||||
console.error('Hello');
|
||||
console.error('Good day\n in div');
|
||||
console.error('Bye\n in div');
|
||||
assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Hello"
|
||||
|
||||
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 = expectToThrowFailure(() => {
|
||||
console.error('Hello\n in div');
|
||||
console.error('Good day');
|
||||
console.error('Bye\n in div');
|
||||
assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Good day"
|
||||
|
||||
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 = expectToThrowFailure(() => {
|
||||
console.error('Hello');
|
||||
console.error('Good day');
|
||||
console.error('Bye');
|
||||
assertConsoleErrorDev(['Hello', 'Good day', 'Bye']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Missing component stack for:
|
||||
"Hello"
|
||||
|
||||
Missing component stack for:
|
||||
"Good day"
|
||||
|
||||
Missing component stack for:
|
||||
"Bye"
|
||||
|
||||
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('regression: checks entire string, not just the first letter', async () => {
|
||||
@@ -2385,12 +2329,13 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected error(s) recorded.
|
||||
|
||||
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."
|
||||
- Expected errors
|
||||
+ Received errors
|
||||
|
||||
- Hello
|
||||
+ Hello in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -2407,16 +2352,16 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected error(s) recorded.
|
||||
|
||||
Unexpected component stack for:
|
||||
"Bye
|
||||
in div (at **)"
|
||||
- Expected errors
|
||||
+ Received errors
|
||||
|
||||
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."
|
||||
- Hello
|
||||
+ Hello in div (at **)
|
||||
Good day
|
||||
- Bye
|
||||
+ Bye in div (at **)"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -2428,9 +2373,9 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
console.error('Bye\n in div');
|
||||
}
|
||||
assertConsoleErrorDev([
|
||||
'Hello',
|
||||
'Hello\n in div',
|
||||
['Good day', {withoutStack: true}],
|
||||
'Bye',
|
||||
'Bye\n in div',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -2536,12 +2481,13 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected error(s) recorded.
|
||||
|
||||
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."
|
||||
- Expected errors
|
||||
+ Received errors
|
||||
|
||||
- Hello
|
||||
+ Hello in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -2570,16 +2516,16 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Unexpected component stack for:
|
||||
"Hello
|
||||
in div (at **)"
|
||||
Unexpected error(s) recorded.
|
||||
|
||||
Unexpected component stack for:
|
||||
"Bye
|
||||
in div (at **)"
|
||||
- Expected errors
|
||||
+ Received errors
|
||||
|
||||
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."
|
||||
- Hello
|
||||
+ Hello in div (at **)
|
||||
Good day
|
||||
- Bye
|
||||
+ Bye in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -2678,13 +2624,18 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
it('fails if component stack is passed twice', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error('Hi %s%s', '\n in div', '\n in div');
|
||||
assertConsoleErrorDev(['Hi']);
|
||||
assertConsoleErrorDev(['Hi \n in div (at **)']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Received more than one component stack for a warning:
|
||||
"Hi %s%s""
|
||||
Unexpected error(s) recorded.
|
||||
|
||||
- Expected errors
|
||||
+ Received errors
|
||||
|
||||
Hi in div (at **)
|
||||
+ in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -2693,16 +2644,23 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
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']);
|
||||
assertConsoleErrorDev([
|
||||
'Hi \n in div (at **)',
|
||||
'Bye \n in div (at **)',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Received more than one component stack for a warning:
|
||||
"Hi %s%s"
|
||||
Unexpected error(s) recorded.
|
||||
|
||||
Received more than one component stack for a warning:
|
||||
"Bye %s%s""
|
||||
- Expected errors
|
||||
+ Received errors
|
||||
|
||||
Hi in div (at **)
|
||||
+ in div (at **)
|
||||
Bye in div (at **)
|
||||
+ in div (at **)"
|
||||
`);
|
||||
});
|
||||
|
||||
@@ -2711,14 +2669,14 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error('Hi \n in div');
|
||||
console.error('Bye \n in div');
|
||||
assertConsoleErrorDev('Hi', 'Bye');
|
||||
assertConsoleErrorDev('Hi \n in div', 'Bye \n in div');
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Expected messages should be an array of strings but was given type "string"."
|
||||
`);
|
||||
assertConsoleErrorDev(['Hi', 'Bye']);
|
||||
assertConsoleErrorDev(['Hi \n in div', 'Bye \n in div']);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
@@ -2733,7 +2691,7 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
|
||||
Expected messages should be an array of strings but was given type "string"."
|
||||
`);
|
||||
assertConsoleErrorDev(['Hi', 'Bye']);
|
||||
assertConsoleErrorDev(['Hi \n in div', 'Bye \n in div']);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
@@ -2749,7 +2707,133 @@ describe('ReactInternalTestUtils console assertions', () => {
|
||||
|
||||
Expected messages should be an array of strings but was given type "string"."
|
||||
`);
|
||||
assertConsoleErrorDev(['Hi', 'Wow', 'Bye']);
|
||||
assertConsoleErrorDev([
|
||||
'Hi \n in div',
|
||||
'Wow \n in div',
|
||||
'Bye \n in div',
|
||||
]);
|
||||
});
|
||||
|
||||
describe('in <stack> placeholder', () => {
|
||||
// @gate __DEV__
|
||||
it('fails if `in <stack>` is used for a component stack instead of an error stack', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error('Warning message\n in div');
|
||||
assertConsoleErrorDev(['Warning message\n in <stack>']);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Incorrect use of \\n in <stack> placeholder. The placeholder is for JavaScript Error stack traces (messages starting with "Error:"), not for React component stacks.
|
||||
|
||||
Expected: "Warning message
|
||||
in <stack>"
|
||||
Received: "Warning message
|
||||
in div (at **)"
|
||||
|
||||
If this error has a component stack, include the full component stack in your expected message (e.g., "Warning message\\n in ComponentName (at **)")."
|
||||
`);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
it('fails if `in <stack>` is used for multiple component stacks', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error('First warning\n in span');
|
||||
console.error('Second warning\n in div');
|
||||
assertConsoleErrorDev([
|
||||
'First warning\n in <stack>',
|
||||
'Second warning\n in <stack>',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Incorrect use of \\n in <stack> placeholder. The placeholder is for JavaScript Error stack traces (messages starting with "Error:"), not for React component stacks.
|
||||
|
||||
Expected: "First warning
|
||||
in <stack>"
|
||||
Received: "First warning
|
||||
in span (at **)"
|
||||
|
||||
If this error has a component stack, include the full component stack in your expected message (e.g., "Warning message\\n in ComponentName (at **)").
|
||||
|
||||
Incorrect use of \\n in <stack> placeholder. The placeholder is for JavaScript Error stack traces (messages starting with "Error:"), not for React component stacks.
|
||||
|
||||
Expected: "Second warning
|
||||
in <stack>"
|
||||
Received: "Second warning
|
||||
in div (at **)"
|
||||
|
||||
If this error has a component stack, include the full component stack in your expected message (e.g., "Warning message\\n in ComponentName (at **)")."
|
||||
`);
|
||||
});
|
||||
|
||||
it('allows `in <stack>` for actual error stack traces', () => {
|
||||
// This should pass - \n in <stack> is correctly used for an error stack
|
||||
console.error(new Error('Something went wrong'));
|
||||
assertConsoleErrorDev(['Error: Something went wrong\n in <stack>']);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
it('fails if error stack trace is present but \\n in <stack> is not expected', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error(new Error('Something went wrong'));
|
||||
assertConsoleErrorDev(['Error: Something went wrong']);
|
||||
});
|
||||
expect(message).toMatch(`Unexpected error stack trace for:`);
|
||||
expect(message).toMatch(`Error: Something went wrong`);
|
||||
expect(message).toMatch(
|
||||
'If this error should include an error stack trace, add \\n in <stack> to your expected message'
|
||||
);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
it('fails if `in <stack>` is expected but no stack is present', () => {
|
||||
const message = expectToThrowFailure(() => {
|
||||
console.error('Error: Something went wrong');
|
||||
assertConsoleErrorDev([
|
||||
'Error: Something went wrong\n in <stack>',
|
||||
]);
|
||||
});
|
||||
expect(message).toMatchInlineSnapshot(`
|
||||
"assertConsoleErrorDev(expected)
|
||||
|
||||
Missing error stack trace for:
|
||||
"Error: Something went wrong"
|
||||
|
||||
The expected message uses \\n in <stack> but the actual error doesn't include an error stack trace.
|
||||
If this error should not have an error stack trace, remove \\n in <stack> from your expected message."
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('[Environment] placeholder', () => {
|
||||
// @gate __DEV__
|
||||
it('expands [Server] to ANSI escape sequence for server badge', () => {
|
||||
const badge = '\u001b[0m\u001b[7m Server \u001b[0m';
|
||||
console.error(badge + 'Error: something went wrong');
|
||||
assertConsoleErrorDev([
|
||||
['[Server] Error: something went wrong', {withoutStack: true}],
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
it('expands [Prerender] to ANSI escape sequence for server badge', () => {
|
||||
const badge = '\u001b[0m\u001b[7m Prerender \u001b[0m';
|
||||
console.error(badge + 'Error: something went wrong');
|
||||
assertConsoleErrorDev([
|
||||
['[Prerender] Error: something went wrong', {withoutStack: true}],
|
||||
]);
|
||||
});
|
||||
|
||||
// @gate __DEV__
|
||||
it('expands [Cache] to ANSI escape sequence for server badge', () => {
|
||||
const badge = '\u001b[0m\u001b[7m Cache \u001b[0m';
|
||||
console.error(badge + 'Error: something went wrong');
|
||||
assertConsoleErrorDev([
|
||||
['[Cache] Error: something went wrong', {withoutStack: true}],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail if waitFor is called before asserting', async () => {
|
||||
|
||||
@@ -168,6 +168,53 @@ function normalizeCodeLocInfo(str) {
|
||||
});
|
||||
}
|
||||
|
||||
// Expands environment placeholders like [Server] into ANSI escape sequences.
|
||||
// This allows test assertions to use a cleaner syntax like "[Server] Error:"
|
||||
// instead of the full escape sequence "\u001b[0m\u001b[7m Server \u001b[0mError:"
|
||||
function expandEnvironmentPlaceholders(str) {
|
||||
if (typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
// [Environment] -> ANSI escape sequence for environment badge
|
||||
// The format is: reset + inverse + " Environment " + reset
|
||||
return str.replace(
|
||||
/^\[(\w+)] /g,
|
||||
(match, env) => '\u001b[0m\u001b[7m ' + env + ' \u001b[0m',
|
||||
);
|
||||
}
|
||||
|
||||
// The error stack placeholder that can be used in expected messages
|
||||
const ERROR_STACK_PLACEHOLDER = '\n in <stack>';
|
||||
// A marker used to protect the placeholder during normalization
|
||||
const ERROR_STACK_PLACEHOLDER_MARKER = '\n in <__STACK_PLACEHOLDER__>';
|
||||
|
||||
// Normalizes expected messages, handling special placeholders
|
||||
function normalizeExpectedMessage(str) {
|
||||
if (typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
// Protect the error stack placeholder from normalization
|
||||
// (normalizeCodeLocInfo would add "(at **)" to it)
|
||||
const hasStackPlaceholder = str.includes(ERROR_STACK_PLACEHOLDER);
|
||||
let result = str;
|
||||
if (hasStackPlaceholder) {
|
||||
result = result.replace(
|
||||
ERROR_STACK_PLACEHOLDER,
|
||||
ERROR_STACK_PLACEHOLDER_MARKER,
|
||||
);
|
||||
}
|
||||
result = normalizeCodeLocInfo(result);
|
||||
result = expandEnvironmentPlaceholders(result);
|
||||
if (hasStackPlaceholder) {
|
||||
// Restore the placeholder (remove the "(at **)" that was added)
|
||||
result = result.replace(
|
||||
ERROR_STACK_PLACEHOLDER_MARKER + ' (at **)',
|
||||
ERROR_STACK_PLACEHOLDER,
|
||||
);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeComponentStack(entry) {
|
||||
if (
|
||||
typeof entry[0] === 'string' &&
|
||||
@@ -187,6 +234,15 @@ const isLikelyAComponentStack = message =>
|
||||
message.includes('\n in ') ||
|
||||
message.includes('\n at '));
|
||||
|
||||
// Error stack traces start with "*Error:" and contain "at" frames with file paths
|
||||
// Component stacks contain "in ComponentName" patterns
|
||||
// This helps validate that \n in <stack> is used correctly
|
||||
const isLikelyAnErrorStackTrace = message =>
|
||||
typeof message === 'string' &&
|
||||
message.includes('Error:') &&
|
||||
// Has "at" frames typical of error stacks (with file:line:col)
|
||||
/\n\s+at .+\(.*:\d+:\d+\)/.test(message);
|
||||
|
||||
export function createLogAssertion(
|
||||
consoleMethod,
|
||||
matcherName,
|
||||
@@ -236,13 +292,11 @@ export function createLogAssertion(
|
||||
|
||||
const withoutStack = options.withoutStack;
|
||||
|
||||
// Warn about invalid global withoutStack values.
|
||||
if (consoleMethod === 'log' && withoutStack !== undefined) {
|
||||
throwFormattedError(
|
||||
`Do not pass withoutStack to assertConsoleLogDev, console.log does not have component stacks.`,
|
||||
);
|
||||
} else if (withoutStack !== undefined && withoutStack !== true) {
|
||||
// withoutStack can only have a value true.
|
||||
throwFormattedError(
|
||||
`The second argument must be {withoutStack: true}.` +
|
||||
`\n\nInstead received ${JSON.stringify(options)}.`,
|
||||
@@ -256,8 +310,11 @@ export function createLogAssertion(
|
||||
const unexpectedLogs = [];
|
||||
const unexpectedMissingComponentStack = [];
|
||||
const unexpectedIncludingComponentStack = [];
|
||||
const unexpectedMissingErrorStack = [];
|
||||
const unexpectedIncludingErrorStack = [];
|
||||
const logsMismatchingFormat = [];
|
||||
const logsWithExtraComponentStack = [];
|
||||
const stackTracePlaceholderMisuses = [];
|
||||
|
||||
// Loop over all the observed logs to determine:
|
||||
// - Which expected logs are missing
|
||||
@@ -319,11 +376,11 @@ export function createLogAssertion(
|
||||
);
|
||||
}
|
||||
|
||||
expectedMessage = normalizeCodeLocInfo(currentExpectedMessage);
|
||||
expectedMessage = normalizeExpectedMessage(currentExpectedMessage);
|
||||
expectedWithoutStack = expectedMessageOrArray[1].withoutStack;
|
||||
} else if (typeof expectedMessageOrArray === 'string') {
|
||||
// Should be in the form assert(['log']) or assert(['log'], {withoutStack: true})
|
||||
expectedMessage = normalizeCodeLocInfo(expectedMessageOrArray);
|
||||
expectedMessage = normalizeExpectedMessage(expectedMessageOrArray);
|
||||
// withoutStack: inherit from global option - simplify when withoutStack is removed.
|
||||
if (consoleMethod === 'log') {
|
||||
expectedWithoutStack = true;
|
||||
} else {
|
||||
@@ -381,19 +438,93 @@ export function createLogAssertion(
|
||||
}
|
||||
|
||||
// Main logic to check if log is expected, with the component stack.
|
||||
if (
|
||||
typeof expectedMessage === 'string' &&
|
||||
(normalizedMessage === expectedMessage ||
|
||||
normalizedMessage.includes(expectedMessage))
|
||||
) {
|
||||
// Check for exact match OR if the message matches with a component stack appended
|
||||
let matchesExpectedMessage = false;
|
||||
let expectsErrorStack = false;
|
||||
const hasErrorStack = isLikelyAnErrorStackTrace(message);
|
||||
|
||||
if (typeof expectedMessage === 'string') {
|
||||
if (normalizedMessage === expectedMessage) {
|
||||
matchesExpectedMessage = true;
|
||||
} else if (expectedMessage.includes('\n in <stack>')) {
|
||||
expectsErrorStack = true;
|
||||
// \n in <stack> is ONLY for JavaScript Error stack traces (e.g., "Error: message\n at fn (file.js:1:2)")
|
||||
// NOT for React component stacks (e.g., "\n in ComponentName (at **)").
|
||||
// Validate that the actual message looks like an error stack trace.
|
||||
if (!hasErrorStack) {
|
||||
// The actual message doesn't look like an error stack trace.
|
||||
// This is likely a misuse - someone used \n in <stack> for a component stack.
|
||||
stackTracePlaceholderMisuses.push({
|
||||
expected: expectedMessage,
|
||||
received: normalizedMessage,
|
||||
});
|
||||
}
|
||||
|
||||
const expectedMessageWithoutStack = expectedMessage.replace(
|
||||
'\n in <stack>',
|
||||
'',
|
||||
);
|
||||
if (normalizedMessage.startsWith(expectedMessageWithoutStack)) {
|
||||
// Remove the stack trace
|
||||
const remainder = normalizedMessage.slice(
|
||||
expectedMessageWithoutStack.length,
|
||||
);
|
||||
|
||||
// After normalization, both error stacks and component stacks look like
|
||||
// component stacks (at frames are converted to "in ... (at **)" format).
|
||||
// So we check isLikelyAComponentStack for matching purposes.
|
||||
if (isLikelyAComponentStack(remainder)) {
|
||||
const messageWithoutStack = normalizedMessage.replace(
|
||||
remainder,
|
||||
'',
|
||||
);
|
||||
if (messageWithoutStack === expectedMessageWithoutStack) {
|
||||
matchesExpectedMessage = true;
|
||||
}
|
||||
} else if (remainder === '') {
|
||||
// \n in <stack> was expected but there's no stack at all
|
||||
matchesExpectedMessage = true;
|
||||
}
|
||||
} else if (normalizedMessage === expectedMessageWithoutStack) {
|
||||
// \n in <stack> was expected but actual has no stack at all (exact match without stack)
|
||||
matchesExpectedMessage = true;
|
||||
}
|
||||
} else if (
|
||||
hasErrorStack &&
|
||||
!expectedMessage.includes('\n in <stack>') &&
|
||||
normalizedMessage.startsWith(expectedMessage)
|
||||
) {
|
||||
matchesExpectedMessage = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchesExpectedMessage) {
|
||||
// withoutStack: Check for unexpected/missing component stacks.
|
||||
// These checks can be simplified when withoutStack is removed.
|
||||
if (isLikelyAComponentStack(normalizedMessage)) {
|
||||
if (expectedWithoutStack === true) {
|
||||
if (expectedWithoutStack === true && !hasErrorStack) {
|
||||
// Only report unexpected component stack if it's not an error stack
|
||||
// (error stacks look like component stacks after normalization)
|
||||
unexpectedIncludingComponentStack.push(normalizedMessage);
|
||||
}
|
||||
} else if (expectedWithoutStack !== true) {
|
||||
} else if (expectedWithoutStack !== true && !expectsErrorStack) {
|
||||
unexpectedMissingComponentStack.push(normalizedMessage);
|
||||
}
|
||||
|
||||
// Check for unexpected/missing error stacks
|
||||
if (hasErrorStack && !expectsErrorStack) {
|
||||
// Error stack is present but \n in <stack> was not in the expected message
|
||||
unexpectedIncludingErrorStack.push(normalizedMessage);
|
||||
} else if (
|
||||
expectsErrorStack &&
|
||||
!hasErrorStack &&
|
||||
!isLikelyAComponentStack(normalizedMessage)
|
||||
) {
|
||||
// \n in <stack> was expected but the actual message doesn't have any stack at all
|
||||
// (if it has a component stack, stackTracePlaceholderMisuses already handles it)
|
||||
unexpectedMissingErrorStack.push(normalizedMessage);
|
||||
}
|
||||
|
||||
// Found expected log, remove it from missing.
|
||||
missingExpectedLogs.splice(0, 1);
|
||||
} else {
|
||||
@@ -422,6 +553,21 @@ export function createLogAssertion(
|
||||
)}`;
|
||||
}
|
||||
|
||||
// Wrong %s formatting is a failure.
|
||||
// This is a common mistake when creating new warnings.
|
||||
if (logsMismatchingFormat.length > 0) {
|
||||
throwFormattedError(
|
||||
logsMismatchingFormat
|
||||
.map(
|
||||
item =>
|
||||
`Received ${item.args.length} arguments for a message with ${
|
||||
item.expectedArgCount
|
||||
} placeholders:\n ${printReceived(item.format)}`,
|
||||
)
|
||||
.join('\n\n'),
|
||||
);
|
||||
}
|
||||
|
||||
// Any unexpected warnings should be treated as a failure.
|
||||
if (unexpectedLogs.length > 0) {
|
||||
throwFormattedError(
|
||||
@@ -466,18 +612,33 @@ export function createLogAssertion(
|
||||
);
|
||||
}
|
||||
|
||||
// Wrong %s formatting is a failure.
|
||||
// This is a common mistake when creating new warnings.
|
||||
if (logsMismatchingFormat.length > 0) {
|
||||
// Any logs that include an error stack trace but \n in <stack> wasn't expected.
|
||||
if (unexpectedIncludingErrorStack.length > 0) {
|
||||
throwFormattedError(
|
||||
logsMismatchingFormat
|
||||
`${unexpectedIncludingErrorStack
|
||||
.map(
|
||||
item =>
|
||||
`Received ${item.args.length} arguments for a message with ${
|
||||
item.expectedArgCount
|
||||
} placeholders:\n ${printReceived(item.format)}`,
|
||||
stack =>
|
||||
`Unexpected error stack trace for:\n ${printReceived(stack)}`,
|
||||
)
|
||||
.join('\n\n'),
|
||||
.join(
|
||||
'\n\n',
|
||||
)}\n\nIf this ${logName()} should include an error stack trace, add \\n in <stack> to your expected message ` +
|
||||
`(e.g., "Error: message\\n in <stack>").`,
|
||||
);
|
||||
}
|
||||
|
||||
// Any logs that are missing an error stack trace when \n in <stack> was expected.
|
||||
if (unexpectedMissingErrorStack.length > 0) {
|
||||
throwFormattedError(
|
||||
`${unexpectedMissingErrorStack
|
||||
.map(
|
||||
stack =>
|
||||
`Missing error stack trace for:\n ${printReceived(stack)}`,
|
||||
)
|
||||
.join(
|
||||
'\n\n',
|
||||
)}\n\nThe expected message uses \\n in <stack> but the actual ${logName()} doesn't include an error stack trace.` +
|
||||
`\nIf this ${logName()} should not have an error stack trace, remove \\n in <stack> from your expected message.`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -496,6 +657,25 @@ export function createLogAssertion(
|
||||
.join('\n\n'),
|
||||
);
|
||||
}
|
||||
|
||||
// Using \n in <stack> for component stacks is a misuse.
|
||||
// \n in <stack> should only be used for JavaScript Error stack traces,
|
||||
// not for React component stacks.
|
||||
if (stackTracePlaceholderMisuses.length > 0) {
|
||||
throwFormattedError(
|
||||
`${stackTracePlaceholderMisuses
|
||||
.map(
|
||||
item =>
|
||||
`Incorrect use of \\n in <stack> placeholder. The placeholder is for JavaScript Error ` +
|
||||
`stack traces (messages starting with "Error:"), not for React component stacks.\n\n` +
|
||||
`Expected: ${printReceived(item.expected)}\n` +
|
||||
`Received: ${printReceived(item.received)}\n\n` +
|
||||
`If this ${logName()} has a component stack, include the full component stack in your expected message ` +
|
||||
`(e.g., "Warning message\\n in ComponentName (at **)").`,
|
||||
)
|
||||
.join('\n\n')}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1729,7 +1729,8 @@ describe('ReactFlight', () => {
|
||||
'Only plain objects can be passed to Client Components from Server Components. ' +
|
||||
'Objects with symbol properties like Symbol.iterator are not supported.\n' +
|
||||
' <... value={{}}>\n' +
|
||||
' ^^^^\n',
|
||||
' ^^^^\n' +
|
||||
' in (at **)',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -3258,7 +3259,7 @@ describe('ReactFlight', () => {
|
||||
const transport = ReactNoopFlightServer.render({
|
||||
root: ReactServer.createElement(App),
|
||||
});
|
||||
assertConsoleErrorDev(['Error: err']);
|
||||
assertConsoleErrorDev(['Error: err' + '\n in <stack>']);
|
||||
|
||||
expect(mockConsoleLog).toHaveBeenCalledTimes(1);
|
||||
expect(mockConsoleLog.mock.calls[0][0]).toBe('hi');
|
||||
|
||||
@@ -734,7 +734,11 @@ describe('ReactHooksInspection', () => {
|
||||
});
|
||||
const results = normalizeSourceLoc(tree);
|
||||
expect(results).toHaveLength(1);
|
||||
expect(results[0]).toMatchInlineSnapshot(`
|
||||
expect(results[0]).toMatchInlineSnapshot(
|
||||
{
|
||||
subHooks: [{value: expect.any(Promise)}],
|
||||
},
|
||||
`
|
||||
{
|
||||
"debugInfo": null,
|
||||
"hookSource": {
|
||||
@@ -759,12 +763,13 @@ describe('ReactHooksInspection', () => {
|
||||
"isStateEditable": false,
|
||||
"name": "Use",
|
||||
"subHooks": [],
|
||||
"value": Promise {},
|
||||
"value": Any<Promise>,
|
||||
},
|
||||
],
|
||||
"value": undefined,
|
||||
}
|
||||
`);
|
||||
`,
|
||||
);
|
||||
});
|
||||
|
||||
describe('useDebugValue', () => {
|
||||
|
||||
@@ -548,16 +548,23 @@ describe('ReactDOM', () => {
|
||||
' in App (at **)',
|
||||
// ReactDOM(App > div > ServerEntry) >>> ReactDOMServer(Child) >>> ReactDOMServer(App2) >>> ReactDOMServer(blink)
|
||||
'Invalid ARIA attribute `ariaTypo2`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' +
|
||||
' in blink (at **)',
|
||||
' in blink (at **)\n' +
|
||||
' in App2 (at **)\n' +
|
||||
' in Child (at **)\n' +
|
||||
' in ServerEntry (at **)',
|
||||
// ReactDOM(App > div > ServerEntry) >>> ReactDOMServer(Child) >>> ReactDOMServer(App2 > Child2 > span)
|
||||
'Invalid ARIA attribute `ariaTypo3`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' +
|
||||
' in span (at **)\n' +
|
||||
' in Child2 (at **)\n' +
|
||||
' in App2 (at **)',
|
||||
' in App2 (at **)\n' +
|
||||
' in Child (at **)\n' +
|
||||
' in ServerEntry (at **)',
|
||||
// ReactDOM(App > div > ServerEntry) >>> ReactDOMServer(Child > span)
|
||||
'Invalid ARIA attribute `ariaTypo4`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' +
|
||||
' in span (at **)\n' +
|
||||
' in Child (at **)',
|
||||
' in Child (at **)\n' +
|
||||
' in ServerEntry (at **)',
|
||||
|
||||
// ReactDOM(App > div > font)
|
||||
'Invalid ARIA attribute `ariaTypo5`. ARIA attributes follow the pattern aria-* and must be lowercase.\n' +
|
||||
' in font (at **)\n' +
|
||||
@@ -775,7 +782,11 @@ describe('ReactDOM', () => {
|
||||
|
||||
// @TODO remove this warning check when we loosen the tag nesting restrictions to allow arbitrary tags at the
|
||||
// root of the application
|
||||
assertConsoleErrorDev(['In HTML, <head> cannot be a child of <main>']);
|
||||
assertConsoleErrorDev([
|
||||
'In HTML, <head> cannot be a child of <main>.\nThis will cause a hydration error.\n' +
|
||||
' in head (at **)\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
|
||||
await act(() => {
|
||||
root.render(<App phase={1} />);
|
||||
|
||||
@@ -6879,9 +6879,12 @@ describe('ReactDOMFizzServer', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(finished).toBe(true);
|
||||
@@ -6943,9 +6946,12 @@ describe('ReactDOMFizzServer', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(finished).toBe(true);
|
||||
@@ -7007,9 +7013,12 @@ describe('ReactDOMFizzServer', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(finished).toBe(true);
|
||||
@@ -7069,9 +7078,12 @@ describe('ReactDOMFizzServer', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(finished).toBe(true);
|
||||
@@ -9024,7 +9036,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
pipe(writable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0mR4nd0m". When React manages style rules using `precedence` it will only include rules if the nonce matches the style nonce "R4nd0m" that was included with this render.',
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0mR4nd0m". When React manages style rules using `precedence` it will only include rules if the nonce matches the style nonce "R4nd0m" that was included with this render.' +
|
||||
'\n in style (at **)',
|
||||
]);
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
@@ -9054,7 +9067,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
pipe(writable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.' +
|
||||
'\n in style (at **)',
|
||||
]);
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
@@ -9085,7 +9099,8 @@ describe('ReactDOMFizzServer', () => {
|
||||
pipe(writable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.',
|
||||
'React encountered a style tag with `precedence` "default" and `nonce` "R4nd0m". When React manages style rules using `precedence` it will only include a nonce attributes if you also provide the same style nonce value as a render option.' +
|
||||
'\n in style (at **)',
|
||||
]);
|
||||
expect(getVisibleChildren(document)).toEqual(
|
||||
<html>
|
||||
|
||||
@@ -3628,7 +3628,24 @@ body {
|
||||
assertLog(['load stylesheet: foo']);
|
||||
await waitForAll([]);
|
||||
assertConsoleErrorDev([
|
||||
"Hydration failed because the server rendered HTML didn't match the client.",
|
||||
"Error: Hydration failed because the server rendered HTML didn't match the client. " +
|
||||
'As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:\n\n' +
|
||||
"- A server/client branch `if (typeof window !== 'undefined')`.\n" +
|
||||
"- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.\n" +
|
||||
"- Date formatting in a user's locale which doesn't match the server.\n" +
|
||||
'- External changing data without sending a snapshot of it along with the HTML.\n' +
|
||||
'- Invalid HTML tag nesting.\n\n' +
|
||||
'It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.\n\n' +
|
||||
'https://react.dev/link/hydration-mismatch\n\n' +
|
||||
' <html>\n' +
|
||||
' <body>\n' +
|
||||
' <div>\n' +
|
||||
' <div>\n' +
|
||||
' <Suspense fallback="loading 2...">\n' +
|
||||
' <Component>\n' +
|
||||
' <link>\n' +
|
||||
'+ <div>' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
jest.runAllTimers();
|
||||
|
||||
|
||||
@@ -120,11 +120,14 @@ describe('ReactDOMSrcObject', () => {
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'Passing Blob, MediaSource or MediaStream to <source src> is not supported. ' +
|
||||
'Pass it directly to <img src>, <video src> or <audio src> instead.',
|
||||
'Pass it directly to <img src>, <video src> or <audio src> instead.' +
|
||||
'\n in source (at **)',
|
||||
'Passing Blob, MediaSource or MediaStream to <source src> is not supported. ' +
|
||||
'Pass it directly to <img src>, <video src> or <audio src> instead.',
|
||||
'Pass it directly to <img src>, <video src> or <audio src> instead.' +
|
||||
'\n in source (at **)',
|
||||
'Passing Blob, MediaSource or MediaStream to <source src> is not supported. ' +
|
||||
'Pass it directly to <img src>, <video src> or <audio src> instead.',
|
||||
'Pass it directly to <img src>, <video src> or <audio src> instead.' +
|
||||
'\n in source (at **)',
|
||||
]);
|
||||
expect(videoRef.current.firstChild.src).not.toMatch(/^blob:/);
|
||||
expect(videoRef.current.firstChild.src).toContain('[object%20Blob]'); // toString:ed
|
||||
|
||||
@@ -110,7 +110,8 @@ describe('ReactFlushSync', () => {
|
||||
assertConsoleErrorDev([
|
||||
'flushSync was called from inside a lifecycle method. React ' +
|
||||
'cannot flush when React is already rendering. Consider moving this ' +
|
||||
'call to a scheduler task or micro task.',
|
||||
'call to a scheduler task or micro task.' +
|
||||
'\n in App',
|
||||
]);
|
||||
|
||||
await waitForPaint([]);
|
||||
|
||||
@@ -2145,7 +2145,8 @@ describe('ReactFlightDOM', () => {
|
||||
pipe(flightWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2169,9 +2170,12 @@ describe('ReactFlightDOM', () => {
|
||||
).pipe(fizzWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
@@ -2235,7 +2239,8 @@ describe('ReactFlightDOM', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2260,9 +2265,12 @@ describe('ReactFlightDOM', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
@@ -2327,7 +2335,8 @@ describe('ReactFlightDOM', () => {
|
||||
pipe(flightWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2351,9 +2360,12 @@ describe('ReactFlightDOM', () => {
|
||||
).pipe(fizzWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
@@ -2416,7 +2428,8 @@ describe('ReactFlightDOM', () => {
|
||||
pipe(flightWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2440,9 +2453,12 @@ describe('ReactFlightDOM', () => {
|
||||
).pipe(fizzWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
@@ -2504,7 +2520,8 @@ describe('ReactFlightDOM', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2528,9 +2545,12 @@ describe('ReactFlightDOM', () => {
|
||||
).pipe(fizzWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
@@ -2596,7 +2616,8 @@ describe('ReactFlightDOM', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2620,8 +2641,10 @@ describe('ReactFlightDOM', () => {
|
||||
).pipe(fizzWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'The render was aborted by the server without a reason.',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
@@ -2668,7 +2691,8 @@ describe('ReactFlightDOM', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2692,7 +2716,8 @@ describe('ReactFlightDOM', () => {
|
||||
).pipe(fizzWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
@@ -2760,8 +2785,9 @@ describe('ReactFlightDOM', () => {
|
||||
});
|
||||
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'bam!',
|
||||
'Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'Error: bam!\n in <stack>',
|
||||
]);
|
||||
|
||||
const response =
|
||||
@@ -2785,8 +2811,9 @@ describe('ReactFlightDOM', () => {
|
||||
).pipe(fizzWritable);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'The render was aborted by the server without a reason.',
|
||||
'bam!',
|
||||
'[Server] Error: The render was aborted by the server without a reason.' +
|
||||
'\n in <stack>',
|
||||
'[Server] Error: bam!\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(shellErrors).toEqual([]);
|
||||
|
||||
@@ -957,7 +957,8 @@ describe('ReactFlightDOMForm', () => {
|
||||
'Failed to serialize an action for progressive enhancement:\n' +
|
||||
'Error: React Element cannot be passed to Server Functions from the Client without a temporary reference set. Pass a TemporaryReferenceSet to the options.\n' +
|
||||
' [<div/>]\n' +
|
||||
' ^^^^^^',
|
||||
' ^^^^^^' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
// The error message was returned as JSX.
|
||||
@@ -1032,7 +1033,8 @@ describe('ReactFlightDOMForm', () => {
|
||||
await submitTheForm();
|
||||
assertConsoleErrorDev([
|
||||
'Failed to serialize an action for progressive enhancement:\n' +
|
||||
'Error: File/Blob fields are not yet supported in progressive forms. Will fallback to client hydration.',
|
||||
'Error: File/Blob fields are not yet supported in progressive forms. Will fallback to client hydration.' +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
|
||||
expect(blob instanceof Blob).toBe(true);
|
||||
|
||||
@@ -196,7 +196,8 @@ describe('ReactFlight', () => {
|
||||
"The props of this element may help locate this element: { children: 'Free!', [key]: [Getter] }",
|
||||
{withoutStack: true},
|
||||
],
|
||||
"TypeError: Cannot read properties of undefined (reading 'stack')",
|
||||
"TypeError: Cannot read properties of undefined (reading 'stack')" +
|
||||
'\n in <stack>',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -864,6 +864,7 @@ describe('ReactChildren', () => {
|
||||
});
|
||||
|
||||
it('warns for mapped list children without keys', async () => {
|
||||
spyOnDev(console, 'error').mockImplementation(() => {});
|
||||
function ComponentRenderingMappedChildren({children}) {
|
||||
return (
|
||||
<div>
|
||||
@@ -883,13 +884,14 @@ describe('ReactChildren', () => {
|
||||
</ComponentRenderingMappedChildren>,
|
||||
);
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
'Each child in a list should have a unique "key" prop.\n\n' +
|
||||
'Check the render method of `ComponentRenderingMappedChildren`.' +
|
||||
' See https://react.dev/link/warning-keys for more information.\n' +
|
||||
' in div (at **)\n' +
|
||||
' in **/ReactChildren-test.js:**:** (at **)',
|
||||
]);
|
||||
if (__DEV__) {
|
||||
const calls = console.error.mock.calls;
|
||||
console.error.mockRestore();
|
||||
expect(calls.length).toBe(1);
|
||||
expect(calls[0][0]).toEqual(
|
||||
'Each child in a list should have a unique "key" prop.%s%s See https://react.dev/link/warning-keys for more information.',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not warn for mapped static children without keys', async () => {
|
||||
|
||||
@@ -349,10 +349,13 @@ describe('create-react-class-integration', () => {
|
||||
});
|
||||
assertConsoleErrorDev([
|
||||
[
|
||||
'Component uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
|
||||
'Component uses the legacy childContextTypes API which will soon be removed. ' +
|
||||
'Use React.createContext() instead. (https://react.dev/link/legacy-context)',
|
||||
{withoutStack: true},
|
||||
],
|
||||
'Component uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
|
||||
'Component uses the legacy contextTypes API which will soon be removed. ' +
|
||||
'Use React.createContext() with static contextType instead. (https://react.dev/link/legacy-context)' +
|
||||
'\n in ReactClassComponent (at **)',
|
||||
]);
|
||||
expect(container.firstChild.className).toBe('foo');
|
||||
});
|
||||
|
||||
@@ -641,11 +641,21 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
|
||||
'The result of getSnapshot should be cached to avoid an infinite loop',
|
||||
{withoutStack: true},
|
||||
],
|
||||
'Error: Maximum update depth exceeded',
|
||||
'The above error occurred i',
|
||||
[
|
||||
'Error: Maximum update depth exceeded. ' +
|
||||
'This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. ' +
|
||||
'React limits the number of nested updates to prevent infinite loops.' +
|
||||
'\n in <stack>',
|
||||
{withoutStack: true},
|
||||
],
|
||||
'The above error occurred in the <App> component:\n\n' +
|
||||
' in App (at **)\n\n' +
|
||||
'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
|
||||
'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.',
|
||||
]
|
||||
: [
|
||||
'The result of getSnapshot should be cached to avoid an infinite loop',
|
||||
'The result of getSnapshot should be cached to avoid an infinite loop' +
|
||||
'\n in App (at **)',
|
||||
],
|
||||
);
|
||||
});
|
||||
@@ -839,7 +849,12 @@ describe('Shared useSyncExternalStore behavior (shim and built-in)', () => {
|
||||
await act(() => {
|
||||
ReactDOM.hydrate(React.createElement(App, null), container);
|
||||
});
|
||||
assertConsoleErrorDev(['Text content did not match']);
|
||||
assertConsoleErrorDev([
|
||||
'Warning: Text content did not match. Server: "server" Client: "client"\n' +
|
||||
' in Text (at **)\n' +
|
||||
' in div (at **)\n' +
|
||||
' in App (at **)',
|
||||
]);
|
||||
assertLog(['client', 'Passive effect: client']);
|
||||
}
|
||||
expect(container.textContent).toEqual('client');
|
||||
|
||||
Reference in New Issue
Block a user