]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
test(vapor): errorHandling
authorEvan You <evan@vuejs.org>
Mon, 9 Dec 2024 15:42:23 +0000 (23:42 +0800)
committerEvan You <evan@vuejs.org>
Mon, 9 Dec 2024 15:42:23 +0000 (23:42 +0800)
packages/runtime-core/src/component.ts
packages/runtime-core/src/componentEmits.ts
packages/runtime-vapor/__tests__/errorHandling.spec.ts
packages/runtime-vapor/__tests__/rendererElement.spec.ts [deleted file]
packages/runtime-vapor/src/component.ts

index 58b1bd6428f38a48059616c6158313245040e1c3..72a595258a441701cc10d784a5f5f2a6789b3a71 100644 (file)
@@ -386,6 +386,10 @@ export interface GenericComponentInstance {
    * @internal
    */
   setupState?: Data
+  /**
+   * @internal
+   */
+  setupContext?: any
   /**
    * devtools access to additional info
    * @internal
@@ -594,10 +598,6 @@ export interface ComponentInternalInstance extends GenericComponentInstance {
   ceReload?: (newStyles?: string[]) => void
 
   // the rest are only for stateful components ---------------------------------
-  /**
-   * @internal
-   */
-  setupContext: SetupContext | null
   /**
    * setup related
    * @internal
index c77e8536e6b36f9bdd823cfb5bd1bf08c28ef7d7..20b14ee2f9ec632a8c2005aa797337371627d3fb 100644 (file)
@@ -223,7 +223,7 @@ export function baseEmit(
 
   if (handler) {
     callWithAsyncErrorHandling(
-      handler as Function,
+      handler as Function | Function[],
       instance,
       ErrorCodes.COMPONENT_EVENT_HANDLER,
       args,
index 0cc3de878bea8358679a5392c6681adab2d89f53..d0f5d1c4cb142ccab3bd1f73fdb03bb80ad337de 100644 (file)
@@ -1,12 +1,15 @@
-import type { Component } from '../src/_old/component'
-import { type RefEl, setRef } from '../src/dom/templateRef'
-import { onErrorCaptured, onMounted } from '../src/_old/apiLifecycle'
-import { createComponent } from '../src/_old/apiCreateComponent'
+import {
+  nextTick,
+  onErrorCaptured,
+  onMounted,
+  ref,
+  watch,
+  watchEffect,
+} from '@vue/runtime-dom'
+import { createComponent, setRef, template } from '../src'
 import { makeRender } from './_utils'
-import { template } from '../src/dom/template'
-import { watch, watchEffect } from '../src/_old/apiWatch'
-import { nextTick } from '../src/_old/scheduler'
-import { ref } from '@vue/reactivity'
+import type { VaporComponent } from '../src/component'
+import type { RefEl } from '../src/dom/templateRef'
 
 const define = makeRender()
 
@@ -15,7 +18,7 @@ describe('error handling', () => {
     const err = new Error('foo')
     const fn = vi.fn()
 
-    const Comp: Component = {
+    const Comp: VaporComponent = {
       setup() {
         onErrorCaptured((err, instance, info) => {
           fn(err, info, 'root')
@@ -26,7 +29,7 @@ describe('error handling', () => {
       },
     }
 
-    const Child: Component = {
+    const Child: VaporComponent = {
       name: 'Child',
       setup() {
         onErrorCaptured((err, instance, info) => {
@@ -36,11 +39,12 @@ describe('error handling', () => {
       },
     }
 
-    const GrandChild: Component = {
+    const GrandChild: VaporComponent = {
       setup() {
         onMounted(() => {
           throw err
         })
+        return []
       },
     }
 
@@ -54,7 +58,7 @@ describe('error handling', () => {
     const err = new Error('foo')
     const fn = vi.fn()
 
-    const Comp = {
+    const Comp: VaporComponent = {
       setup() {
         onErrorCaptured((err, instance, info) => {
           fn(err, info, 'root')
@@ -64,7 +68,7 @@ describe('error handling', () => {
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup() {
         onErrorCaptured((err, instance, info) => {
           fn(err, info, 'child')
@@ -74,11 +78,12 @@ describe('error handling', () => {
       },
     }
 
-    const GrandChild = {
+    const GrandChild: VaporComponent = {
       setup() {
         onMounted(() => {
           throw err
         })
+        return []
       },
     }
 
@@ -91,7 +96,7 @@ describe('error handling', () => {
     const err = new Error('foo')
     const fn = vi.fn()
 
-    const Comp = {
+    const Comp: VaporComponent = {
       setup() {
         onErrorCaptured((err, instance, info) => {
           fn(err, info)
@@ -101,11 +106,12 @@ describe('error handling', () => {
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup() {
         onMounted(async () => {
           throw err
         })
+        return []
       },
     }
 
@@ -120,7 +126,7 @@ describe('error handling', () => {
     const err2 = new Error('bar')
     const fn = vi.fn()
 
-    const Comp = {
+    const Comp: VaporComponent = {
       setup() {
         onErrorCaptured((err, instance, info) => {
           fn(err, info)
@@ -130,7 +136,7 @@ describe('error handling', () => {
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup() {
         onErrorCaptured(() => {
           throw err2
@@ -139,11 +145,12 @@ describe('error handling', () => {
       },
     }
 
-    const GrandChild = {
+    const GrandChild: VaporComponent = {
       setup() {
         onMounted(() => {
           throw err
         })
+        return []
       },
     }
 
@@ -175,6 +182,7 @@ describe('error handling', () => {
 
     define(Comp).render()
     expect(fn).toHaveBeenCalledWith(err, 'setup function')
+    expect(`returned non-block value`).toHaveBeenWarned()
   })
 
   test('in render function', () => {
@@ -201,7 +209,7 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'render function')
   })
 
-  test('in function ref', () => {
+  test.todo('in function ref', () => {
     const err = new Error('foo')
     const ref = () => {
       throw err
@@ -234,7 +242,7 @@ describe('error handling', () => {
     const err = new Error('foo')
     const fn = vi.fn()
 
-    const Comp = {
+    const Comp: VaporComponent = {
       setup() {
         onErrorCaptured((err, instance, info) => {
           fn(err, info)
@@ -244,11 +252,12 @@ describe('error handling', () => {
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup() {
         watchEffect(() => {
           throw err
         })
+        return []
       },
     }
 
@@ -270,7 +279,7 @@ describe('error handling', () => {
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup() {
         watch(
           () => {
@@ -278,6 +287,7 @@ describe('error handling', () => {
           },
           () => {},
         )
+        return []
       },
     }
 
@@ -300,7 +310,7 @@ describe('error handling', () => {
     }
 
     const count = ref(0)
-    const Child = {
+    const Child: VaporComponent = {
       setup() {
         watch(
           () => count.value,
@@ -308,6 +318,7 @@ describe('error handling', () => {
             throw err
           },
         )
+        return []
       },
     }
 
@@ -333,7 +344,7 @@ describe('error handling', () => {
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup() {
         watchEffect(onCleanup => {
           count.value
@@ -341,6 +352,7 @@ describe('error handling', () => {
             throw err
           })
         })
+        return []
       },
     }
 
@@ -362,24 +374,25 @@ describe('error handling', () => {
           return false
         })
         return createComponent(Child, {
-          onFoo: () => {
+          onFoo: () => () => {
             throw err
           },
         })
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup(props: any, { emit }: any) {
         emit('foo')
+        return []
       },
     }
 
     define(Comp).render()
-    expect(fn).toHaveBeenCalledWith(err, 'setup function')
+    expect(fn).toHaveBeenCalledWith(err, 'component event handler')
   })
 
-  test.todo('in component event handler via emit (async)', async () => {
+  test('in component event handler via emit (async)', async () => {
     const err = new Error('foo')
     const fn = vi.fn()
 
@@ -390,26 +403,27 @@ describe('error handling', () => {
           return false
         })
         return createComponent(Child, {
-          async onFoo() {
+          onFoo: () => async () => {
             throw err
           },
         })
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       props: ['onFoo'],
       setup(props: any, { emit }: any) {
         emit('foo')
+        return []
       },
     }
 
     define(Comp).render()
     await nextTick()
-    expect(fn).toHaveBeenCalledWith(err, 'setup function')
+    expect(fn).toHaveBeenCalledWith(err, 'component event handler')
   })
 
-  test.todo('in component event handler via emit (async + array)', async () => {
+  test('in component event handler via emit (async + array)', async () => {
     const err = new Error('foo')
     const fn = vi.fn()
 
@@ -419,36 +433,33 @@ describe('error handling', () => {
       return p
     }
 
+    const handlers = [
+      createAsyncHandler(Promise.reject(err)),
+      createAsyncHandler(Promise.resolve(1)),
+    ]
+
     const Comp = {
       setup() {
         onErrorCaptured((err, instance, info) => {
           fn(err, info)
           return false
         })
-        return createComponent(Child, [
-          {
-            onFoo: () => {
-              createAsyncHandler(Promise.reject(err))
-              createAsyncHandler(Promise.resolve(1))
-            },
-          },
-        ])
+        return createComponent(Child, {
+          onFoo: () => handlers,
+        })
       },
     }
 
-    const Child = {
+    const Child: VaporComponent = {
       setup(props: any, { emit }: any) {
         emit('foo')
+        return []
       },
     }
 
     define(Comp).render()
 
-    try {
-      await Promise.all(res)
-    } catch (e: any) {
-      expect(e).toBe(err)
-    }
+    await expect(() => Promise.all(res)).rejects.toThrowError()
     expect(fn).toHaveBeenCalledWith(err, 'component event handler')
   })
 
@@ -523,6 +534,7 @@ describe('error handling', () => {
         watchEffect(async () => {
           throw error4
         })
+        return []
       },
     }).create()
 
diff --git a/packages/runtime-vapor/__tests__/rendererElement.spec.ts b/packages/runtime-vapor/__tests__/rendererElement.spec.ts
deleted file mode 100644 (file)
index c33f2cb..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-import { makeRender } from './_utils'
-import { template } from '../src/dom/template'
-
-const define = makeRender()
-
-describe('renderer: element', () => {
-  it('should create an element', () => {
-    const { html } = define({
-      render() {
-        return template(`<div>`)()
-      },
-    }).render()
-
-    expect(html()).toBe('<div></div>')
-  })
-
-  it('should create an element with props', () => {
-    const { html } = define({
-      render() {
-        return template(`<div id="foo" class="bar">`)()
-      },
-    }).render()
-
-    expect(html()).toBe('<div id="foo" class="bar"></div>')
-  })
-
-  it('should create an element with direct text children', () => {
-    const { html } = define({
-      render() {
-        return template(`<div>foo bar`)()
-      },
-    }).render()
-
-    expect(html()).toBe('<div>foo bar</div>')
-  })
-
-  it('should create an element with direct text children and props', () => {
-    const { html } = define({
-      render() {
-        return template(`<div id="foo">bar`)()
-      },
-    }).render()
-
-    expect(html()).toBe('<div id="foo">bar</div>')
-  })
-
-  it.fails('should update an element tag which is already mounted', () => {
-    const { html } = define({
-      render() {
-        return template(`<div>foo`)()
-      },
-    }).render()
-    expect(html()).toBe('<div>foo</div>')
-
-    define({
-      render() {
-        return template(`<span>foo`)()
-      },
-    }).render()
-    expect(html()).toBe('<span>foo</span>')
-  })
-
-  it.fails('should update element props which is already mounted', () => {
-    const { html } = define({
-      render() {
-        return template(`<div id="baz">foo`)()
-      },
-    }).render()
-    expect(html()).toBe('<div id="baz">foo</div>')
-
-    define({
-      render() {
-        return template(`<div id="baz" class="bar">foo`)()
-      },
-    }).render()
-    expect(html()).toBe('<div id="baz" class="bar">foo</div>')
-  })
-})
index 501a86b8a6770099dc076e8efaf2c0084396cee6..033b2fa6a26fba5c4556d349474d6ac6b792b30e 100644 (file)
@@ -4,12 +4,14 @@ import {
   EffectScope,
   type EmitFn,
   type EmitsOptions,
+  ErrorCodes,
   type GenericAppContext,
   type GenericComponentInstance,
   type LifecycleHook,
   type NormalizedPropsOptions,
   type ObjectEmitsOptions,
   type SuspenseBoundary,
+  callWithErrorHandling,
   currentInstance,
   nextUid,
   popWarningContext,
@@ -125,12 +127,13 @@ export function createComponent(
   }
 
   const setupFn = isFunction(component) ? component : component.setup
+  const setupContext = (instance.setupContext =
+    setupFn && setupFn.length > 1 ? new SetupContext(instance) : null)
   const setupResult = setupFn
-    ? setupFn(
+    ? callWithErrorHandling(setupFn, instance, ErrorCodes.SETUP_FUNCTION, [
         instance.props,
-        // @ts-expect-error
-        setupFn.length > 1 ? new SetupContext(instance) : null,
-      ) || EMPTY_OBJ
+        setupContext,
+      ]) || EMPTY_OBJ
     : EMPTY_OBJ
 
   if (__DEV__ && !isBlock(setupResult)) {
@@ -187,14 +190,19 @@ export function createComponent(
  * dev only
  */
 export function devRender(instance: VaporComponentInstance): void {
-  instance.block = instance.type.render!.call(
-    null,
-    instance.setupState,
-    instance.props,
-    instance.emit,
-    instance.attrs,
-    instance.slots,
-  )
+  instance.block =
+    callWithErrorHandling(
+      instance.type.render!,
+      instance,
+      ErrorCodes.RENDER_FUNCTION,
+      [
+        instance.setupState,
+        instance.props,
+        instance.emit,
+        instance.attrs,
+        instance.slots,
+      ],
+    ) || []
 }
 
 const emptyContext: GenericAppContext = {
@@ -257,6 +265,8 @@ export class VaporComponentInstance implements GenericComponentInstance {
   ec?: LifecycleHook // LifecycleHooks.ERROR_CAPTURED
   sp?: LifecycleHook<() => Promise<unknown>> // LifecycleHooks.SERVER_PREFETCH
 
+  setupContext?: SetupContext | null
+
   // dev only
   setupState?: Record<string, any>
   devtoolsRawSetupState?: any