]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix: add warnings (#82)
authorTim van den Eijnden <TimvdEijnden@users.noreply.github.com>
Wed, 9 Oct 2019 18:03:21 +0000 (20:03 +0200)
committerEvan You <yyx990803@gmail.com>
Wed, 9 Oct 2019 18:03:21 +0000 (14:03 -0400)
* 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

packages/runtime-core/__tests__/apiOptions.spec.ts
packages/runtime-core/__tests__/componentProxy.spec.ts [new file with mode: 0644]
packages/runtime-core/src/apiOptions.ts
packages/runtime-core/src/componentProxy.ts

index 2ba4bc3d57e0e60339df26b8905412ff2d59713b..8c3fbdf24c759cb50ccc7b1ec4594e3a1c9f040e 100644 (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()
+    })
+  })
 })
diff --git a/packages/runtime-core/__tests__/componentProxy.spec.ts b/packages/runtime-core/__tests__/componentProxy.spec.ts
new file mode 100644 (file)
index 0000000..b102889
--- /dev/null
@@ -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()
+      }
+    })
+  })
+})
index fd3f2ff8d0b0c9c702635317d0b98e74e26ae951..fe91d300233a6a3f22b522d4a9eed5afe907eb94 100644 (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}"`)
       }
     }
   }
index e02496ed36b421e45e84138271c496f3931e2ec6..b175a8386d41e85f678bb49d4a05b92dbdba98c2 100644 (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