21
22
var ReactTestUtils;
23
24
-var Props = require('ReactPropTypes');
24
+var PropTypes = require('ReactPropTypes');
25
var React = require('React');
26
var ReactPropTypeLocations = require('ReactPropTypeLocations');
27
28
-var warn;
29
-var mocks;
30
-
31
-function typeCheck(declaration, value) {
32
- var props = {};
33
- if (arguments.length > 1) {
34
- props.testProp = value;
35
- }
36
- return declaration(
37
- props, 'testProp', 'testComponent', ReactPropTypeLocations.prop
28
+var Component;
29
+var MyComponent;
30
+var requiredMessage =
31
+ 'Required prop `testProp` was not specified in `testComponent`.';
32
+
33
+function typeCheckFail(declaration, value, message) {
34
+ var props = {testProp: value};
35
+ var error = declaration(
36
+ props,
37
+ 'testProp',
38
+ 'testComponent',
39
+ ReactPropTypeLocations.prop
40
);
41
+ expect(error instanceof Error).toBe(true);
42
+ expect(error.message).toBe(message);
43
}
44
41
-var MyComponent = React.createClass({
42
- render: function() {
43
- return <div />;
44
- }
45
-});
45
+function typeCheckPass(declaration, value) {
46
+ var props = {testProp: value};
47
+ var error = declaration(
48
+ props,
49
+ 'testProp',
50
+ 'testComponent',
51
+ ReactPropTypeLocations.prop
52
+ );
53
+ expect(error).toBe(undefined);
54
+}
55
56
describe('Primitive Types', function() {
48
- beforeEach(function() {
49
- require('mock-modules').dumpCache();
50
-
51
- mocks = require('mocks');
52
- ReactTestUtils = require('ReactTestUtils');
53
-
54
- warn = console.warn;
55
- console.warn = mocks.getMockFunction();
56
- });
57
-
58
- afterEach(function() {
59
- console.warn = warn;
60
- });
61
-
57
it("should warn for invalid strings", function() {
63
- typeCheck(Props.string, []);
64
- typeCheck(Props.string, false);
65
- typeCheck(Props.string, 1);
66
- typeCheck(Props.string, {});
67
-
68
- expect(console.warn.mock.calls.length).toBe(4);
69
-
70
- expect(console.warn.mock.calls[0][0]).toBe(
71
- 'Warning: Invalid prop `testProp` of type `array` ' +
72
- 'supplied to `testComponent`, expected `string`.'
58
+ typeCheckFail(
59
+ PropTypes.string,
60
+ [],
61
+ 'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' +
62
+ 'expected `string`.'
63
);
74
- expect(console.warn.mock.calls[1][0]).toBe(
75
- 'Warning: Invalid prop `testProp` of type `boolean` ' +
76
- 'supplied to `testComponent`, expected `string`.'
64
+ typeCheckFail(
65
+ PropTypes.string,
66
+ false,
67
+ 'Invalid prop `testProp` of type `boolean` supplied to ' +
68
+ '`testComponent`, expected `string`.'
69
);
78
- expect(console.warn.mock.calls[2][0]).toBe(
79
- 'Warning: Invalid prop `testProp` of type `number` ' +
80
- 'supplied to `testComponent`, expected `string`.'
70
+ typeCheckFail(
71
+ PropTypes.string,
72
+ 0,
73
+ 'Invalid prop `testProp` of type `number` supplied to ' +
74
+ '`testComponent`, expected `string`.'
75
);
82
- expect(console.warn.mock.calls[3][0]).toBe(
83
- 'Warning: Invalid prop `testProp` of type `object` ' +
84
- 'supplied to `testComponent`, expected `string`.'
76
+ typeCheckFail(
77
+ PropTypes.string,
78
+ {},
79
+ 'Invalid prop `testProp` of type `object` supplied to ' +
80
+ '`testComponent`, expected `string`.'
81
);
82
});
83
88
- it("should not warn for valid values", function() {
89
- typeCheck(Props.array, []);
90
- typeCheck(Props.bool, false);
91
- typeCheck(Props.func, function() {});
92
- typeCheck(Props.number, 0);
93
- typeCheck(Props.object, {});
94
- typeCheck(Props.string, '');
84
+ it('should check for corner case JS types', function() {
85
+ typeCheckFail(
86
+ PropTypes.string,
87
+ new Date(),
88
+ 'Invalid prop `testProp` of type `date` supplied to ' +
89
+ '`testComponent`, expected `string`.'
90
+ );
91
+ typeCheckFail(
92
+ PropTypes.string,
93
+ /please/,
94
+ 'Invalid prop `testProp` of type `regexp` supplied to ' +
95
+ '`testComponent`, expected `string`.'
96
+ );
97
+ });
98
96
- // No warnings should have been logged.
97
- expect(console.warn.mock.calls.length).toBe(0);
99
+ it("should not warn for valid values", function() {
100
+ typeCheckPass(PropTypes.array, []);
101
+ typeCheckPass(PropTypes.bool, false);
102
+ typeCheckPass(PropTypes.func, function() {});
103
+ typeCheckPass(PropTypes.number, 0);
104
+ typeCheckPass(PropTypes.object, {});
105
+ typeCheckPass(PropTypes.string, '');
106
});
107
108
it("should be implicitly optional and not warn without values", function() {
101
- typeCheck(Props.string, null);
102
- typeCheck(Props.string, undefined);
103
-
104
- // No warnings should have been logged.
105
- expect(console.warn.mock.calls.length).toBe(0);
109
+ typeCheckPass(PropTypes.string, null);
110
+ typeCheckPass(PropTypes.string, undefined);
111
});
112
113
it("should warn for missing required values", function() {
109
- typeCheck(Props.string.isRequired, null);
110
- typeCheck(Props.string.isRequired, undefined);
114
+ typeCheckFail(PropTypes.string.isRequired, null, requiredMessage);
115
+ typeCheckFail(PropTypes.string.isRequired, undefined, requiredMessage);
116
+ });
117
+});
118
112
- expect(console.warn.mock.calls.length).toBe(2);
119
+describe('Any type', function() {
120
+ it('should should accept any value', function() {
121
+ typeCheckPass(PropTypes.any, 0);
122
+ typeCheckPass(PropTypes.any, 'str');
123
+ typeCheckPass(PropTypes.any, []);
124
+ });
125
114
- expect(console.warn.mock.calls[0][0]).toBe(
115
- 'Warning: Required prop `testProp` was not specified in ' +
116
- '`testComponent`.'
117
- );
118
- expect(console.warn.mock.calls[1][0]).toBe(
119
- 'Warning: Required prop `testProp` was not specified in ' +
120
- '`testComponent`.'
121
- );
126
+ it("should be implicitly optional and not warn without values", function() {
127
+ typeCheckPass(PropTypes.any, null);
128
+ typeCheckPass(PropTypes.any, undefined);
129
});
130
124
- it("should have a weak version that returns true/false", function() {
125
- expect(typeCheck(Props.string.weak, null)).toEqual(true);
126
- expect(typeCheck(Props.string.weak.isRequired, null)).toEqual(false);
127
- expect(typeCheck(Props.string.isRequired.weak, null)).toEqual(false);
131
+ it("should warn for missing required values", function() {
132
+ typeCheckFail(PropTypes.any.isRequired, null, requiredMessage);
133
+ typeCheckFail(PropTypes.any.isRequired, undefined, requiredMessage);
134
});
135
});
136
131
-describe('Enum Types', function() {
132
- beforeEach(function() {
133
- require('mock-modules').dumpCache();
134
- mocks = require('mocks');
135
- warn = console.warn;
136
- console.warn = mocks.getMockFunction();
137
+describe('ArrayOf Type', function() {
138
+ it('should support the arrayOf propTypes', function() {
139
+ typeCheckPass(PropTypes.arrayOf(PropTypes.number), [1, 2, 3]);
140
+ typeCheckPass(PropTypes.arrayOf(PropTypes.string), ['a', 'b', 'c']);
141
+ typeCheckPass(PropTypes.arrayOf(PropTypes.oneOf(['a', 'b'])), ['a', 'b']);
142
});
143
139
- afterEach(function() {
140
- console.warn = warn;
144
+ it('should support arrayOf with complex types', function() {
145
+ typeCheckPass(
146
+ PropTypes.arrayOf(PropTypes.shape({a: PropTypes.number.isRequired})),
147
+ [{a: 1}, {a: 2}]
148
+ );
149
+
150
+ function Thing() {}
151
+ typeCheckPass(
152
+ PropTypes.arrayOf(PropTypes.instanceOf(Thing)),
153
+ [new Thing(), new Thing()]
154
+ );
155
});
156
143
- it("should warn for invalid strings", function() {
144
- typeCheck(Props.oneOf(['red', 'blue']), true);
145
- typeCheck(Props.oneOf(['red', 'blue']), []);
146
- typeCheck(Props.oneOf(['red', 'blue']), '');
157
+ it('should warn with invalid items in the array', function() {
158
+ typeCheckFail(
159
+ PropTypes.arrayOf(PropTypes.number),
160
+ [1, 2, 'b'],
161
+ 'Invalid prop `2` of type `string` supplied to `testComponent`, ' +
162
+ 'expected `number`.'
163
+ );
164
+ });
165
148
- expect(console.warn.mock.calls.length).toBe(3);
166
+ it('should warn with invalid complex types', function() {
167
+ function Thing() {}
168
+ var name = Thing.name || '<<anonymous>>';
169
150
- expect(console.warn.mock.calls[0][0]).toBe(
151
- 'Warning: Invalid prop `testProp` supplied to ' +
152
- '`testComponent`, expected one of ["blue","red"].'
170
+ typeCheckFail(
171
+ PropTypes.arrayOf(PropTypes.instanceOf(Thing)),
172
+ [new Thing(), 'xyz'],
173
+ 'Invalid prop `1` supplied to `testComponent`, expected instance of `' +
174
+ name + '`.'
175
);
176
+ });
177
155
- expect(console.warn.mock.calls[1][0]).toBe(
156
- 'Warning: Invalid prop `testProp` supplied to ' +
157
- '`testComponent`, expected one of ["blue","red"].'
178
+ it('should warn when passed something other than an array', function() {
179
+ typeCheckFail(
180
+ PropTypes.arrayOf(PropTypes.number),
181
+ {'0': 'maybe-array', length: 1},
182
+ 'Invalid prop `testProp` of type `object` supplied to `testComponent`, ' +
183
+ 'expected an array.'
184
);
159
-
160
- expect(console.warn.mock.calls[2][0]).toBe(
161
- 'Warning: Invalid prop `testProp` supplied to ' +
162
- '`testComponent`, expected one of ["blue","red"].'
185
+ typeCheckFail(
186
+ PropTypes.arrayOf(PropTypes.number),
187
+ 123,
188
+ 'Invalid prop `testProp` of type `number` supplied to `testComponent`, ' +
189
+ 'expected an array.'
190
+ );
191
+ typeCheckFail(
192
+ PropTypes.arrayOf(PropTypes.number),
193
+ 'string',
194
+ 'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' +
195
+ 'expected an array.'
196
);
197
});
198
166
- it("should not warn for valid values", function() {
167
- typeCheck(Props.oneOf(['red', 'blue']), 'red');
168
- typeCheck(Props.oneOf(['red', 'blue']), 'blue');
169
-
170
- // No warnings should have been logged.
171
- expect(console.warn.mock.calls.length).toBe(0);
199
+ it('should not warn when passing an empty array', function() {
200
+ typeCheckPass(PropTypes.arrayOf(PropTypes.number), []);
201
});
202
203
it("should be implicitly optional and not warn without values", function() {
175
- typeCheck(Props.oneOf(['red', 'blue']), null);
176
- typeCheck(Props.oneOf(['red', 'blue']), undefined);
177
-
178
- // No warnings should have been logged.
179
- expect(console.warn.mock.calls.length).toBe(0);
204
+ typeCheckPass(PropTypes.arrayOf(PropTypes.number), null);
205
+ typeCheckPass(PropTypes.arrayOf(PropTypes.number), undefined);
206
});
207
182
- it("should have a weak version that returns true/false", function() {
183
- var checker = Props.oneOf(['red', 'blue']);
184
- expect(typeCheck(checker.weak, null)).toEqual(true);
185
- expect(typeCheck(checker.weak.isRequired, null)).toEqual(false);
186
- expect(typeCheck(checker.isRequired.weak, null)).toEqual(false);
208
+ it("should warn for missing required values", function() {
209
+ typeCheckFail(
210
+ PropTypes.arrayOf(PropTypes.number).isRequired, null, requiredMessage
211
+ );
212
+ typeCheckFail(
213
+ PropTypes.arrayOf(PropTypes.number).isRequired, undefined, requiredMessage
214
+ );
215
});
216
});
217
190
-describe('Shape Types', function() {
218
+describe('Component Type', function() {
219
beforeEach(function() {
220
require('mock-modules').dumpCache();
193
- mocks = require('mocks');
194
- warn = console.warn;
195
- console.warn = mocks.getMockFunction();
221
+ ReactTestUtils = require('ReactTestUtils');
222
+ Component = React.createClass({
223
+ propTypes: {
224
+ label: PropTypes.component.isRequired
225
+ },
226
+
227
+ render: function() {
228
+ return <div>{this.props.label}</div>;
229
+ }
230
+ });
231
+ spyOn(console, 'warn');
232
});
233
198
- afterEach(function() {
199
- console.warn = warn;
234
+ it('should support components', () => {
235
+ typeCheckPass(PropTypes.component, <div />);
236
});
237
202
- it("should warn for non objects", function() {
203
- typeCheck(Props.shape({}), 'some string');
204
- typeCheck(Props.shape({}), ['array']);
238
+ it('should not support multiple components or scalar values', () => {
239
+ var message = 'Invalid prop `testProp` supplied to `testComponent`, ' +
240
+ 'expected a React component.';
241
+ typeCheckFail(PropTypes.component, [<div />, <div />], message);
242
+ typeCheckFail(PropTypes.component, 123, message);
243
+ typeCheckFail(PropTypes.component, 'foo', message);
244
+ typeCheckFail(PropTypes.component, false, message);
245
+ });
246
206
- expect(console.warn.mock.calls.length).toBe(2);
247
+ it('should be able to define a single child as label', () => {
248
+ var instance = <Component label={<div />} />;
249
+ instance = ReactTestUtils.renderIntoDocument(instance);
250
208
- expect(console.warn.mock.calls[0][0]).toBe(
209
- 'Warning: Invalid prop `testProp` of type `string` ' +
210
- 'supplied to `testComponent`, expected `object`.'
211
- );
212
- expect(console.warn.mock.calls[1][0]).toBe(
213
- 'Warning: Invalid prop `testProp` of type `array` ' +
214
- 'supplied to `testComponent`, expected `object`.'
215
- );
251
+ expect(console.warn.argsForCall.length).toBe(0);
252
});
253
218
- it("should not warn for empty values", function() {
219
- typeCheck(Props.shape({}), undefined);
220
- typeCheck(Props.shape({}), null);
221
- typeCheck(Props.shape({}), {});
254
+ it('should warn when passing no label and isRequired is set', () => {
255
+ var instance = <Component />;
256
+ instance = ReactTestUtils.renderIntoDocument(instance);
257
223
- // No warnings should have been logged.
224
- expect(console.warn.mock.calls.length).toBe(0);
258
+ expect(console.warn.argsForCall.length).toBe(1);
259
});
260
227
- it("should warn for empty required value", function() {
228
- typeCheck(Props.shape({}).isRequired, undefined);
229
- typeCheck(Props.shape({}).isRequired, null);
261
+ it("should be implicitly optional and not warn without values", function() {
262
+ typeCheckPass(PropTypes.component, null);
263
+ typeCheckPass(PropTypes.component, undefined);
264
+ });
265
231
- expect(console.warn.mock.calls.length).toBe(2);
266
+ it("should warn for missing required values", function() {
267
+ typeCheckFail(PropTypes.component.isRequired, null, requiredMessage);
268
+ typeCheckFail(PropTypes.component.isRequired, undefined, requiredMessage);
269
+ });
270
+});
271
233
- expect(console.warn.mock.calls[0][0]).toBe(
234
- 'Warning: Required prop `testProp` was not specified in ' +
235
- '`testComponent`.'
272
+describe('Instance Types', function() {
273
+ it("should warn for invalid instances", function() {
274
+ function Person() {}
275
+ var name = Person.name || '<<anonymous>>';
276
+ typeCheckFail(
277
+ PropTypes.instanceOf(Person),
278
+ false,
279
+ 'Invalid prop `testProp` supplied to `testComponent`, expected ' +
280
+ 'instance of `' + name + '`.'
281
);
237
- expect(console.warn.mock.calls[1][0]).toBe(
238
- 'Warning: Required prop `testProp` was not specified in ' +
239
- '`testComponent`.'
282
+ typeCheckFail(
283
+ PropTypes.instanceOf(Person),
284
+ {},
285
+ 'Invalid prop `testProp` supplied to `testComponent`, expected ' +
286
+ 'instance of `' + name + '`.'
287
+ );
288
+ typeCheckFail(
289
+ PropTypes.instanceOf(Person),
290
+ '',
291
+ 'Invalid prop `testProp` supplied to `testComponent`, expected ' +
292
+ 'instance of `' + name + '`.'
293
);
241
-
242
- // Should not warn
243
- typeCheck(Props.shape({}).isRequired, {});
244
- expect(console.warn.mock.calls.length).toBe(2);
294
});
295
247
- it("should not warn for non specified types", function() {
248
- typeCheck(Props.shape({}), {key: 1});
296
+ it("should not warn for valid values", function() {
297
+ function Person() {}
298
+ function Engineer() {}
299
+ Engineer.prototype = new Person();
300
250
- // No warnings should have been logged.
251
- expect(console.warn.mock.calls.length).toBe(0);
301
+ typeCheckPass(PropTypes.instanceOf(Person), new Person());
302
+ typeCheckPass(PropTypes.instanceOf(Person), new Engineer());
303
});
304
254
- it("should not warn for valid types", function() {
255
- typeCheck(Props.shape({
256
- key: Props.number
257
- }), {key: 1});
258
-
259
- // No warnings should have been logged.
260
- expect(console.warn.mock.calls.length).toBe(0);
305
+ it("should be implicitly optional and not warn without values", function() {
306
+ typeCheckPass(PropTypes.instanceOf(String), null);
307
+ typeCheckPass(PropTypes.instanceOf(String), undefined);
308
});
309
263
- it("should warn for required valid types", function() {
264
- typeCheck(Props.shape({
265
- key: Props.number.isRequired
266
- }), {});
267
-
268
- expect(console.warn.mock.calls.length).toBe(1);
269
-
270
- expect(console.warn.mock.calls[0][0]).toBe(
271
- 'Warning: Required prop `key` was not specified in ' +
272
- '`testComponent`.'
310
+ it("should warn for missing required values", function() {
311
+ typeCheckFail(
312
+ PropTypes.instanceOf(String).isRequired, null, requiredMessage
313
);
274
- });
275
-
276
- it("should warn for invalid key types", function() {
277
- typeCheck(Props.shape({
278
- key: Props.number
279
- }), {key: 'abc'});
280
-
281
- expect(console.warn.mock.calls.length).toBe(1);
282
-
283
- expect(console.warn.mock.calls[0][0]).toBe(
284
- 'Warning: Invalid prop `key` of type `string` supplied to ' +
285
- '`testComponent`, expected `number`.'
314
+ typeCheckFail(
315
+ PropTypes.instanceOf(String).isRequired, undefined, requiredMessage
316
);
317
});
318
});
319
290
-describe('ArrayOf Type', function() {
320
+describe('React Component Types', function() {
321
beforeEach(function() {
292
- require('mock-modules').dumpCache();
293
- mocks = require('mocks');
294
- warn = console.warn;
295
- console.warn = mocks.getMockFunction();
322
+ MyComponent = React.createClass({
323
+ render: function() {
324
+ return <div />;
325
+ }
326
+ });
327
});
328
298
- afterEach(function() {
299
- console.warn = warn;
329
+ it('should warn for invalid values', function() {
330
+ typeCheckFail(
331
+ PropTypes.renderable,
332
+ false,
333
+ 'Invalid prop `testProp` supplied to `testComponent`, expected a ' +
334
+ 'renderable prop.'
335
+ );
336
+ typeCheckFail(
337
+ PropTypes.renderable,
338
+ function() {},
339
+ 'Invalid prop `testProp` supplied to `testComponent`, expected a ' +
340
+ 'renderable prop.'
341
+ );
342
+ typeCheckFail(
343
+ PropTypes.renderable,
344
+ {key: function() {}},
345
+ 'Invalid prop `testProp` supplied to `testComponent`, expected a ' +
346
+ 'renderable prop.'
347
+ );
348
});
349
302
- it('should support the arrayOf propType', function() {
303
- typeCheck(Props.arrayOf(Props.number), [1, 2, 3]);
304
- typeCheck(Props.arrayOf(Props.string), ['a', 'b', 'c']);
305
- typeCheck(Props.arrayOf(Props.oneOf(['a', 'b'])), ['a', 'b']);
350
+ it('should not warn for valid values', function() {
351
+ // DOM component
352
+ typeCheckPass(PropTypes.renderable, <div />);
353
+ // Custom component
354
+ typeCheckPass(PropTypes.renderable, <MyComponent />);
355
+ // String
356
+ typeCheckPass(PropTypes.renderable, 'Some string');
357
+ // Empty array
358
+ typeCheckPass(PropTypes.renderable, []);
359
+ // Empty object
360
+ typeCheckPass(PropTypes.renderable, {});
361
+ // Array of renderable things
362
+ typeCheckPass(PropTypes.renderable, [
363
+ 123,
364
+ 'Some string',
365
+ <div />,
366
+ ['Another string', [456], <span />, <MyComponent />],
367
+ <MyComponent />
368
+ ]);
369
307
- // No warnings should have been logged.
308
- expect(console.warn.mock.calls.length).toBe(0);
370
+ // Object of rendereable things
371
+ typeCheckPass(PropTypes.renderable, {
372
+ k0: 123,
373
+ k1: 'Some string',
374
+ k2: <div />,
375
+ k3: {
376
+ k30: <MyComponent />,
377
+ k31: {k310: <a />},
378
+ k32: 'Another string'
379
+ }
380
+ });
381
});
382
311
- it('should support arrayOf with complex types', function() {
312
- typeCheck(Props.arrayOf(Props.shape({
313
- a: Props.number.isRequired
314
- })), [{a: 1}, {a: 2}]);
315
-
316
- function Thing() {}
317
- typeCheck(
318
- Props.arrayOf(Props.instanceOf(Thing)),
319
- [new Thing(), new Thing()]
320
- );
321
-
322
- // No warnings should have been logged.
323
- expect(console.warn.mock.calls.length).toBe(0);
383
+ it('should not warn for null/undefined if not required', function() {
384
+ typeCheckPass(PropTypes.renderable, null);
385
+ typeCheckPass(PropTypes.renderable, undefined);
386
});
387
326
- it('should warn with invalid items in the array', function() {
327
- typeCheck(Props.arrayOf(Props.number), [1, 2, 'b']);
328
-
329
- expect(console.warn.mock.calls.length).toBe(1);
330
-
331
- expect(console.warn.mock.calls[0][0]).toBe(
332
- 'Warning: Invalid prop `2` of type `string` supplied to ' +
333
- '`testComponent`, expected `number`.'
388
+ it('should warn for missing required values', function() {
389
+ typeCheckFail(
390
+ PropTypes.renderable.isRequired,
391
+ null,
392
+ 'Required prop `testProp` was not specified in `testComponent`.'
393
+ );
394
+ typeCheckFail(
395
+ PropTypes.renderable.isRequired,
396
+ undefined,
397
+ 'Required prop `testProp` was not specified in `testComponent`.'
398
);
399
});
400
337
- it('should warn with invalid complex types', function() {
338
- function Thing() {}
339
- var name = Thing.name || '<<anonymous>>';
401
+ it('should accept empty array & object for required props', function() {
402
+ typeCheckPass(PropTypes.renderable.isRequired, []);
403
+ typeCheckPass(PropTypes.renderable.isRequired, {});
404
+ });
405
+});
406
341
- typeCheck(
342
- Props.arrayOf(Props.instanceOf(Thing)),
343
- [new Thing(), 'xyz']
407
+describe('OneOf Types', function() {
408
+ it("should warn for invalid strings", function() {
409
+ typeCheckFail(
410
+ PropTypes.oneOf(['red', 'blue']),
411
+ true,
412
+ 'Invalid prop `testProp` of value `true` supplied to `testComponent`, ' +
413
+ 'expected one of ["red","blue"].'
414
);
345
-
346
- expect(console.warn.mock.calls.length).toBe(1);
347
-
348
- expect(console.warn.mock.calls[0][0]).toBe(
349
- 'Warning: Invalid prop `1` supplied to ' +
350
- '`testComponent`, expected instance of `' + name + '`.'
415
+ typeCheckFail(
416
+ PropTypes.oneOf(['red', 'blue']),
417
+ [],
418
+ 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' +
419
+ 'expected one of ["red","blue"].'
420
+ );
421
+ typeCheckFail(
422
+ PropTypes.oneOf(['red', 'blue']),
423
+ '',
424
+ 'Invalid prop `testProp` of value `` supplied to `testComponent`, ' +
425
+ 'expected one of ["red","blue"].'
426
+ );
427
+ typeCheckFail(
428
+ PropTypes.oneOf([0, 'false']),
429
+ false,
430
+ 'Invalid prop `testProp` of value `false` supplied to `testComponent`, ' +
431
+ 'expected one of [0,"false"].'
432
);
433
});
434
354
- it('should warn when passed something other than an array', function() {
355
- typeCheck(Props.arrayOf(Props.number), {'0': 'maybe-array', length: 1});
356
- typeCheck(Props.arrayOf(Props.number), 123);
357
- typeCheck(Props.arrayOf(Props.number), 'string');
358
-
359
- expect(console.warn.mock.calls.length).toBe(3);
360
-
361
- var message = 'Warning: Invalid prop `testProp` supplied to ' +
362
- '`testComponent`, expected an array.';
435
+ it("should not warn for valid values", function() {
436
+ typeCheckPass(PropTypes.oneOf(['red', 'blue']), 'red');
437
+ typeCheckPass(PropTypes.oneOf(['red', 'blue']), 'blue');
438
+ });
439
364
- expect(console.warn.mock.calls[0][0]).toBe(message);
365
- expect(console.warn.mock.calls[1][0]).toBe(message);
366
- expect(console.warn.mock.calls[2][0]).toBe(message);
440
+ it("should be implicitly optional and not warn without values", function() {
441
+ typeCheckPass(PropTypes.oneOf(['red', 'blue']), null);
442
+ typeCheckPass(PropTypes.oneOf(['red', 'blue']), undefined);
443
});
444
445
+ it("should warn for missing required values", function() {
446
+ typeCheckFail(
447
+ PropTypes.oneOf(['red', 'blue']).isRequired, null, requiredMessage
448
+ );
449
+ typeCheckFail(
450
+ PropTypes.oneOf(['red', 'blue']).isRequired, undefined, requiredMessage
451
+ );
452
+ });
453
});
454
371
-describe('Instance Types', function() {
372
- beforeEach(function() {
373
- require('mock-modules').dumpCache();
374
- mocks = require('mocks');
375
- warn = console.warn;
376
- console.warn = mocks.getMockFunction();
455
+describe('Union Types', function() {
456
+ it('should warn if none of the types are valid', function() {
457
+ typeCheckFail(
458
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
459
+ [],
460
+ 'Invalid prop `testProp` supplied to `testComponent`.'
461
+ );
462
});
463
379
- afterEach(function() {
380
- console.warn = warn;
464
+ it('should not warn if one of the types are valid', function() {
465
+ var checker = PropTypes.oneOfType([
466
+ PropTypes.string,
467
+ PropTypes.number
468
+ ]);
469
+ typeCheckPass(checker, null);
470
+ typeCheckPass(checker, 'foo');
471
+ typeCheckPass(checker, 123);
472
});
473
383
- it("should warn for invalid instances", function() {
384
- function Person() {}
385
- var name = Person.name || '<<anonymous>>';
386
- typeCheck(Props.instanceOf(Person), false);
387
- typeCheck(Props.instanceOf(Person), {});
388
- typeCheck(Props.instanceOf(Person), '');
389
-
390
- expect(console.warn.mock.calls.length).toBe(3);
391
-
392
- expect(console.warn.mock.calls[0][0]).toBe(
393
- 'Warning: Invalid prop `testProp` supplied to ' +
394
- '`testComponent`, expected instance of `' + name + '`.'
474
+ it("should be implicitly optional and not warn without values", function() {
475
+ typeCheckPass(
476
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]), null
477
);
396
-
397
- expect(console.warn.mock.calls[1][0]).toBe(
398
- 'Warning: Invalid prop `testProp` supplied to ' +
399
- '`testComponent`, expected instance of `' + name + '`.'
400
- );
401
-
402
- expect(console.warn.mock.calls[2][0]).toBe(
403
- 'Warning: Invalid prop `testProp` supplied to ' +
404
- '`testComponent`, expected instance of `' + name + '`.'
478
+ typeCheckPass(
479
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]), undefined
480
);
481
});
482
408
- it("should not warn for valid values", function() {
409
- function Person() {}
410
- function Engineer() {}
411
- Engineer.prototype = new Person();
412
-
413
- typeCheck(Props.instanceOf(Person), new Person());
414
- typeCheck(Props.instanceOf(Person), new Engineer());
415
-
416
- // No warnings should have been logged.
417
- expect(console.warn.mock.calls.length).toBe(0);
483
+ it("should warn for missing required values", function() {
484
+ typeCheckFail(
485
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
486
+ null,
487
+ requiredMessage
488
+ );
489
+ typeCheckFail(
490
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
491
+ undefined,
492
+ requiredMessage
493
+ );
494
});
495
});
496
421
-describe('Component Type', function() {
422
- beforeEach(function() {
423
- require('mock-modules').dumpCache();
424
- mocks = require('mocks');
425
- warn = console.warn;
426
- console.warn = mocks.getMockFunction();
497
+describe('Shape Types', function() {
498
+ it("should warn for non objects", function() {
499
+ typeCheckFail(
500
+ PropTypes.shape({}),
501
+ 'some string',
502
+ 'Invalid prop `testProp` of type `string` supplied to `testComponent`, ' +
503
+ 'expected `object`.'
504
+ );
505
+ typeCheckFail(
506
+ PropTypes.shape({}),
507
+ ['array'],
508
+ 'Invalid prop `testProp` of type `array` supplied to `testComponent`, ' +
509
+ 'expected `object`.'
510
+ );
511
});
512
429
- afterEach(function() {
430
- console.warn = warn;
513
+ it("should not warn for empty values", function() {
514
+ typeCheckPass(PropTypes.shape({}), undefined);
515
+ typeCheckPass(PropTypes.shape({}), null);
516
+ typeCheckPass(PropTypes.shape({}), {});
517
});
518
433
- it('should support components', () => {
434
- typeCheck(Props.component, <div />);
435
-
436
- // No warnings should have been logged.
437
- expect(console.warn.mock.calls.length).toBe(0);
519
+ it("should not warn for an empty object", function() {
520
+ typeCheckPass(PropTypes.shape({}).isRequired, {});
521
});
522
440
- it('should not support multiple components or scalar values', () => {
441
- var list = [[<div />, <div />], 123, 'foo', false];
442
- list.forEach((value) => typeCheck(Props.component, value));
443
- expect(console.warn.mock.calls.length).toBe(list.length);
523
+ it("should not warn for non specified types", function() {
524
+ typeCheckPass(PropTypes.shape({}), {key: 1});
525
});
526
446
- var Component = React.createClass({
447
- propTypes: {
448
- label: Props.component.isRequired
449
- },
450
-
451
- render: function() {
452
- return <div>{this.props.label}</div>;
453
- }
527
+ it("should not warn for valid types", function() {
528
+ typeCheckPass(PropTypes.shape({key: PropTypes.number}), {key: 1});
529
});
530
456
- it('should be able to define a single child as label', () => {
457
- var instance = <Component label={<div />} />;
458
- instance = ReactTestUtils.renderIntoDocument(instance);
531
+ it("should warn for required valid types", function() {
532
+ typeCheckFail(
533
+ PropTypes.shape({key: PropTypes.number.isRequired}),
534
+ {},
535
+ 'Required prop `key` was not specified in `testComponent`.'
536
+ );
537
+ });
538
460
- // No warnings should have been logged.
461
- expect(console.warn.mock.calls.length).toBe(0);
539
+ it("should warn for invalid key types", function() {
540
+ typeCheckFail(PropTypes.shape({key: PropTypes.number}),
541
+ {key: 'abc'},
542
+ 'Invalid prop `key` of type `string` supplied to `testComponent`, ' +
543
+ 'expected `number`.'
544
+ );
545
});
546
464
- it('should warn when passing no label and isRequired is set', () => {
465
- var instance = <Component />;
466
- instance = ReactTestUtils.renderIntoDocument(instance);
547
+ it("should be implicitly optional and not warn without values", function() {
548
+ typeCheckPass(
549
+ PropTypes.shape(PropTypes.shape({key: PropTypes.number})), null
550
+ );
551
+ typeCheckPass(
552
+ PropTypes.shape(PropTypes.shape({key: PropTypes.number})), undefined
553
+ );
554
+ });
555
468
- expect(console.warn.mock.calls.length).toBe(1);
556
+ it("should warn for missing required values", function() {
557
+ typeCheckFail(
558
+ PropTypes.shape({key: PropTypes.number}).isRequired,
559
+ null,
560
+ requiredMessage
561
+ );
562
+ typeCheckFail(
563
+ PropTypes.shape({key: PropTypes.number}).isRequired,
564
+ undefined,
565
+ requiredMessage
566
+ );
567
});
568
});
569
472
-describe('Union Types', function() {
570
+describe('Custom validator', function() {
571
beforeEach(function() {
572
require('mock-modules').dumpCache();
475
- mocks = require('mocks');
476
- warn = console.warn;
477
- console.warn = mocks.getMockFunction();
478
- });
479
-
480
- afterEach(function() {
481
- console.warn = warn;
573
+ ReactTestUtils = require('ReactTestUtils');
574
+ spyOn(console, 'warn');
575
});
576
484
- it('should warn if none of the types are valid', function() {
485
- var checker = Props.oneOfType([
486
- Props.string,
487
- Props.number
488
- ]);
489
- typeCheck(checker, []);
490
- checker = Props.oneOfType([
491
- Props.string.isRequired,
492
- Props.number.isRequired
493
- ]);
494
- typeCheck(checker, null);
577
+ it('should have been called with the right params', function() {
578
+ var spy = jasmine.createSpy();
579
+ var Component = React.createClass({
580
+ propTypes: {num: spy},
581
496
- expect(console.warn.mock.calls.length).toBe(2);
582
+ render: function() {
583
+ return <div />;
584
+ }
585
+ });
586
498
- expect(console.warn.mock.calls[0][0]).toBe(
499
- 'Warning: Invalid prop `testProp` ' +
500
- 'supplied to `testComponent`.'
501
- );
587
+ var instance = <Component num={5} />;
588
+ instance = ReactTestUtils.renderIntoDocument(instance);
589
503
- expect(console.warn.mock.calls[1][0]).toBe(
504
- 'Warning: Invalid prop `testProp` ' +
505
- 'supplied to `testComponent`.'
506
- );
590
+ expect(spy.argsForCall.length).toBe(1);
591
+ expect(spy.argsForCall[0][1]).toBe('num');
592
+ expect(spy.argsForCall[0][2]).toBe('Component');
593
});
594
509
- it('should not warn if one of the types are valid', function() {
510
- var checker = Props.oneOfType([
511
- Props.string,
512
- Props.number
513
- ]);
514
- typeCheck(checker, null);
515
- expect(console.warn.mock.calls.length).toBe(0);
516
- typeCheck(checker, 'foo');
517
- expect(console.warn.mock.calls.length).toBe(0);
518
- typeCheck(checker, 123);
519
- expect(console.warn.mock.calls.length).toBe(0);
520
-
521
- checker = Props.oneOfType([
522
- Props.string,
523
- Props.number.isRequired
524
- ]);
525
- typeCheck(checker, null);
526
- expect(console.warn.mock.calls.length).toBe(0);
527
- typeCheck(checker, 'foo');
528
- expect(console.warn.mock.calls.length).toBe(0);
529
- typeCheck(checker, 123);
530
- expect(console.warn.mock.calls.length).toBe(0);
531
- });
595
+ it('should have been called even if the prop is not present', function() {
596
+ var spy = jasmine.createSpy();
597
+ var Component = React.createClass({
598
+ propTypes: {num: spy},
599
533
- describe('React Component Types', function() {
534
- beforeEach(function() {
535
- require('mock-modules').dumpCache();
600
+ render: function() {
601
+ return <div />;
602
+ }
603
});
604
538
- var myFunc = function() {};
539
-
540
- it('should warn for invalid values', function() {
541
- typeCheck(Props.renderable, false);
542
- typeCheck(Props.renderable, myFunc);
543
- typeCheck(Props.renderable, {key: myFunc});
544
-
545
- expect(console.warn.mock.calls.length).toBe(3);
546
-
547
- expect(console.warn.mock.calls[0][0]).toBe(
548
- 'Warning: Invalid prop `testProp` supplied to ' +
549
- '`testComponent`, expected a renderable prop.'
550
- );
551
-
552
- expect(console.warn.mock.calls[1][0]).toBe(
553
- 'Warning: Invalid prop `testProp` supplied to ' +
554
- '`testComponent`, expected a renderable prop.'
555
- );
605
+ var instance = <Component bla={5} />;
606
+ instance = ReactTestUtils.renderIntoDocument(instance);
607
557
- expect(console.warn.mock.calls[2][0]).toBe(
558
- 'Warning: Invalid prop `testProp` supplied to ' +
559
- '`testComponent`, expected a renderable prop.'
560
- );
561
- });
608
+ expect(spy.argsForCall.length).toBe(1);
609
+ });
610
563
- it('should not warn for valid values', function() {
564
- // DOM component
565
- typeCheck(Props.renderable, <div />);
566
- expect(console.warn.mock.calls.length).toBe(0);
567
- // Custom component
568
- typeCheck(Props.renderable, <MyComponent />);
569
- expect(console.warn.mock.calls.length).toBe(0);
570
- // String
571
- typeCheck(Props.renderable, 'Some string');
572
- expect(console.warn.mock.calls.length).toBe(0);
573
- // Empty array
574
- typeCheck(Props.renderable, []);
575
- expect(console.warn.mock.calls.length).toBe(0);
576
- // Empty object
577
- typeCheck(Props.renderable, {});
578
- expect(console.warn.mock.calls.length).toBe(0);
579
- // Array of renderable things
580
-
581
- typeCheck(Props.renderable, [
582
- 123,
583
- 'Some string',
584
- <div />,
585
- ['Another string', [456], <span />, <MyComponent />],
586
- <MyComponent />
587
- ]);
588
- expect(console.warn.mock.calls.length).toBe(0);
589
-
590
- // Object of rendereable things
591
- typeCheck(Props.renderable, {
592
- k0: 123,
593
- k1: 'Some string',
594
- k2: <div />,
595
- k3: {
596
- k30: <MyComponent />,
597
- k31: {k310: <a />},
598
- k32: 'Another string'
611
+ it('should have received the validator\'s return value', function() {
612
+ var spy = jasmine.createSpy().andCallFake(
613
+ function(props, propName, componentName) {
614
+ if (props[propName] !== 5) {
615
+ return new Error('num must be 5!');
616
}
600
- });
601
- expect(console.warn.mock.calls.length).toBe(0);
602
- });
603
-
604
- it('should not warn for null/undefined if not required', function() {
605
- typeCheck(Props.renderable, null);
606
- typeCheck(Props.renderable, undefined);
607
-
608
- // No warnings should have been logged.
609
- expect(console.warn.mock.calls.length).toBe(0);
610
- });
611
-
612
- it('should warn for missing required values', function() {
613
- typeCheck(Props.renderable.isRequired, null);
614
- typeCheck(Props.renderable.isRequired, undefined);
615
-
616
- expect(console.warn.mock.calls.length).toBe(2);
617
+ }
618
+ );
619
+ var Component = React.createClass({
620
+ propTypes: {num: spy},
621
618
- expect(console.warn.mock.calls[0][0]).toBe(
619
- 'Warning: Required prop `testProp` was not specified in ' +
620
- '`testComponent`.'
621
- );
622
- expect(console.warn.mock.calls[1][0]).toBe(
623
- 'Warning: Required prop `testProp` was not specified in ' +
624
- '`testComponent`.'
625
- );
622
+ render: function() {
623
+ return <div />;
624
+ }
625
});
626
628
- it('should accept empty array & object for required props', function() {
629
- typeCheck(Props.renderable.isRequired, []);
630
- typeCheck(Props.renderable.isRequired, {});
631
-
632
- // No warnings should have been logged.
633
- expect(console.warn.mock.calls.length).toBe(0);
634
- });
627
+ var instance = <Component num={6} />;
628
+ instance = ReactTestUtils.renderIntoDocument(instance);
629
+ expect(console.warn.argsForCall.length).toBe(1);
630
+ expect(console.warn.argsForCall[0][0]).toBe('Warning: num must be 5!');
631
});
632
637
- describe('Any type', function() {
638
- it('should should accept any value', function() {
639
- typeCheck(Props.any, 1);
640
- expect(console.warn.mock.calls.length).toBe(0);
641
- typeCheck(Props.any, 'str');
642
- expect(console.warn.mock.calls.length).toBe(0);
643
- typeCheck(Props.any.isRequired, 1);
644
- expect(console.warn.mock.calls.length).toBe(0);
645
- typeCheck(Props.any.isRequired, 'str');
646
- expect(console.warn.mock.calls.length).toBe(0);
647
-
648
- typeCheck(Props.any, null);
649
- expect(console.warn.mock.calls.length).toBe(0);
650
- typeCheck(Props.any, undefined);
651
- expect(console.warn.mock.calls.length).toBe(0);
652
-
653
- typeCheck(Props.any.isRequired, null);
654
- typeCheck(Props.any.isRequired, undefined);
655
-
656
- expect(console.warn.mock.calls.length).toBe(2);
657
- expect(console.warn.mock.calls[0][0]).toBe(
658
- 'Warning: Required prop `testProp` was not specified in ' +
659
- '`testComponent`.'
633
+ it('should not warn if the validator returned anything else than an error',
634
+ function() {
635
+ var spy = jasmine.createSpy().andCallFake(
636
+ function(props, propName, componentName) {
637
+ return 'This message will never reach anyone';
638
+ }
639
);
640
+ var Component = React.createClass({
641
+ propTypes: {num: spy},
642
662
- expect(console.warn.mock.calls[1][0]).toBe(
663
- 'Warning: Required prop `testProp` was not specified in ' +
664
- '`testComponent`.'
665
- );
666
- });
643
+ render: function() {
644
+ return <div />;
645
+ }
646
+ });
647
668
- it('should have a weak version that returns true/false', function() {
669
- expect(typeCheck(Props.any.weak, null)).toEqual(true);
670
- expect(typeCheck(Props.any.weak.isRequired, null)).toEqual(false);
671
- expect(typeCheck(Props.any.isRequired.weak, null)).toEqual(false);
672
- });
673
- });
648
+ var instance = <Component num={5} />;
649
+ instance = ReactTestUtils.renderIntoDocument(instance);
650
+ expect(console.warn.argsForCall.length).toBe(0);
651
+ }
652
+ );
653
});