]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-core): emit now returns array of return values from all triggered handlers
authorEvan You <yyx990803@gmail.com>
Mon, 20 Jan 2020 16:24:08 +0000 (11:24 -0500)
committerEvan You <yyx990803@gmail.com>
Mon, 20 Jan 2020 16:24:08 +0000 (11:24 -0500)
close #635

packages/runtime-core/__tests__/component.spec.ts
packages/runtime-core/__tests__/errorHandling.spec.ts
packages/runtime-core/src/component.ts
packages/runtime-core/src/errorHandling.ts

index 929b35770a817451f433675bff5aa323e8f70097..848509af8c75381eb500841a2dc7da76dc881980 100644 (file)
@@ -1,4 +1,11 @@
-import { h, ref, render, nodeOps, nextTick } from '@vue/runtime-test'
+import {
+  h,
+  ref,
+  render,
+  nodeOps,
+  nextTick,
+  defineComponent
+} from '@vue/runtime-test'
 
 describe('renderer: component', () => {
   test.todo('should work')
@@ -52,4 +59,35 @@ describe('renderer: component', () => {
       expect(spy).toHaveBeenCalledTimes(2)
     })
   })
+
+  test('emit', async () => {
+    let noMatchEmitResult: any
+    let singleEmitResult: any
+    let multiEmitResult: any
+
+    const Child = defineComponent({
+      setup(_, { emit }) {
+        noMatchEmitResult = emit('foo')
+        singleEmitResult = emit('bar')
+        multiEmitResult = emit('baz')
+        return () => h('div')
+      }
+    })
+
+    const App = {
+      setup() {
+        return () =>
+          h(Child, {
+            onBar: () => 1,
+            onBaz: [() => Promise.resolve(2), () => Promise.resolve(3)]
+          })
+      }
+    }
+
+    render(h(App), nodeOps.createElement('div'))
+
+    expect(noMatchEmitResult).toMatchObject([])
+    expect(singleEmitResult).toMatchObject([1])
+    expect(await Promise.all(multiEmitResult)).toMatchObject([2, 3])
+  })
 })
index 39aebe43c147b5dc81e2689b999c95cbeb63fbeb..be809d5ff9835d59299cdeac99ab57082ddeb902 100644 (file)
@@ -362,7 +362,7 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'watcher cleanup function')
   })
 
-  test('in component event handler', () => {
+  test('in component event handler via emit', () => {
     const err = new Error('foo')
     const fn = jest.fn()
 
@@ -392,6 +392,78 @@ describe('error handling', () => {
     expect(fn).toHaveBeenCalledWith(err, 'component event handler')
   })
 
+  test('in component event handler via emit (async)', async () => {
+    const err = new Error('foo')
+    const fn = jest.fn()
+
+    const Comp = {
+      setup() {
+        onErrorCaptured((err, instance, info) => {
+          fn(err, info)
+          return true
+        })
+        return () =>
+          h(Child, {
+            async onFoo() {
+              throw err
+            }
+          })
+      }
+    }
+
+    let res: any
+    const Child = {
+      setup(props: any, { emit }: any) {
+        res = emit('foo')
+        return () => null
+      }
+    }
+
+    render(h(Comp), nodeOps.createElement('div'))
+
+    try {
+      await Promise.all(res)
+    } catch (e) {
+      expect(e).toBe(err)
+    }
+    expect(fn).toHaveBeenCalledWith(err, 'component event handler')
+  })
+
+  test('in component event handler via emit (async + array)', async () => {
+    const err = new Error('foo')
+    const fn = jest.fn()
+
+    const Comp = {
+      setup() {
+        onErrorCaptured((err, instance, info) => {
+          fn(err, info)
+          return true
+        })
+        return () =>
+          h(Child, {
+            onFoo: [() => Promise.reject(err), () => Promise.resolve(1)]
+          })
+      }
+    }
+
+    let res: any
+    const Child = {
+      setup(props: any, { emit }: any) {
+        res = emit('foo')
+        return () => null
+      }
+    }
+
+    render(h(Comp), nodeOps.createElement('div'))
+
+    try {
+      await Promise.all(res)
+    } catch (e) {
+      expect(e).toBe(err)
+    }
+    expect(fn).toHaveBeenCalledWith(err, 'component event handler')
+  })
+
   it('should warn unhandled', () => {
     const onError = jest.spyOn(console, 'error')
     onError.mockImplementation(() => {})
index 3eafc78df8cec0eb8085e595a94027ba5476db9b..9bfaf50b0015d5f42d8c1e947845fe9d06aac0b5 100644 (file)
@@ -24,7 +24,8 @@ import {
   isObject,
   NO,
   makeMap,
-  isPromise
+  isPromise,
+  isArray
 } from '@vue/shared'
 import { SuspenseBoundary } from './components/Suspense'
 import { CompilerOptions } from '@vue/compiler-core'
@@ -70,7 +71,7 @@ export const enum LifecycleHooks {
   ERROR_CAPTURED = 'ec'
 }
 
-export type Emit = (event: string, ...args: unknown[]) => void
+export type Emit = (event: string, ...args: unknown[]) => any[]
 
 export interface SetupContext {
   attrs: Data
@@ -218,16 +219,19 @@ export function defineComponentInstance(
     rtc: null,
     ec: null,
 
-    emit: (event, ...args) => {
+    emit: (event, ...args): any[] => {
       const props = instance.vnode.props || EMPTY_OBJ
       const handler = props[`on${event}`] || props[`on${capitalize(event)}`]
       if (handler) {
-        callWithAsyncErrorHandling(
+        const res = callWithAsyncErrorHandling(
           handler,
           instance,
           ErrorCodes.COMPONENT_EVENT_HANDLER,
           args
         )
+        return isArray(res) ? res : [res]
+      } else {
+        return []
       }
     }
   }
index 5d5592fba77ef9db24d29dbcd0561c4672f52052..0d965c63c0c12d588d8a34515f5b6648f4883170 100644 (file)
@@ -74,7 +74,7 @@ export function callWithAsyncErrorHandling(
   instance: ComponentInternalInstance | null,
   type: ErrorTypes,
   args?: unknown[]
-) {
+): any[] {
   if (isFunction(fn)) {
     const res = callWithErrorHandling(fn, instance, type, args)
     if (res != null && !res._isVue && isPromise(res)) {
@@ -85,9 +85,11 @@ export function callWithAsyncErrorHandling(
     return res
   }
 
+  const values = []
   for (let i = 0; i < fn.length; i++) {
-    callWithAsyncErrorHandling(fn[i], instance, type, args)
+    values.push(callWithAsyncErrorHandling(fn[i], instance, type, args))
   }
+  return values
 }
 
 export function handleError(