fix: add warnings (#82)

* fix: add warnings

- invalid watch handler path
- attempting to mutate readonly computed value
- attempt of mutating public property
- attempt of mutating prop

* fix: more descriptive warnings + details

* fix: test apiOptions warnings

* fix: update warning in componentProxy

* fix: update warnings in componentProxy & apiOptions

* fix: update warning in componentProxy

* fix: implemented tests for componentProxy

* fix: remove comment + small refactor
This commit is contained in:
Tim van den Eijnden 2019-10-09 20:03:21 +02:00 committed by Evan You
parent def27239bd
commit 0177355242
4 changed files with 86 additions and 5 deletions

View File

@ -8,7 +8,8 @@ import {
nextTick,
renderToString,
ref,
createComponent
createComponent,
mockWarn
} from '@vue/runtime-test'
describe('api: options', () => {
@ -505,4 +506,37 @@ describe('api: options', () => {
await nextTick()
expect(serializeInner(root)).toBe(`<div>1,1,3</div>`)
})
describe('warnings', () => {
mockWarn()
test('Expected a function as watch handler', () => {
const Comp = {
watch: {
foo: 'notExistingMethod'
},
render() {}
}
const root = nodeOps.createElement('div')
render(h(Comp), root)
expect(
'Invalid watch handler specified by key "notExistingMethod"'
).toHaveBeenWarned()
})
test('Invalid watch option', () => {
const Comp = {
watch: { foo: true },
render() {}
}
const root = nodeOps.createElement('div')
// @ts-ignore
render(h(Comp), root)
expect('Invalid watch option: "foo"').toHaveBeenWarned()
})
})
})

View File

@ -0,0 +1,40 @@
import { createApp, nodeOps, mockWarn } from '@vue/runtime-test'
const createTestInstance = (props?: any) => {
const component = {
render() {}
}
const root = nodeOps.createElement('div')
return createApp().mount(component, root, props)
}
describe('component proxy', () => {
describe('warnings', () => {
mockWarn()
test('Attempting to mutate public property', () => {
const app = createTestInstance()
try {
app.$props = { foo: 'bar' }
} catch {
expect(
'Attempting to mutate public property "$props". ' +
'Properties starting with $ are reserved and readonly.'
).toHaveBeenWarned()
}
})
test('Attempting to mutate prop', () => {
const app = createTestInstance({ foo: 'foo' })
try {
app.foo = 'bar'
} catch {
expect(
'Attempting to mutate prop "foo". Props are readonly.'
).toHaveBeenWarned()
}
})
})
})

View File

@ -267,7 +267,7 @@ export function applyOptions(
if (isFunction(handler)) {
watch(getter, handler as WatchHandler)
} else if (__DEV__) {
// TODO warn invalid watch handler path
warn(`Invalid watch handler specified by key "${raw}"`, handler)
}
} else if (isFunction(raw)) {
watch(getter, raw.bind(ctx))
@ -275,7 +275,7 @@ export function applyOptions(
// TODO 2.x compat
watch(getter, raw.handler.bind(ctx), raw)
} else if (__DEV__) {
// TODO warn invalid watch options
warn(`Invalid watch option: "${key}"`)
}
}
}

View File

@ -4,6 +4,7 @@ import { instanceWatch } from './apiWatch'
import { EMPTY_OBJ, hasOwn, globalsWhitelist } from '@vue/shared'
import { ExtractComputedReturns } from './apiOptions'
import { UnwrapRef } from '@vue/reactivity'
import { warn } from './warning'
// public properties exposed on the proxy, which is used as the render context
// in templates (as `this` in the render option)
@ -91,10 +92,16 @@ export const PublicInstanceProxyHandlers = {
} else if (hasOwn(renderContext, key)) {
renderContext[key] = value
} else if (key[0] === '$' && key.slice(1) in target) {
// TODO warn attempt of mutating public property
__DEV__ &&
warn(
`Attempting to mutate public property "${key}". ` +
`Properties starting with $ are reserved and readonly.`,
target
)
return false
} else if (key in target.props) {
// TODO warn attempt of mutating prop
__DEV__ &&
warn(`Attempting to mutate prop "${key}". Props are readonly.`, target)
return false
} else {
target.user[key] = value