Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Anthony Fu <anthonyfu117@hotmail.com>
- Loading branch information
Showing
5 changed files
with
233 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,84 +1,230 @@ | ||
import type { Fn } from '@vueuse/shared' | ||
import type { SpyInstance } from 'vitest' | ||
import { noop } from '@vueuse/shared' | ||
import { isVue2 } from 'vue-demi' | ||
import type { Ref } from 'vue' | ||
import { effectScope, nextTick, ref } from 'vue' | ||
import { useEventListener } from '.' | ||
|
||
describe('useEventListener', () => { | ||
let target: Ref<HTMLDivElement | null> | ||
let listener: () => any | ||
const options = { capture: true } | ||
let stop: Fn | ||
let target: HTMLDivElement | ||
let removeSpy: SpyInstance | ||
let addSpy: SpyInstance | ||
|
||
beforeEach(() => { | ||
target = ref(document.createElement('div')) | ||
listener = vi.fn() | ||
target = document.createElement('div') | ||
removeSpy = vitest.spyOn(target, 'removeEventListener') | ||
addSpy = vitest.spyOn(target, 'addEventListener') | ||
}) | ||
|
||
it('should not listen when target is invalid', async () => { | ||
useEventListener(target, 'click', listener) | ||
const el = target.value | ||
target.value = null | ||
await nextTick() | ||
el?.dispatchEvent(new MouseEvent('click')) | ||
await nextTick() | ||
it('should be defined', () => { | ||
expect(useEventListener).toBeDefined() | ||
}) | ||
|
||
describe('given both none array', () => { | ||
const listener = vitest.fn() | ||
const event = 'click' | ||
|
||
beforeEach(() => { | ||
listener.mockReset() | ||
stop = useEventListener(target, event, listener, options) | ||
}) | ||
|
||
it('should add listener', () => { | ||
expect(addSpy).toBeCalledTimes(1) | ||
}) | ||
|
||
it('should trigger listener', () => { | ||
expect(listener).not.toBeCalled() | ||
target.dispatchEvent(new MouseEvent(event)) | ||
expect(listener).toBeCalledTimes(1) | ||
}) | ||
|
||
it('should remove listener', () => { | ||
expect(removeSpy).not.toBeCalled() | ||
|
||
expect(listener).toHaveBeenCalledTimes(isVue2 ? 1 : 0) | ||
expect(useEventListener(null, 'click', listener)).toBe(noop) | ||
stop() | ||
|
||
expect(removeSpy).toBeCalledTimes(1) | ||
expect(removeSpy).toBeCalledWith(event, listener, options) | ||
}) | ||
}) | ||
|
||
function getTargetName(useTarget: boolean) { | ||
return useTarget ? 'element' : 'window' | ||
} | ||
describe('given array of events but single listener', () => { | ||
const listener = vitest.fn() | ||
const events = ['click', 'scroll', 'blur', 'resize'] | ||
|
||
function getArgs(useTarget: boolean) { | ||
return (useTarget ? [target, 'click', listener] : ['click', listener]) | ||
} | ||
beforeEach(() => { | ||
listener.mockReset() | ||
stop = useEventListener(target, events, listener, options) | ||
}) | ||
|
||
function trigger(useTarget: boolean) { | ||
(useTarget ? target.value : window)!.dispatchEvent(new MouseEvent('click')) | ||
} | ||
it('should add listener for all events', () => { | ||
events.forEach(event => expect(addSpy).toBeCalledWith(event, listener, options)) | ||
}) | ||
|
||
function testTarget(useTarget: boolean) { | ||
it(`should ${getTargetName(useTarget)} listen event`, async () => { | ||
// @ts-expect-error mock different args | ||
const stop = useEventListener(...getArgs(useTarget)) | ||
it('should trigger listener with all events', () => { | ||
expect(listener).not.toBeCalled() | ||
events.forEach((event, index) => { | ||
target.dispatchEvent(new Event(event)) | ||
expect(listener).toBeCalledTimes(index + 1) | ||
}) | ||
}) | ||
|
||
trigger(useTarget) | ||
it('should remove listener with all events', () => { | ||
expect(removeSpy).not.toBeCalled() | ||
|
||
await nextTick() | ||
stop() | ||
|
||
expect(listener).toHaveBeenCalledTimes(1) | ||
expect(removeSpy).toBeCalledTimes(events.length) | ||
events.forEach(event => expect(removeSpy).toBeCalledWith(event, listener, options)) | ||
}) | ||
}) | ||
|
||
it(`should ${getTargetName(useTarget)} manually stop listening event`, async () => { | ||
// @ts-expect-error mock different args | ||
const stop = useEventListener(...getArgs(useTarget)) | ||
describe('given single event but array of listeners', () => { | ||
const listeners = [vitest.fn(), vitest.fn(), vitest.fn()] | ||
const event = 'click' | ||
|
||
beforeEach(() => { | ||
listeners.forEach(listener => listener.mockReset()) | ||
stop = useEventListener(target, event, listeners, options) | ||
}) | ||
|
||
it('should add all listeners', () => { | ||
listeners.forEach(listener => expect(addSpy).toBeCalledWith(event, listener, options)) | ||
}) | ||
|
||
it('should call all listeners with single click event', () => { | ||
listeners.forEach(listener => expect(listener).not.toBeCalled()) | ||
|
||
target.dispatchEvent(new Event(event)) | ||
|
||
listeners.forEach(listener => expect(listener).toBeCalledTimes(1)) | ||
}) | ||
|
||
it('should remove listeners', () => { | ||
expect(removeSpy).not.toBeCalled() | ||
|
||
stop() | ||
|
||
trigger(useTarget) | ||
expect(removeSpy).toBeCalledTimes(listeners.length) | ||
listeners.forEach(listener => expect(removeSpy).toBeCalledWith(event, listener, options)) | ||
}) | ||
}) | ||
|
||
await nextTick() | ||
describe('given both array of events and listeners', () => { | ||
const listeners = [vitest.fn(), vitest.fn(), vitest.fn()] | ||
const events = ['click', 'scroll', 'blur', 'resize', 'custom-event'] | ||
|
||
expect(listener).toHaveBeenCalledTimes(0) | ||
beforeEach(() => { | ||
listeners.forEach(listener => listener.mockReset()) | ||
stop = useEventListener(target, events, listeners, options) | ||
}) | ||
|
||
it(`should ${getTargetName(useTarget)} auto stop listening event`, async () => { | ||
const scope = effectScope() | ||
await scope.run(async () => { | ||
// @ts-expect-error mock different args | ||
useEventListener(...getArgs(useTarget)) | ||
it('should add all listeners for all events', () => { | ||
listeners.forEach((listener) => { | ||
events.forEach((event) => { | ||
expect(addSpy).toBeCalledWith(event, listener, options) | ||
}) | ||
}) | ||
}) | ||
|
||
it('should call all listeners with all events', () => { | ||
events.forEach((event, index) => { | ||
target.dispatchEvent(new Event(event)) | ||
listeners.forEach(listener => expect(listener).toBeCalledTimes(index + 1)) | ||
}) | ||
}) | ||
|
||
it('should remove all listeners with all events', () => { | ||
stop() | ||
|
||
listeners.forEach((listener) => { | ||
events.forEach((event) => { | ||
expect(removeSpy).toBeCalledWith(event, listener, options) | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
||
await scope.stop() | ||
describe('multiple events', () => { | ||
let target: Ref<HTMLDivElement | null> | ||
let listener: () => any | ||
|
||
trigger(useTarget) | ||
beforeEach(() => { | ||
target = ref(document.createElement('div')) | ||
listener = vi.fn() | ||
}) | ||
|
||
it('should not listen when target is invalid', async () => { | ||
useEventListener(target, 'click', listener) | ||
const el = target.value | ||
target.value = null | ||
await nextTick() | ||
el?.dispatchEvent(new MouseEvent('click')) | ||
await nextTick() | ||
|
||
expect(listener).toHaveBeenCalledTimes(isVue2 ? 1 : 0) | ||
expect(useEventListener(null, 'click', listener)).toBe(noop) | ||
}) | ||
} | ||
|
||
testTarget(false) | ||
testTarget(true) | ||
function getTargetName(useTarget: boolean) { | ||
return useTarget ? 'element' : 'window' | ||
} | ||
|
||
function getArgs(useTarget: boolean) { | ||
return (useTarget ? [target, 'click', listener] : ['click', listener]) | ||
} | ||
|
||
function trigger(useTarget: boolean) { | ||
(useTarget ? target.value : window)!.dispatchEvent(new MouseEvent('click')) | ||
} | ||
|
||
function testTarget(useTarget: boolean) { | ||
it(`should ${getTargetName(useTarget)} listen event`, async () => { | ||
// @ts-expect-error mock different args | ||
const stop = useEventListener(...getArgs(useTarget)) | ||
|
||
trigger(useTarget) | ||
|
||
await nextTick() | ||
|
||
expect(listener).toHaveBeenCalledTimes(1) | ||
}) | ||
|
||
it(`should ${getTargetName(useTarget)} manually stop listening event`, async () => { | ||
// @ts-expect-error mock different args | ||
const stop = useEventListener(...getArgs(useTarget)) | ||
|
||
stop() | ||
|
||
trigger(useTarget) | ||
|
||
await nextTick() | ||
|
||
expect(listener).toHaveBeenCalledTimes(0) | ||
}) | ||
|
||
it(`should ${getTargetName(useTarget)} auto stop listening event`, async () => { | ||
const scope = effectScope() | ||
await scope.run(async () => { | ||
// @ts-expect-error mock different args | ||
useEventListener(...getArgs(useTarget)) | ||
}) | ||
|
||
await scope.stop() | ||
|
||
trigger(useTarget) | ||
|
||
await nextTick() | ||
|
||
expect(listener).toHaveBeenCalledTimes(isVue2 ? 1 : 0) | ||
}) | ||
} | ||
|
||
testTarget(false) | ||
testTarget(true) | ||
}) | ||
}) |
Oops, something went wrong.