Skip to content

Commit

Permalink
[asserts helpers] react package (facebook#31853)
Browse files Browse the repository at this point in the history
Based off facebook#31844

Commit to review:
facebook@11aa104

Converts the rest of the `react` package.
  • Loading branch information
rickhanlonii authored Dec 23, 2024
1 parent 6907aa2 commit 94867f3
Show file tree
Hide file tree
Showing 18 changed files with 1,431 additions and 1,013 deletions.
52 changes: 37 additions & 15 deletions packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,28 @@ describe('ReactLazy', () => {
expect(error.message).toMatch('Element type is invalid');
assertLog(['Loading...']);
assertConsoleErrorDev([
'Expected the result of a dynamic import() call',
'Expected the result of a dynamic import() call',
'lazy: Expected the result of a dynamic import() call. ' +
'Instead received: function Text(props) {\n' +
' Scheduler.log(props.text);\n' +
' return props.text;\n' +
' }\n\n' +
'Your code should look like: \n ' +
"const MyComponent = lazy(() => import('./MyComponent'))\n" +
(gate('enableOwnerStacks')
? ''
: ' in Lazy (at **)\n' + ' in Suspense (at **)\n') +
' in App (at **)',
'lazy: Expected the result of a dynamic import() call. ' +
'Instead received: function Text(props) {\n' +
' Scheduler.log(props.text);\n' +
' return props.text;\n' +
' }\n\n' +
'Your code should look like: \n ' +
"const MyComponent = lazy(() => import('./MyComponent'))\n" +
(gate('enableOwnerStacks')
? ''
: ' in Lazy (at **)\n' + ' in Suspense (at **)\n') +
' in App (at **)',
]);
expect(root).not.toMatchRenderedOutput('Hi');
});
Expand Down Expand Up @@ -852,19 +872,21 @@ describe('ReactLazy', () => {
expect(root).not.toMatchRenderedOutput('22');

// Mount
await expect(async () => {
await act(() => resolveFakeImport(Add));
}).toErrorDev(
shouldWarnAboutFunctionDefaultProps
? [
'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
]
: shouldWarnAboutMemoDefaultProps
? [
'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
]
: [],
);
await act(() => resolveFakeImport(Add));

if (shouldWarnAboutFunctionDefaultProps) {
assertConsoleErrorDev([
'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.\n' +
' in Add (at **)\n' +
' in Suspense (at **)',
]);
} else if (shouldWarnAboutMemoDefaultProps) {
assertConsoleErrorDev([
'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.\n' +
' in Suspense (at **)',
]);
}

expect(root).toMatchRenderedOutput('22');

// Update
Expand Down
217 changes: 132 additions & 85 deletions packages/react/src/__tests__/ReactCoffeeScriptClass-test.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ PropTypes = null
React = null
ReactDOM = null
ReactDOMClient = null
assertConsoleErrorDev = null
assertConsoleWarnDev = null

featureFlags = require 'shared/ReactFeatureFlags'

Expand All @@ -28,6 +30,9 @@ describe 'ReactCoffeeScriptClass', ->
root = ReactDOMClient.createRoot container
attachedListener = null
renderedName = null
TestUtils = require 'internal-test-utils'
assertConsoleErrorDev = TestUtils.assertConsoleErrorDev
assertConsoleWarnDev = TestUtils.assertConsoleWarnDev
InnerComponent = class extends React.Component
getName: -> this.props.name
render: ->
Expand All @@ -53,14 +58,15 @@ describe 'ReactCoffeeScriptClass', ->
event.preventDefault()
caughtErrors.push(event.error)
window.addEventListener 'error', errorHandler;
expect(->
ReactDOM.flushSync ->
root.render React.createElement(Foo)
).toErrorDev([
# A failed component renders twice in DEV in concurrent mode
'No `render` method found on the Foo instance',
'No `render` method found on the Foo instance',
])
ReactDOM.flushSync ->
root.render React.createElement(Foo)
assertConsoleErrorDev [
# A failed component renders twice in DEV in concurrent mode
'No `render` method found on the Foo instance: you may have forgotten to define `render`.\n' +
' in Foo (at **)',
'No `render` method found on the Foo instance: you may have forgotten to define `render`.\n' +
' in Foo (at **)',
]
window.removeEventListener 'error', errorHandler;
expect(caughtErrors).toEqual([
expect.objectContaining(
Expand Down Expand Up @@ -136,35 +142,39 @@ describe 'ReactCoffeeScriptClass', ->
React.createElement('div')
getDerivedStateFromProps: ->
{}
expect(->
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')
return
).toErrorDev 'Foo: getDerivedStateFromProps() is defined as an instance method and will be ignored. Instead, declare it as a static method.'
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')
assertConsoleErrorDev [
'Foo: getDerivedStateFromProps() is defined as an instance method and will be ignored. Instead, declare it as a static method.\n' +
' in Foo (at **)']

it 'warns if getDerivedStateFromError is not static', ->
class Foo extends React.Component
render: ->
React.createElement('div')
getDerivedStateFromError: ->
{}
expect(->
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')
return
).toErrorDev 'Foo: getDerivedStateFromError() is defined as an instance method and will be ignored. Instead, declare it as a static method.'
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')

assertConsoleErrorDev [
'Foo: getDerivedStateFromError() is defined as an instance method and will be ignored. Instead, declare it as a static method.\n' +
' in Foo (at **)'
]

it 'warns if getSnapshotBeforeUpdate is static', ->
class Foo extends React.Component
render: ->
React.createElement('div')
Foo.getSnapshotBeforeUpdate = () ->
{}
expect(->
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')
return
).toErrorDev 'Foo: getSnapshotBeforeUpdate() is defined as a static method and will be ignored. Instead, declare it as an instance method.'
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')

assertConsoleErrorDev [
'Foo: getSnapshotBeforeUpdate() is defined as a static method and will be ignored. Instead, declare it as an instance method.\n' +
' in Foo (at **)'
]

it 'warns if state not initialized before static getDerivedStateFromProps', ->
class Foo extends React.Component
Expand All @@ -177,16 +187,16 @@ describe 'ReactCoffeeScriptClass', ->
foo: nextProps.foo
bar: 'bar'
}
expect(->
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')
return
).toErrorDev (
'`Foo` uses `getDerivedStateFromProps` but its initial state is ' +
'undefined. This is not recommended. Instead, define the initial state by ' +
'assigning an object to `this.state` in the constructor of `Foo`. ' +
'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.'
)
ReactDOM.flushSync ->
root.render React.createElement(Foo, foo: 'foo')

assertConsoleErrorDev [
'`Foo` uses `getDerivedStateFromProps` but its initial state is
undefined. This is not recommended. Instead, define the initial state by
assigning an object to `this.state` in the constructor of `Foo`.
This ensures that `getDerivedStateFromProps` arguments have a consistent shape.\n' +
' in Foo (at **)'
]

it 'updates initial state with values returned by static getDerivedStateFromProps', ->
class Foo extends React.Component
Expand Down Expand Up @@ -254,12 +264,28 @@ describe 'ReactCoffeeScriptClass', ->
render: ->
React.createElement Foo

expect(->
test React.createElement(Outer), 'SPAN', 'foo'
).toErrorDev([
'Outer uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
'Foo uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
])
test React.createElement(Outer), 'SPAN', 'foo'

if featureFlags.enableOwnerStacks
assertConsoleErrorDev([
'Outer uses the legacy childContextTypes API which will soon be removed.
Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' +
' in Outer (at **)',
'Foo 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 Outer (at **)',
]);
else
assertConsoleErrorDev([
'Outer uses the legacy childContextTypes API which will soon be removed.
Use React.createContext() instead. (https://react.dev/link/legacy-context)\n' +
' in Outer (at **)',
'Foo 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 Foo (at **)\n' +
' in Outer (at **)',
]);


it 'renders only once when setting state in componentWillMount', ->
renderCount = 0
Expand All @@ -286,9 +312,11 @@ describe 'ReactCoffeeScriptClass', ->
render: ->
React.createElement('span')

expect(->
test React.createElement(Foo), 'SPAN', ''
).toErrorDev('Foo.state: must be set to an object or null')
test React.createElement(Foo), 'SPAN', ''
assertConsoleErrorDev [
'Foo.state: must be set to an object or null\n' +
' in Foo (at **)'
]

it 'should render with null in the initial state property', ->
class Foo extends React.Component
Expand Down Expand Up @@ -430,14 +458,21 @@ describe 'ReactCoffeeScriptClass', ->
className: 'foo'
)

expect(->
test React.createElement(Foo), 'SPAN', 'foo'
).toErrorDev([
'getInitialState was defined on Foo, a plain JavaScript class.',
'getDefaultProps was defined on Foo, a plain JavaScript class.',
'contextTypes was defined as an instance property on Foo.',
'contextType was defined as an instance property on Foo.',
])
test React.createElement(Foo), 'SPAN', 'foo'
assertConsoleErrorDev [
'getInitialState was defined on Foo, a plain JavaScript class.
This is only supported for classes created using React.createClass.
Did you mean to define a state property instead?\n' +
' in Foo (at **)',
'getDefaultProps was defined on Foo, a plain JavaScript class.
This is only supported for classes created using React.createClass.
Use a static property to define defaultProps instead.\n' +
' in Foo (at **)',
'contextType was defined as an instance property on Foo. Use a static property to define contextType instead.\n' +
' in Foo (at **)',
'contextTypes was defined as an instance property on Foo. Use a static property to define contextTypes instead.\n' +
' in Foo (at **)',
]
expect(getInitialStateWasCalled).toBe false
expect(getDefaultPropsWasCalled).toBe false

Expand Down Expand Up @@ -468,13 +503,13 @@ describe 'ReactCoffeeScriptClass', ->
className: 'foo'
)

expect(->
test React.createElement(NamedComponent), 'SPAN', 'foo'
).toErrorDev(
test React.createElement(NamedComponent), 'SPAN', 'foo'
assertConsoleErrorDev [
'NamedComponent has a method called componentShouldUpdate().
Did you mean shouldComponentUpdate()? The name is phrased as a
question because the function is expected to return a value.'
)
question because the function is expected to return a value.\n' +
' in NamedComponent (at **)'
]

it 'should warn when misspelling componentWillReceiveProps', ->
class NamedComponent extends React.Component
Expand All @@ -486,12 +521,12 @@ describe 'ReactCoffeeScriptClass', ->
className: 'foo'
)

expect(->
test React.createElement(NamedComponent), 'SPAN', 'foo'
).toErrorDev(
test React.createElement(NamedComponent), 'SPAN', 'foo'
assertConsoleErrorDev [
'NamedComponent has a method called componentWillRecieveProps().
Did you mean componentWillReceiveProps()?'
)
Did you mean componentWillReceiveProps()?\n' +
' in NamedComponent (at **)'
]

it 'should warn when misspelling UNSAFE_componentWillReceiveProps', ->
class NamedComponent extends React.Component
Expand All @@ -503,28 +538,28 @@ describe 'ReactCoffeeScriptClass', ->
className: 'foo'
)

expect(->
test React.createElement(NamedComponent), 'SPAN', 'foo'
).toErrorDev(
test React.createElement(NamedComponent), 'SPAN', 'foo'
assertConsoleErrorDev [
'NamedComponent has a method called UNSAFE_componentWillRecieveProps().
Did you mean UNSAFE_componentWillReceiveProps()?'
)
Did you mean UNSAFE_componentWillReceiveProps()?\n' +
' in NamedComponent (at **)'
]

it 'should throw AND warn when trying to access classic APIs', ->
ref = React.createRef()
test React.createElement(InnerComponent, name: 'foo', ref: ref), 'DIV', 'foo'
expect(->
expect(-> ref.current.replaceState {}).toThrow()
).toWarnDev(
'replaceState(...) is deprecated in plain JavaScript React classes',
{withoutStack: true}
)
expect(->
expect(-> ref.current.isMounted()).toThrow()
).toWarnDev(
'isMounted(...) is deprecated in plain JavaScript React classes',
{withoutStack: true}
)

expect(-> ref.current.replaceState {}).toThrow()
assertConsoleWarnDev([
'replaceState(...) is deprecated in plain JavaScript React classes.
Refactor your code to use setState instead (see https://github.com/facebook/react/issues/3236).'
], {withoutStack: true})

expect(-> ref.current.isMounted()).toThrow()
assertConsoleWarnDev([
'isMounted(...) is deprecated in plain JavaScript React classes.
Instead, make sure to clean up subscriptions and pending requests in componentWillUnmount to prevent memory leaks.',
], {withoutStack: true})

if !featureFlags.disableLegacyContext
it 'supports this.context passed via getChildContext', ->
Expand All @@ -542,13 +577,25 @@ describe 'ReactCoffeeScriptClass', ->
render: ->
React.createElement Bar

expect(->
test React.createElement(Foo), 'DIV', 'bar-through-context'
).toErrorDev(
[
'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.',
'Bar uses the legacy contextTypes API which will soon be removed. Use React.createContext() with static contextType instead.',
],
)
test React.createElement(Foo), 'DIV', 'bar-through-context'
if featureFlags.enableOwnerStacks
assertConsoleErrorDev [
'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.
(https://react.dev/link/legacy-context)\n' +
' in Foo (at **)',
'Bar 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 Foo (at **)'
]
else
assertConsoleErrorDev [
'Foo uses the legacy childContextTypes API which will soon be removed. Use React.createContext() instead.
(https://react.dev/link/legacy-context)\n' +
' in Foo (at **)',
'Bar 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 Bar (at **)\n' +
' in Foo (at **)'
]

undefined
Loading

0 comments on commit 94867f3

Please sign in to comment.