12
// TODO: All these warnings should become static errors using Flow instead
13
// of dynamic errors when using JSX with Flow.
14
let React;
15
-let ReactDOM;
16
-let ReactDOMClient;
15
let ReactTestUtils;
18
-let PropTypes;
19
-let act;
16
17
describe('ReactJSXElementValidator', () => {
18
let Component;
21
beforeEach(() => {
22
jest.resetModules();
23
28
- PropTypes = require('prop-types');
24
React = require('react');
30
- ReactDOM = require('react-dom');
31
- ReactDOMClient = require('react-dom/client');
25
ReactTestUtils = require('react-dom/test-utils');
33
- act = require('internal-test-utils').act;
26
27
Component = class extends React.Component {
28
render() {
36
}
37
};
38
RequiredPropComponent.displayName = 'RequiredPropComponent';
47
- RequiredPropComponent.propTypes = {prop: PropTypes.string.isRequired};
39
});
40
41
it('warns for keys for arrays of elements in children position', () => {
141
void (<Component>{[{}, {}]}</Component>);
142
});
143
153
- it('should give context for PropType errors in nested components.', () => {
154
- // In this test, we're making sure that if a proptype error is found in a
155
- // component, we give a small hint as to which parent instantiated that
156
- // component as per warnings about key usage in ReactElementValidator.
144
+ it('should give context for errors in nested components.', () => {
145
class MyComp extends React.Component {
146
render() {
159
- return <div>My color is {this.color}</div>;
147
+ return [<div />];
148
}
149
}
162
- MyComp.propTypes = {
163
- color: PropTypes.string,
164
- };
150
class ParentComp extends React.Component {
151
render() {
167
- return <MyComp color={123} />;
152
+ return <MyComp />;
153
}
154
}
155
expect(() => ReactTestUtils.renderIntoDocument(<ParentComp />)).toErrorDev(
171
- 'Warning: Failed prop type: ' +
172
- 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
173
- 'expected `string`.\n' +
156
+ 'Each child in a list should have a unique "key" prop. ' +
157
+ 'See https://reactjs.org/link/warning-keys for more information.\n' +
158
' in MyComp (at **)\n' +
159
' in ParentComp (at **)',
160
);
161
});
162
179
- it('should update component stack after receiving next element', async () => {
180
- function MyComp() {
181
- return null;
182
- }
183
- MyComp.propTypes = {
184
- color: PropTypes.string,
185
- };
186
- function MiddleComp(props) {
187
- return <MyComp color={props.color} />;
188
- }
189
- function ParentComp(props) {
190
- if (props.warn) {
191
- // This element has a source thanks to JSX.
192
- return <MiddleComp color={42} />;
193
- }
194
- // This element has no source.
195
- return React.createElement(MiddleComp, {color: 'blue'});
196
- }
197
-
198
- const container = document.createElement('div');
199
- const root = ReactDOMClient.createRoot(container);
200
- await act(() => {
201
- root.render(<ParentComp warn={false} />);
202
- });
203
- expect(() =>
204
- ReactDOM.flushSync(() => {
205
- root.render(<ParentComp warn={true} />);
206
- }),
207
- ).toErrorDev(
208
- 'Warning: Failed prop type: ' +
209
- 'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
210
- 'expected `string`.\n' +
211
- ' in MyComp (at **)\n' +
212
- ' in MiddleComp (at **)\n' +
213
- ' in ParentComp (at **)',
214
- );
215
- });
216
-
163
it('gives a helpful error when passing null, undefined, or boolean', () => {
164
const Undefined = undefined;
165
const Null = null;
192
void (<Div />);
193
});
194
249
- it('should check default prop values', () => {
250
- RequiredPropComponent.defaultProps = {prop: null};
251
-
252
- expect(() =>
253
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent />),
254
- ).toErrorDev(
255
- 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
256
- '`RequiredPropComponent`, but its value is `null`.\n' +
257
- ' in RequiredPropComponent (at **)',
258
- );
259
- });
260
-
261
- it('should not check the default for explicit null', () => {
262
- expect(() =>
263
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={null} />),
264
- ).toErrorDev(
265
- 'Warning: Failed prop type: The prop `prop` is marked as required in ' +
266
- '`RequiredPropComponent`, but its value is `null`.\n' +
267
- ' in RequiredPropComponent (at **)',
268
- );
269
- });
270
-
271
- it('should check declared prop types', () => {
272
- expect(() =>
273
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent />),
274
- ).toErrorDev(
275
- 'Warning: Failed prop type: ' +
276
- 'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
277
- 'its value is `undefined`.\n' +
278
- ' in RequiredPropComponent (at **)',
279
- );
280
- expect(() =>
281
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={42} />),
282
- ).toErrorDev(
283
- 'Warning: Failed prop type: ' +
284
- 'Invalid prop `prop` of type `number` supplied to ' +
285
- '`RequiredPropComponent`, expected `string`.\n' +
286
- ' in RequiredPropComponent (at **)',
287
- );
288
-
289
- // Should not error for strings
290
- ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop="string" />);
291
- });
292
-
293
- it('should warn on invalid prop types', () => {
294
- // Since there is no prevalidation step for ES6 classes, there is no hook
295
- // for us to issue a warning earlier than element creation when the error
296
- // actually occurs. Since this step is skipped in production, we should just
297
- // warn instead of throwing for this case.
298
- class NullPropTypeComponent extends React.Component {
299
- render() {
300
- return <span>{this.props.prop}</span>;
301
- }
302
- }
303
- NullPropTypeComponent.propTypes = {
304
- prop: null,
305
- };
306
- expect(() =>
307
- ReactTestUtils.renderIntoDocument(<NullPropTypeComponent />),
308
- ).toErrorDev(
309
- 'NullPropTypeComponent: prop type `prop` is invalid; it must be a ' +
310
- 'function, usually from the `prop-types` package,',
311
- );
312
- });
313
-
314
- // @gate !disableLegacyContext || !__DEV__
315
- it('should not warn on invalid context types', () => {
316
- class NullContextTypeComponent extends React.Component {
317
- render() {
318
- return <span>{this.props.prop}</span>;
319
- }
320
- }
321
- NullContextTypeComponent.contextTypes = {
322
- prop: null,
323
- };
324
- ReactTestUtils.renderIntoDocument(<NullContextTypeComponent />);
325
- });
326
-
327
- it('should warn if getDefaultProps is specified on the class', () => {
328
- class GetDefaultPropsComponent extends React.Component {
329
- render() {
330
- return <span>{this.props.prop}</span>;
331
- }
332
- }
333
- GetDefaultPropsComponent.getDefaultProps = () => ({
334
- prop: 'foo',
335
- });
336
- expect(() =>
337
- ReactTestUtils.renderIntoDocument(<GetDefaultPropsComponent />),
338
- ).toErrorDev(
339
- 'getDefaultProps is only used on classic React.createClass definitions.' +
340
- ' Use a static property named `defaultProps` instead.',
341
- {withoutStack: true},
342
- );
343
- });
344
-
345
- it('should warn if component declares PropTypes instead of propTypes', () => {
346
- class MisspelledPropTypesComponent extends React.Component {
347
- render() {
348
- return <span>{this.props.prop}</span>;
349
- }
350
- }
351
- MisspelledPropTypesComponent.PropTypes = {
352
- prop: PropTypes.string,
353
- };
354
- expect(() =>
355
- ReactTestUtils.renderIntoDocument(
356
- <MisspelledPropTypesComponent prop="hi" />,
357
- ),
358
- ).toErrorDev(
359
- 'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
360
- 'instead of `propTypes`. Did you misspell the property assignment?',
361
- {withoutStack: true},
362
- );
363
- });
364
-
195
it('warns for fragments with illegal attributes', () => {
196
class Foo extends React.Component {
197
render() {