Skip to content

Commit d3efa2a

Browse files
committed
tests for prop assertions
1 parent 9510eac commit d3efa2a

File tree

5 files changed

+151
-7
lines changed

5 files changed

+151
-7
lines changed

src/compiler/compile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ function compileProps (el, attrs, propDescriptors) {
481481
props.push(prop)
482482
} else if (assertions && assertions.required) {
483483
_.warn(
484-
'Required prop missing: ' + name
484+
'Missing required prop: ' + name
485485
)
486486
}
487487
}

src/directives/prop.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
var _ = require('../util')
12
var Watcher = require('../watcher')
23
var bindingModes = require('../config')._propBindingModes
34

@@ -24,7 +25,9 @@ module.exports = {
2425
if (!locked) {
2526
locked = true
2627
// all props have been initialized already
27-
child[childKey] = val
28+
if (_.assertProp(prop, val)) {
29+
child[childKey] = val
30+
}
2831
locked = false
2932
}
3033
},
@@ -34,7 +37,10 @@ module.exports = {
3437
// set the child initial value first, before setting
3538
// up the child watcher to avoid triggering it
3639
// immediately.
37-
child.$set(childKey, this.parentWatcher.value)
40+
var value = this.parentWatcher.value
41+
if (_.assertProp(prop, value)) {
42+
child.$set(childKey, value)
43+
}
3844

3945
// only setup two-way binding if this is not a one-way
4046
// binding.

src/util/misc.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ exports.assertProp = function (prop, value) {
4343
_.warn(
4444
'Invalid prop: type check failed for ' +
4545
prop.path + '="' + prop.raw + '".' +
46-
(expectedType ? ' Expected ' + expectedType + '.' : '')
46+
' Expected ' + formatType(expectedType) +
47+
', got ' + formatValue(value) + '.'
4748
)
4849
return false
4950
}
@@ -60,6 +61,16 @@ exports.assertProp = function (prop, value) {
6061
return true
6162
}
6263

64+
function formatType (val) {
65+
return val
66+
? val.charAt(0).toUpperCase() + val.slice(1)
67+
: 'custom type'
68+
}
69+
70+
function formatValue (val) {
71+
return Object.prototype.toString.call(val).slice(8, -1)
72+
}
73+
6374
/**
6475
* Check if an element is a component, if yes return its
6576
* component id.

test/unit/lib/util.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,15 @@ var scope = typeof window === 'undefined'
33
: window
44

55
scope.hasWarned = function (_, msg) {
6-
var args = _.warn.calls.argsFor(0)
7-
return !!(args[0] && args[0].indexOf(msg) > -1)
8-
}
6+
var count = _.warn.calls.count()
7+
while (count--) {
8+
var args = _.warn.calls.argsFor(count)
9+
if (args.some(containsMsg)) {
10+
return true
11+
}
12+
}
13+
14+
function containsMsg (arg) {
15+
return arg.indexOf(msg) > -1
16+
}
17+
}

test/unit/specs/directives/prop_spec.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,5 +226,123 @@ if (_.inBrowser) {
226226
expect(el.innerHTML).toBe('<p>AAA</p><p>DDD</p>')
227227
})
228228

229+
describe('assertions', function () {
230+
231+
function makeInstance (value, type, validator) {
232+
return new Vue({
233+
el: document.createElement('div'),
234+
template: '<test prop="{{val}}"></test>',
235+
data: {
236+
val: value
237+
},
238+
components: {
239+
test: {
240+
props: [
241+
{
242+
name: 'prop',
243+
type: type,
244+
validator: validator
245+
}
246+
]
247+
}
248+
}
249+
})
250+
}
251+
252+
it('string', function () {
253+
makeInstance('hello', String)
254+
expect(_.warn).not.toHaveBeenCalled()
255+
makeInstance(123, String)
256+
expect(hasWarned(_, 'Expected String')).toBe(true)
257+
})
258+
259+
it('number', function () {
260+
makeInstance(123, Number)
261+
expect(_.warn).not.toHaveBeenCalled()
262+
makeInstance('123', Number)
263+
expect(hasWarned(_, 'Expected Number')).toBe(true)
264+
})
265+
266+
it('boolean', function () {
267+
makeInstance(true, Boolean)
268+
expect(_.warn).not.toHaveBeenCalled()
269+
makeInstance('123', Boolean)
270+
expect(hasWarned(_, 'Expected Boolean')).toBe(true)
271+
})
272+
273+
it('function', function () {
274+
makeInstance(function () {}, Function)
275+
expect(_.warn).not.toHaveBeenCalled()
276+
makeInstance(123, Function)
277+
expect(hasWarned(_, 'Expected Function')).toBe(true)
278+
})
279+
280+
it('object', function () {
281+
makeInstance({}, Object)
282+
expect(_.warn).not.toHaveBeenCalled()
283+
makeInstance([], Object)
284+
expect(hasWarned(_, 'Expected Object')).toBe(true)
285+
})
286+
287+
it('array', function () {
288+
makeInstance([], Array)
289+
expect(_.warn).not.toHaveBeenCalled()
290+
makeInstance({}, Array)
291+
expect(hasWarned(_, 'Expected Array')).toBe(true)
292+
})
293+
294+
it('custom constructor', function () {
295+
function Class () {}
296+
makeInstance(new Class(), Class)
297+
expect(_.warn).not.toHaveBeenCalled()
298+
makeInstance({}, Class)
299+
expect(hasWarned(_, 'Expected custom type')).toBe(true)
300+
})
301+
302+
it('custom validator', function () {
303+
makeInstance(123, null, function (v) {
304+
return v === 123
305+
})
306+
expect(_.warn).not.toHaveBeenCalled()
307+
makeInstance(123, null, function (v) {
308+
return v === 234
309+
})
310+
expect(hasWarned(_, 'custom validator check failed')).toBe(true)
311+
})
312+
313+
it('type check + custom validator', function () {
314+
makeInstance(123, Number, function (v) {
315+
return v === 123
316+
})
317+
expect(_.warn).not.toHaveBeenCalled()
318+
makeInstance(123, Number, function (v) {
319+
return v === 234
320+
})
321+
expect(hasWarned(_, 'custom validator check failed')).toBe(true)
322+
makeInstance(123, String, function (v) {
323+
return v === 123
324+
})
325+
expect(hasWarned(_, 'Expected String')).toBe(true)
326+
})
327+
328+
it('required', function () {
329+
var vm = new Vue({
330+
el: document.createElement('div'),
331+
template: '<test></test>',
332+
components: {
333+
test: {
334+
props: [
335+
{
336+
name: 'prop',
337+
required: true
338+
}
339+
]
340+
}
341+
}
342+
})
343+
expect(hasWarned(_, 'Missing required prop')).toBe(true)
344+
})
345+
346+
})
229347
})
230348
}

0 commit comments

Comments
 (0)