Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
212 changes: 103 additions & 109 deletions packages/runtime-vapor/__tests__/componentEmits.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,81 +3,91 @@
// Note: emits and listener fallthrough is tested in
// ./rendererAttrsFallthrough.spec.ts.

import { nextTick, onBeforeUnmount } from '../src'
import { toHandlers } from '@vue/runtime-core'
import {
createComponent,
defineComponent,
nextTick,
onBeforeUnmount,
} from '../src'
import { isEmitListener } from '../src/componentEmits'
import { makeRender } from './_utils'

const define = makeRender<any>()
const define = makeRender()

describe.todo('component: emit', () => {
describe('component: emit', () => {
test('trigger handlers', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('foo')
emit('bar')
emit('!baz')
},
})
const onfoo = vi.fn()
const onFoo = vi.fn()
const onBar = vi.fn()
const onBaz = vi.fn()
render({
get onfoo() {
return onfoo
},
get onBar() {
return onBar
},
get ['on!baz']() {
return onBaz
onfoo: () => onFoo,
onBar: () => onBar,
['on!baz']: () => onBaz,
})

expect(onFoo).not.toHaveBeenCalled()
expect(onBar).toHaveBeenCalled()
expect(onBaz).toHaveBeenCalled()
})

test('trigger dynamic emits', () => {
const { render } = define({
setup(_, { emit }) {
emit('foo')
emit('bar')
emit('!baz')
},
})
const onFoo = vi.fn()
const onBar = vi.fn()
const onBaz = vi.fn()
render(() => ({
onfoo: onFoo,
onBar,
['on!baz']: onBaz,
}))

expect(onfoo).not.toHaveBeenCalled()
expect(onFoo).not.toHaveBeenCalled()
expect(onBar).toHaveBeenCalled()
expect(onBaz).toHaveBeenCalled()
})

test('trigger camelCase handler', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('test-event')
},
})

const fooSpy = vi.fn()
render({
get onTestEvent() {
return fooSpy
},
})
render({ onTestEvent: () => fooSpy })
expect(fooSpy).toHaveBeenCalled()
})

test('trigger kebab-case handler', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('test-event')
},
})

const fooSpy = vi.fn()
render({
get ['onTest-event']() {
return fooSpy
},
})
render({ ['onTest-event']: () => fooSpy })
expect(fooSpy).toHaveBeenCalledTimes(1)
})

// #3527
test.todo('trigger mixed case handlers', () => {
test('trigger mixed case handlers', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('test-event')
emit('testEvent')
},
Expand All @@ -86,15 +96,10 @@ describe.todo('component: emit', () => {
const fooSpy = vi.fn()
const barSpy = vi.fn()
render(
// TODO: impl `toHandlers`
{
get ['onTest-Event']() {
return fooSpy
},
get onTestEvent() {
return barSpy
},
},
toHandlers({
'test-event': () => fooSpy,
testEvent: () => barSpy,
}),
)
expect(fooSpy).toHaveBeenCalledTimes(1)
expect(barSpy).toHaveBeenCalledTimes(1)
Expand All @@ -103,8 +108,7 @@ describe.todo('component: emit', () => {
// for v-model:foo-bar usage in DOM templates
test('trigger hyphenated events for update:xxx events', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('update:fooProp')
emit('update:barProp')
},
Expand All @@ -113,12 +117,8 @@ describe.todo('component: emit', () => {
const fooSpy = vi.fn()
const barSpy = vi.fn()
render({
get ['onUpdate:fooProp']() {
return fooSpy
},
get ['onUpdate:bar-prop']() {
return barSpy
},
['onUpdate:fooProp']: () => fooSpy,
['onUpdate:bar-prop']: () => barSpy,
})

expect(fooSpy).toHaveBeenCalled()
Expand All @@ -127,38 +127,57 @@ describe.todo('component: emit', () => {

test('should trigger array of listeners', async () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('foo', 1)
},
})

const fn1 = vi.fn()
const fn2 = vi.fn()

render({
onFoo: () => [fn1, fn2],
})
render({ onFoo: () => [fn1, fn2] })
expect(fn1).toHaveBeenCalledTimes(1)
expect(fn1).toHaveBeenCalledWith(1)
expect(fn2).toHaveBeenCalledTimes(1)
expect(fn2).toHaveBeenCalledWith(1)
})

test.todo('warning for undeclared event (array)', () => {
// TODO: warning
test('warning for undeclared event (array)', () => {
const { render } = define({
emits: ['foo'],

setup(_, { emit }) {
emit('bar')
},
})
render()
expect(
`Component emitted event "bar" but it is neither declared`,
).toHaveBeenWarned()
})

test.todo('warning for undeclared event (object)', () => {
// TODO: warning
test('warning for undeclared event (object)', () => {
const { render } = define({
emits: {
foo: null,
},

setup(_, { emit }) {
emit('bar')
},
})
render()
expect(
`Component emitted event "bar" but it is neither declared`,
).toHaveBeenWarned()
})

test('should not warn if has equivalent onXXX prop', () => {
define({
props: ['onFoo'],
emits: [],
render() {},
setup(_: any, { emit }: any) {

setup(_, { emit }) {
emit('foo')
},
}).render()
Expand All @@ -182,12 +201,11 @@ describe.todo('component: emit', () => {

test('.once', () => {
const { render } = define({
render() {},
emits: {
foo: null,
bar: null,
},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('foo')
emit('foo')
emit('bar')
Expand All @@ -197,71 +215,49 @@ describe.todo('component: emit', () => {
const fn = vi.fn()
const barFn = vi.fn()
render({
get onFooOnce() {
return fn
},
get onBarOnce() {
return barFn
},
onFooOnce: () => fn,
onBarOnce: () => barFn,
})
expect(fn).toHaveBeenCalledTimes(1)
expect(barFn).toHaveBeenCalledTimes(1)
})

test('.once with normal listener of the same name', () => {
const { render } = define({
render() {},
emits: {
foo: null,
},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('foo')
emit('foo')
},
})
const onFoo = vi.fn()
const onFooOnce = vi.fn()
render({
get onFoo() {
return onFoo
},
get onFooOnce() {
return onFooOnce
},
onFoo: () => onFoo,
onFooOnce: () => onFooOnce,
})
expect(onFoo).toHaveBeenCalledTimes(2)
expect(onFooOnce).toHaveBeenCalledTimes(1)
})

test('.number modifier should work with v-model on component', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('update:modelValue', '1')
emit('update:foo', '2')
},
})
const fn1 = vi.fn()
const fn2 = vi.fn()
render({
modelValue() {
return null
},
modelModifiers() {
return { number: true }
},
['onUpdate:modelValue']() {
return fn1
},
foo() {
return null
},
fooModifiers() {
return { number: true }
},
['onUpdate:foo']() {
return fn2
},
modelValue: () => null,
modelModifiers: () => ({ number: true }),
['onUpdate:modelValue']: () => fn1,
foo: () => null,
fooModifiers: () => ({ number: true }),
['onUpdate:foo']: () => fn2,
})
expect(fn1).toHaveBeenCalledTimes(1)
expect(fn1).toHaveBeenCalledWith(1)
Expand All @@ -271,8 +267,7 @@ describe.todo('component: emit', () => {

test('.trim modifier should work with v-model on component', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('update:modelValue', ' one ')
emit('update:foo', ' two ')
},
Expand Down Expand Up @@ -307,8 +302,7 @@ describe.todo('component: emit', () => {

test('.trim and .number modifiers should work with v-model on component', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('update:modelValue', ' +01.2 ')
emit('update:foo', ' 1 ')
},
Expand Down Expand Up @@ -343,8 +337,7 @@ describe.todo('component: emit', () => {

test('only trim string parameter when work with v-model on component', () => {
const { render } = define({
render() {},
setup(_: any, { emit }: any) {
setup(_, { emit }) {
emit('update:modelValue', ' foo ', { bar: ' bar ' })
},
})
Expand Down Expand Up @@ -395,20 +388,21 @@ describe.todo('component: emit', () => {

test('does not emit after unmount', async () => {
const fn = vi.fn()
const { app } = define({

const Foo = defineComponent({
emits: ['closing'],
setup(_: any, { emit }: any) {
setup(_, { emit }) {
onBeforeUnmount(async () => {
await nextTick()
emit('closing', true)
})
},
render() {},
}).render({
get onClosing() {
return fn
},
})

const { app } = define(() =>
createComponent(Foo, { onClosing: () => fn }),
).render()

await nextTick()
app.unmount()
await nextTick()
Expand Down
Loading