| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- // NOTE: this test cases are based on paclages/runtime-core/__tests__/componentEmits.spec.ts
- // Note: emits and listener fallthrough is tested in
- // ./rendererAttrsFallthrough.spec.ts.
- import { nextTick, onBeforeUnmount, unmountComponent } from '../src'
- import { isEmitListener } from '../src/componentEmits'
- import { makeRender } from './_utils'
- const define = makeRender<any>()
- describe('component: emit', () => {
- test('trigger handlers', () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('foo')
- emit('bar')
- emit('!baz')
- },
- })
- 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
- },
- })
- expect(onfoo).not.toHaveBeenCalled()
- expect(onBar).toHaveBeenCalled()
- expect(onBaz).toHaveBeenCalled()
- })
- test('trigger camelCase handler', () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('test-event')
- },
- })
- const fooSpy = vi.fn()
- render({
- get onTestEvent() {
- return fooSpy
- },
- })
- expect(fooSpy).toHaveBeenCalled()
- })
- test('trigger kebab-case handler', () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('test-event')
- },
- })
- const fooSpy = vi.fn()
- render({
- get ['onTest-event']() {
- return fooSpy
- },
- })
- expect(fooSpy).toHaveBeenCalledTimes(1)
- })
- // #3527
- test.todo('trigger mixed case handlers', () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('test-event')
- emit('testEvent')
- },
- })
- const fooSpy = vi.fn()
- const barSpy = vi.fn()
- render(
- // TODO: impl `toHandlers`
- {
- get ['onTest-Event']() {
- return fooSpy
- },
- get onTestEvent() {
- return barSpy
- },
- },
- )
- expect(fooSpy).toHaveBeenCalledTimes(1)
- expect(barSpy).toHaveBeenCalledTimes(1)
- })
- // 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) {
- emit('update:fooProp')
- emit('update:barProp')
- },
- })
- const fooSpy = vi.fn()
- const barSpy = vi.fn()
- render({
- get ['onUpdate:fooProp']() {
- return fooSpy
- },
- get ['onUpdate:bar-prop']() {
- return barSpy
- },
- })
- expect(fooSpy).toHaveBeenCalled()
- expect(barSpy).toHaveBeenCalled()
- })
- test('should trigger array of listeners', async () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('foo', 1)
- },
- })
- const fn1 = vi.fn()
- const fn2 = vi.fn()
- render({
- get onFoo() {
- return [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.todo('warning for undeclared event (object)', () => {
- // TODO: warning
- })
- test('should not warn if has equivalent onXXX prop', () => {
- define({
- props: ['onFoo'],
- emits: [],
- render() {},
- setup(_: any, { emit }: any) {
- emit('foo')
- },
- }).render()
- expect(
- `Component emitted event "foo" but it is neither declared`,
- ).not.toHaveBeenWarned()
- })
- test.todo('validator warning', () => {
- // TODO: warning validator
- })
- // NOTE: not supported mixins
- // test.todo('merging from mixins', () => {})
- // #2651
- // test.todo(
- // 'should not attach normalized object when mixins do not contain emits',
- // () => {},
- // )
- test('.once', () => {
- const { render } = define({
- render() {},
- emits: {
- foo: null,
- bar: null,
- },
- setup(_: any, { emit }: any) {
- emit('foo')
- emit('foo')
- emit('bar')
- emit('bar')
- },
- })
- const fn = vi.fn()
- const barFn = vi.fn()
- render({
- get onFooOnce() {
- return fn
- },
- get onBarOnce() {
- return 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) {
- emit('foo')
- emit('foo')
- },
- })
- const onFoo = vi.fn()
- const onFooOnce = vi.fn()
- render({
- get onFoo() {
- return onFoo
- },
- get onFooOnce() {
- return 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) {
- emit('update:modelValue', '1')
- emit('update:foo', '2')
- },
- })
- const fn1 = vi.fn()
- const fn2 = vi.fn()
- render({
- get modelValue() {
- return null
- },
- get modelModifiers() {
- return { number: true }
- },
- get ['onUpdate:modelValue']() {
- return fn1
- },
- get foo() {
- return null
- },
- get fooModifiers() {
- return { number: true }
- },
- get ['onUpdate:foo']() {
- return fn2
- },
- })
- expect(fn1).toHaveBeenCalledTimes(1)
- expect(fn1).toHaveBeenCalledWith(1)
- expect(fn2).toHaveBeenCalledTimes(1)
- expect(fn2).toHaveBeenCalledWith(2)
- })
- test('.trim modifier should work with v-model on component', () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('update:modelValue', ' one ')
- emit('update:foo', ' two ')
- },
- })
- const fn1 = vi.fn()
- const fn2 = vi.fn()
- render({
- get modelValue() {
- return null
- },
- get modelModifiers() {
- return { trim: true }
- },
- get ['onUpdate:modelValue']() {
- return fn1
- },
- get foo() {
- return null
- },
- get fooModifiers() {
- return { trim: true }
- },
- get 'onUpdate:foo'() {
- return fn2
- },
- })
- expect(fn1).toHaveBeenCalledTimes(1)
- expect(fn1).toHaveBeenCalledWith('one')
- expect(fn2).toHaveBeenCalledTimes(1)
- expect(fn2).toHaveBeenCalledWith('two')
- })
- test('.trim and .number modifiers should work with v-model on component', () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('update:modelValue', ' +01.2 ')
- emit('update:foo', ' 1 ')
- },
- })
- const fn1 = vi.fn()
- const fn2 = vi.fn()
- render({
- get modelValue() {
- return null
- },
- get modelModifiers() {
- return { trim: true, number: true }
- },
- get ['onUpdate:modelValue']() {
- return fn1
- },
- get foo() {
- return null
- },
- get fooModifiers() {
- return { trim: true, number: true }
- },
- get ['onUpdate:foo']() {
- return fn2
- },
- })
- expect(fn1).toHaveBeenCalledTimes(1)
- expect(fn1).toHaveBeenCalledWith(1.2)
- expect(fn2).toHaveBeenCalledTimes(1)
- expect(fn2).toHaveBeenCalledWith(1)
- })
- test('only trim string parameter when work with v-model on component', () => {
- const { render } = define({
- render() {},
- setup(_: any, { emit }: any) {
- emit('update:modelValue', ' foo ', { bar: ' bar ' })
- },
- })
- const fn = vi.fn()
- render({
- get modelValue() {
- return null
- },
- get modelModifiers() {
- return { trim: true }
- },
- get ['onUpdate:modelValue']() {
- return fn
- },
- })
- expect(fn).toHaveBeenCalledTimes(1)
- expect(fn).toHaveBeenCalledWith('foo', { bar: ' bar ' })
- })
- test('isEmitListener', () => {
- const options = {
- get click() {
- return null
- },
- get 'test-event'() {
- return null
- },
- get fooBar() {
- return null
- },
- get FooBaz() {
- return null
- },
- }
- expect(isEmitListener(options, 'onClick')).toBe(true)
- expect(isEmitListener(options, 'onclick')).toBe(false)
- expect(isEmitListener(options, 'onBlick')).toBe(false)
- // .once listeners
- expect(isEmitListener(options, 'onClickOnce')).toBe(true)
- expect(isEmitListener(options, 'onclickOnce')).toBe(false)
- // kebab-case option
- expect(isEmitListener(options, 'onTestEvent')).toBe(true)
- // camelCase option
- expect(isEmitListener(options, 'onFooBar')).toBe(true)
- // PascalCase option
- expect(isEmitListener(options, 'onFooBaz')).toBe(true)
- })
- test('does not emit after unmount', async () => {
- const fn = vi.fn()
- const { instance } = define({
- emits: ['closing'],
- setup(_: any, { emit }: any) {
- onBeforeUnmount(async () => {
- await nextTick()
- emit('closing', true)
- })
- },
- render() {},
- }).render({
- get onClosing() {
- return fn
- },
- })
- await nextTick()
- unmountComponent(instance)
- await nextTick()
- expect(fn).not.toHaveBeenCalled()
- })
- // NOTE: not supported mixins
- // test.todo('merge string array emits', async () => {})
- // test.todo('merge object emits', async () => {})
- })
|