From: Tim van den Eijnden Date: Wed, 9 Oct 2019 18:03:21 +0000 (+0200) Subject: fix: add warnings (#82) X-Git-Tag: v3.0.0-alpha.0~533 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0177355242ae598d345f8f659847aa70f8343034;p=thirdparty%2Fvuejs%2Fcore.git 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 --- diff --git a/packages/runtime-core/__tests__/apiOptions.spec.ts b/packages/runtime-core/__tests__/apiOptions.spec.ts index 2ba4bc3d57..8c3fbdf24c 100644 --- a/packages/runtime-core/__tests__/apiOptions.spec.ts +++ b/packages/runtime-core/__tests__/apiOptions.spec.ts @@ -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(`
1,1,3
`) }) + + 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() + }) + }) }) diff --git a/packages/runtime-core/__tests__/componentProxy.spec.ts b/packages/runtime-core/__tests__/componentProxy.spec.ts new file mode 100644 index 0000000000..b102889ab0 --- /dev/null +++ b/packages/runtime-core/__tests__/componentProxy.spec.ts @@ -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() + } + }) + }) +}) diff --git a/packages/runtime-core/src/apiOptions.ts b/packages/runtime-core/src/apiOptions.ts index fd3f2ff8d0..fe91d30023 100644 --- a/packages/runtime-core/src/apiOptions.ts +++ b/packages/runtime-core/src/apiOptions.ts @@ -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}"`) } } } diff --git a/packages/runtime-core/src/componentProxy.ts b/packages/runtime-core/src/componentProxy.ts index e02496ed36..b175a8386d 100644 --- a/packages/runtime-core/src/componentProxy.ts +++ b/packages/runtime-core/src/componentProxy.ts @@ -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