@rokkit/helpers 1.0.0-next.101

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/dist/src/index.d.ts +1 -0
  3. package/dist/src/matchers/action.d.ts +27 -0
  4. package/dist/src/matchers/array.d.ts +10 -0
  5. package/dist/src/matchers/dataset.d.ts +10 -0
  6. package/dist/src/matchers/event.d.ts +10 -0
  7. package/dist/src/matchers/index.d.ts +4 -0
  8. package/dist/src/matchers/internal.d.ts +1 -0
  9. package/dist/src/mocks/animate.d.ts +1 -0
  10. package/dist/src/mocks/element.d.ts +56 -0
  11. package/dist/src/mocks/index.d.ts +2 -0
  12. package/dist/src/mocks/match-media.d.ts +30 -0
  13. package/dist/src/mocks/resize-observer.d.ts +9 -0
  14. package/dist/src/simulators/touch.d.ts +16 -0
  15. package/dist/vitest.config.d.ts +1 -0
  16. package/package.json +35 -0
  17. package/spec/index.spec.js +14 -0
  18. package/spec/matchers/action.spec.js +102 -0
  19. package/spec/matchers/array.spec.js +33 -0
  20. package/spec/matchers/dataset.spec.js +32 -0
  21. package/spec/matchers/event.spec.js +35 -0
  22. package/spec/matchers/index.spec.js +15 -0
  23. package/spec/mocks/animate.spec.js +24 -0
  24. package/spec/mocks/element.spec.js +136 -0
  25. package/spec/mocks/index.spec.js +16 -0
  26. package/spec/mocks/match-media.spec.js +75 -0
  27. package/spec/mocks/resize-observer.spec.js +52 -0
  28. package/spec/simulator.spec.js +96 -0
  29. package/src/components/MockItem.svelte +5 -0
  30. package/src/index.js +1 -0
  31. package/src/matchers/action.js +88 -0
  32. package/src/matchers/array.js +16 -0
  33. package/src/matchers/dataset.js +18 -0
  34. package/src/matchers/event.js +18 -0
  35. package/src/matchers/index.js +4 -0
  36. package/src/matchers/internal.js +9 -0
  37. package/src/mocks/animate.js +26 -0
  38. package/src/mocks/element.js +83 -0
  39. package/src/mocks/index.js +7 -0
  40. package/src/mocks/match-media.js +90 -0
  41. package/src/mocks/resize-observer.js +25 -0
  42. package/src/simulators/touch.js +67 -0
  43. package/tsconfig.build.json +11 -0
  44. package/vitest.config.js +2 -0
@@ -0,0 +1,75 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { matchMediaMock, updateMedia } from '../../src/mocks/match-media'
3
+
4
+ describe('match-media', () => {
5
+ describe('matchMediaMock', () => {
6
+ it('should return a mock media query', () => {
7
+ const query = matchMediaMock('(min-width: 100px)')
8
+ expect(query.media).toBe('(min-width: 100px)')
9
+ expect(query.matches).toBe(true)
10
+ expect(query.addListener).toEqual(expect.any(Function))
11
+ expect(query.removeListener).toEqual(expect.any(Function))
12
+ })
13
+ })
14
+
15
+ describe('updateMedia', () => {
16
+ it('should mock min-width media query', () => {
17
+ const query = matchMediaMock('(min-width: 100px)')
18
+
19
+ window.innerWidth = 50
20
+ updateMedia()
21
+ expect(query.matches).toBe(false)
22
+
23
+ window.innerWidth = 200
24
+ updateMedia()
25
+ expect(query.matches).toBe(true)
26
+ })
27
+
28
+ it('should mock max-width media query', () => {
29
+ const query = matchMediaMock('(max-width: 100px)')
30
+
31
+ window.innerWidth = 500
32
+ updateMedia()
33
+ expect(query.matches).toBe(false)
34
+
35
+ window.innerWidth = 100
36
+ updateMedia()
37
+ expect(query.matches).toBe(true)
38
+ })
39
+
40
+ it('should mock the media queries', () => {
41
+ const query = matchMediaMock('(min-width: 100px) and (max-width: 200px)')
42
+
43
+ window.innerWidth = 50
44
+ updateMedia()
45
+ expect(query.matches).toBe(false)
46
+
47
+ window.innerWidth = 500
48
+ updateMedia()
49
+ expect(query.matches).toBe(false)
50
+
51
+ window.innerWidth = 150
52
+ updateMedia()
53
+ expect(query.matches).toBe(true)
54
+ })
55
+
56
+ it('should mock multiple media queries', () => {
57
+ const query1 = matchMediaMock('(min-width: 100px)')
58
+ const query2 = matchMediaMock('(max-width: 100px)')
59
+
60
+ window.innerWidth = 50
61
+ updateMedia()
62
+ expect(query1.matches).toBe(false)
63
+ expect(query2.matches).toBe(true)
64
+
65
+ window.innerWidth = 500
66
+ updateMedia()
67
+ expect(query1.matches).toBe(true)
68
+ expect(query2.matches).toBe(false)
69
+
70
+ // should remove the listener
71
+ query1.removeListener()
72
+ query2.removeListener()
73
+ })
74
+ })
75
+ })
@@ -0,0 +1,52 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest'
2
+ import { ResizeObserver } from '../../src/mocks/resize-observer'
3
+
4
+ global.ResizeObserver = ResizeObserver
5
+
6
+ describe('ResizeObserver', () => {
7
+ let resizeObserver
8
+ let callback
9
+ let element
10
+
11
+ beforeEach(() => {
12
+ callback = vi.fn()
13
+ resizeObserver = new ResizeObserver(callback)
14
+ element = document.createElement('div')
15
+ document.body.appendChild(element)
16
+ })
17
+
18
+ it('should observe an element and trigger the callback with initial size', () => {
19
+ resizeObserver.observe(element)
20
+
21
+ expect(callback).toHaveBeenCalledTimes(1)
22
+ const [entry] = callback.mock.calls[0][0]
23
+ expect(entry.target).toBe(element)
24
+ expect(entry.contentRect).toEqual(element.getBoundingClientRect())
25
+ })
26
+
27
+ it('should unobserve an element', () => {
28
+ resizeObserver.observe(element)
29
+ resizeObserver.unobserve(element)
30
+
31
+ expect(resizeObserver.elements.has(element)).toBe(false)
32
+ })
33
+
34
+ it('should disconnect and clear all observed elements', () => {
35
+ resizeObserver.observe(element)
36
+ resizeObserver.disconnect()
37
+
38
+ expect(resizeObserver.elements.size).toBe(0)
39
+ })
40
+
41
+ it('should simulate a resize event', () => {
42
+ resizeObserver.observe(element)
43
+ const newSize = { width: 500, height: 300, top: 0, left: 0, right: 500, bottom: 300 }
44
+
45
+ resizeObserver.simulateResize(element, newSize)
46
+
47
+ expect(callback).toHaveBeenCalledTimes(2)
48
+ const [entry] = callback.mock.calls[1][0]
49
+ expect(entry.target).toBe(element)
50
+ expect(entry.contentRect).toEqual(newSize)
51
+ })
52
+ })
@@ -0,0 +1,96 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
2
+ import { toHaveBeenDispatchedWith } from '../src/matchers/event'
3
+ import {
4
+ simulateMouseEvent,
5
+ simulateTouchEvent,
6
+ simulateMouseSwipe,
7
+ simulateTouchSwipe
8
+ } from '../src/simulators/touch'
9
+
10
+ expect.extend({ toHaveBeenDispatchedWith })
11
+ describe('events', () => {
12
+ const node = document.createElement('div')
13
+
14
+ describe('simulateMouseEvent', () => {
15
+ it('should simulate a mouse event', () => {
16
+ const event = simulateMouseEvent(10, 10)
17
+ expect(event).toEqual({
18
+ clientX: 10,
19
+ clientY: 10,
20
+ stopPropagation: expect.any(Function),
21
+ preventDefault: expect.any(Function)
22
+ })
23
+ })
24
+ })
25
+ describe('simulateTouchEvent', () => {
26
+ it('should simulate a touch event', () => {
27
+ const event = simulateTouchEvent(10, 10)
28
+
29
+ expect(event).toEqual({
30
+ touches: [{ clientX: 10, clientY: 10 }],
31
+ stopPropagation: expect.any(Function),
32
+ preventDefault: expect.any(Function)
33
+ })
34
+ })
35
+ })
36
+
37
+ describe('simulateMouseSwipe', () => {
38
+ const events = ['mousedown', 'mouseup']
39
+
40
+ const handlers = {}
41
+
42
+ beforeEach(() => {
43
+ vi.useFakeTimers()
44
+ events.forEach((event) => {
45
+ handlers[event] = vi.fn()
46
+ node.addEventListener(event, handlers[event])
47
+ })
48
+ })
49
+ afterEach(() => {
50
+ vi.useRealTimers()
51
+ events.forEach((event) => node.removeEventListener(event, handlers[event]))
52
+ })
53
+ it('should simulate a mouse swipe', () => {
54
+ simulateMouseSwipe(node, { x: 10, y: 20 })
55
+ expect(handlers.mousedown).toHaveBeenCalledOnce()
56
+ expect(handlers.mouseup).toHaveBeenCalledOnce()
57
+
58
+ let mouseEvent = handlers.mousedown.mock.calls[0][0]
59
+ expect(mouseEvent.clientX).toEqual(0)
60
+ expect(mouseEvent.clientY).toEqual(0)
61
+
62
+ mouseEvent = handlers.mouseup.mock.calls[0][0]
63
+ expect(mouseEvent.clientX).toEqual(10)
64
+ expect(mouseEvent.clientY).toEqual(20)
65
+ })
66
+ })
67
+ describe('simulateTouchSwipe', () => {
68
+ const events = ['touchstart', 'touchend']
69
+
70
+ const handlers = {}
71
+
72
+ beforeEach(() => {
73
+ vi.useFakeTimers()
74
+ events.forEach((event) => {
75
+ handlers[event] = vi.fn()
76
+ node.addEventListener(event, handlers[event])
77
+ })
78
+ })
79
+ afterEach(() => {
80
+ events.forEach((event) => node.removeEventListener(event, handlers[event]))
81
+ vi.useRealTimers()
82
+ })
83
+
84
+ it('should simulate a touch swipe', () => {
85
+ simulateTouchSwipe(node, { x: 100, y: 50 })
86
+ expect(handlers.touchstart).toHaveBeenCalledOnce()
87
+ expect(handlers.touchend).toHaveBeenCalledOnce()
88
+ expect(handlers.touchstart.mock.calls[0][0].touches).toEqual([
89
+ { clientX: 0, clientY: 0, identifier: 0, target: node }
90
+ ])
91
+ expect(handlers.touchend.mock.calls[0][0].changedTouches).toEqual([
92
+ { clientX: 100, clientY: 50, identifier: 0, target: node }
93
+ ])
94
+ })
95
+ })
96
+ })
@@ -0,0 +1,5 @@
1
+ <script>
2
+ let { value } = $props()
3
+ </script>
4
+
5
+ {value}
package/src/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './simulators/touch'
@@ -0,0 +1,88 @@
1
+ import { getMockNode } from '../mocks'
2
+
3
+ /**
4
+ * Generates a message based on the validation state
5
+ * @param {Array<string>} events
6
+ * @param {Array<string>} handlerKeys
7
+ * @param {boolean} pass
8
+ * @param {boolean} validEvents
9
+ * @returns {string}
10
+ */
11
+ function getMessage(events, handlerKeys, pass, validEvents) {
12
+ let message = ''
13
+ const names = events.join(', ')
14
+ const keys = handlerKeys.join(', ')
15
+
16
+ if (pass) {
17
+ message = `Expected other handlers besides [${names}] to be called, but none were`
18
+ } else if (validEvents) {
19
+ message = `Expected only [${names}] to be called once and the other handlers to not be called`
20
+ } else {
21
+ message = `Expected events from [${keys}] but got unexpected events [${names}]`
22
+ }
23
+
24
+ return message
25
+ }
26
+
27
+ /**
28
+ * Checks if all the given events are registered by the action and cleaned up on destroy.
29
+ *
30
+ * @param {*} action
31
+ * @param {Object<string,any>} options
32
+ * @param {string|Array<string>} events
33
+ * @returns
34
+ */
35
+ export function toUseHandlersFor(action, options, events) {
36
+ if (typeof events === 'string') {
37
+ events = [events]
38
+ }
39
+ const mock = getMockNode(events)
40
+ const actionHandler = action(mock.node, options)
41
+ let result = events.map((event) => ({
42
+ event,
43
+ created: mock.listeners[event] === 1
44
+ }))
45
+
46
+ actionHandler.destroy()
47
+ result = result
48
+ .map((r) => ({ ...r, destroyed: mock.listeners[r.event] === 0 }))
49
+ .map((r) => ({ ...r, pass: r.created && r.destroyed }))
50
+
51
+ const pass = result.every((r) => r.pass)
52
+ const message = [
53
+ 'Expected action',
54
+ pass ? 'not to' : 'to',
55
+ 'manage handlers for',
56
+ `[${events.join(',')}]`,
57
+ 'but result is',
58
+ JSON.stringify(result)
59
+ ].join(' ')
60
+
61
+ return { message: () => message, pass }
62
+ }
63
+
64
+ /**
65
+ * Verifies that only the specified events are triggered. Expects an object of spies with key as event names.
66
+ *
67
+ * @param {Object<string,any>} handler : Object with keys as event names and values as spies
68
+ * @param {string|Array<string>} events : An event name or an array of event names
69
+ * @returns
70
+ */
71
+ export function toOnlyTrigger(handler, events) {
72
+ events = Array.isArray(events) ? events : [events]
73
+ const handlerKeys = Object.keys(handler)
74
+
75
+ const isEventValid = (event) => handlerKeys.includes(event)
76
+ const isCallCountCorrect = (key) => {
77
+ const callCount = handler[key].mock.calls.length
78
+ return events.includes(key) ? callCount === 1 : callCount === 0
79
+ }
80
+
81
+ const validEvents = events.every(isEventValid)
82
+ const pass = validEvents && handlerKeys.every(isCallCountCorrect)
83
+
84
+ return {
85
+ message: () => getMessage(events, handlerKeys, pass, validEvents),
86
+ pass
87
+ }
88
+ }
@@ -0,0 +1,16 @@
1
+ import { getMessage } from './internal'
2
+
3
+ /**
4
+ * Verify that an array contains all of the expected values
5
+ *
6
+ * @param {Array} received - the array to inspect
7
+ * @param {Array} expected - the values to check for
8
+ */
9
+ export function toIncludeAll(received, expected) {
10
+ const pass = expected.every((v) => received.includes(v))
11
+
12
+ return {
13
+ message: () => getMessage(received, expected, pass, 'include all of'),
14
+ pass
15
+ }
16
+ }
@@ -0,0 +1,18 @@
1
+ import { equals } from 'ramda'
2
+ import { getMessage } from './internal'
3
+
4
+ /**
5
+ * Check if the element has valid data attributes
6
+ *
7
+ * @param {HTMLElement} received - HTML element to be checked
8
+ * @param {Object} expected - data to be compared
9
+ */
10
+ export function toHaveValidData(received, expected) {
11
+ const actual = { ...received.dataset }
12
+ const pass = equals(actual, expected)
13
+
14
+ return {
15
+ message: () => getMessage(actual, expected, pass),
16
+ pass
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ import { equals } from '@vitest/expect'
2
+ import { getMessage } from './internal'
3
+
4
+ /**
5
+ * Verify that a spy event has been dispatched with data in event detail
6
+ *
7
+ * @param {Function} spy - the event handler to inspect
8
+ * @param {Object} data - data expected inthe event detail
9
+ */
10
+ export function toHaveBeenDispatchedWith(spy, data) {
11
+ const detail = spy.mock.lastCall[0].detail
12
+ const pass = equals(detail, data)
13
+
14
+ return {
15
+ message: () => getMessage(detail, data, pass),
16
+ pass
17
+ }
18
+ }
@@ -0,0 +1,4 @@
1
+ export * from './array'
2
+ export * from './action'
3
+ export * from './dataset'
4
+ export * from './event'
@@ -0,0 +1,9 @@
1
+ export function getMessage(actual, expected, pass, condition = 'deeply equal') {
2
+ return [
3
+ 'expected',
4
+ JSON.stringify(actual),
5
+ pass ? 'to not' : 'to',
6
+ condition,
7
+ JSON.stringify(expected)
8
+ ].join(' ')
9
+ }
@@ -0,0 +1,26 @@
1
+ import { vi } from 'vitest'
2
+
3
+ // Mock the animate function
4
+ if (!global.Element.prototype.animate) {
5
+ global.Element.prototype.animate = vi.fn().mockImplementation(() => {
6
+ return {
7
+ play: vi.fn(),
8
+ pause: vi.fn(),
9
+ finish: vi.fn(),
10
+ cancel: vi.fn(),
11
+ reverse: vi.fn(),
12
+ persist: vi.fn(),
13
+ onfinish: vi.fn(),
14
+ oncancel: vi.fn(),
15
+ currentTime: 0,
16
+ startTime: 0,
17
+ playbackRate: 1,
18
+ playState: 'idle',
19
+ finished: Promise.resolve(),
20
+ effect: {
21
+ getTiming: vi.fn(),
22
+ getComputedTiming: vi.fn()
23
+ }
24
+ }
25
+ })
26
+ }
@@ -0,0 +1,83 @@
1
+ import { vi } from 'vitest'
2
+
3
+ /**
4
+ * Creates an array of elements with the specified size
5
+ *
6
+ * @param {number} count
7
+ * @param {number} size
8
+ * @param {string} [prop='offsetHeight']
9
+ * @returns {Array<Object<string, number>>}
10
+ */
11
+ export function elementsWithSize(count, size, prop = 'offsetHeight') {
12
+ return Array.from({ length: count }, () => ({
13
+ [prop]: size
14
+ }))
15
+ }
16
+
17
+ /**
18
+ * Creates an array of elements with mixed sizes
19
+ *
20
+ * @param {Array<{count: number, size: number}>} data
21
+ * @param {string} prop
22
+ * @returns {Array<Object<string, number>>}
23
+ */
24
+ export function mixedSizeElements(data, prop) {
25
+ return data.reduce(
26
+ (elements, { count, size }) => [...elements, ...elementsWithSize(count, size, prop)],
27
+ []
28
+ )
29
+ }
30
+
31
+ /**
32
+ * Creates a mock node with functions to add and remove event handlers
33
+ *
34
+ * @param {Array<string} events
35
+ * @returns {{node: HTMLElement, listeners: Object<string, integer>}}
36
+ */
37
+ export function getMockNode(events) {
38
+ const listeners = events.reduce((acc, event) => ({ ...acc, [event]: 0 }), {})
39
+
40
+ const node = {
41
+ dispatchEvent: vi.fn(),
42
+ scrollTo: vi.fn(),
43
+ querySelectorAll: vi.fn().mockImplementation((selector) => {
44
+ return [document.createElement(selector)]
45
+ }),
46
+ querySelector: vi.fn().mockImplementation((selector) => {
47
+ return document.createElement(selector)
48
+ }),
49
+ addEventListener: vi.fn().mockImplementation((name) => ++listeners[name]),
50
+ removeEventListener: vi.fn().mockImplementation((name) => --listeners[name])
51
+ }
52
+ return { node, listeners }
53
+ }
54
+
55
+ /**
56
+ * @typedef {Object} NestedItem
57
+ * @property {string} name
58
+ * @property {string} [dataPath]
59
+ * @property {string} [id]
60
+ * @property {Array<NestedItem>} [children]
61
+ */
62
+
63
+ /**
64
+ * Creates a nested HTML element structure using the provided data
65
+ *
66
+ * @param {NestedItem} item
67
+ * @returns {HTMLElement}
68
+ */
69
+ export function createNestedElement(item) {
70
+ const { name, dataPath, id, children } = item
71
+ const element = document.createElement(name)
72
+
73
+ if (dataPath) element.dataset.path = dataPath
74
+ if (id) element.id = id
75
+
76
+ element.scrollIntoView = vi.fn()
77
+
78
+ if (Array.isArray(children)) {
79
+ children.forEach((child) => element.appendChild(createNestedElement(child)))
80
+ }
81
+
82
+ return element
83
+ }
@@ -0,0 +1,7 @@
1
+ import './animate'
2
+ import { ResizeObserver } from './resize-observer'
3
+
4
+ export * from './match-media'
5
+ export * from './element'
6
+
7
+ global.ResizeObserver = ResizeObserver
@@ -0,0 +1,90 @@
1
+ import { vi } from 'vitest'
2
+
3
+ const watchMediaQueries = []
4
+ const listeners = []
5
+
6
+ /**
7
+ * @typedef {Object} MediaQuery
8
+ * @property {integer} [min-width]
9
+ * @property {integer} [max-width]
10
+ * @property {integer} [min-height]
11
+ * @property {integer} [max-height]
12
+ * @property {integer} [width]
13
+ * @property {integer} [height]
14
+ * @property {integer} [orientation]
15
+ * @property {integer} [aspect-ratio]
16
+ * @property {integer} [min-aspect-ratio]
17
+ * @property {integer} [max-aspect-ratio]
18
+ * @property {integer} [resolution]
19
+ * @property {integer} [min-resolution]
20
+ * @property {integer} [max-resolution]
21
+ * @property {integer} [scan]
22
+ * @property {integer} [grid]
23
+ * @property {integer} [update]
24
+ * @property {integer} [overflow-block]
25
+ */
26
+
27
+ /**
28
+ * Parses a media query string into an object
29
+ *
30
+ * @param {string} mediaQuery
31
+ * @returns {MediaQuery}
32
+ */
33
+ function parseMediaQuery(mediaQuery) {
34
+ const regex = /\(([^:]+):\s*([^)]+)\)/g
35
+ const result = {}
36
+
37
+ let match = null
38
+ while ((match = regex.exec(mediaQuery)) !== null) {
39
+ const [, property, value] = match
40
+ result[property.trim()] = parseInt(value.trim(), 10)
41
+ }
42
+
43
+ return result
44
+ }
45
+
46
+ /**
47
+ * Simulates the evaluation of a media query
48
+ *
49
+ * @param {MediaQuery} mediaQuery
50
+ * @param {integer} width
51
+ * @returns
52
+ */
53
+ function evaluateMediaQuery(mediaQuery, width) {
54
+ const { 'min-width': minWidth = 0, 'max-width': maxWidth = width } = mediaQuery
55
+
56
+ return width >= minWidth && width <= maxWidth
57
+ }
58
+
59
+ /**
60
+ * Mocks the window.matchMedia function
61
+ * @param {string} query
62
+ * @returns {Object}
63
+ */
64
+ export const matchMediaMock = vi.fn().mockImplementation((query) => {
65
+ const mediaQuery = parseMediaQuery(query)
66
+ const handler = (width) => evaluateMediaQuery(mediaQuery, width)
67
+
68
+ const queryObject = {
69
+ media: query,
70
+ matches: handler(window.innerWidth),
71
+ addListener: vi.fn().mockImplementation((listener) => listeners.push(listener)),
72
+ removeListener: vi
73
+ .fn()
74
+ .mockImplementation((listener) => listeners.splice(listeners.indexOf(listener), 1)),
75
+ handler
76
+ }
77
+ watchMediaQueries.push(queryObject)
78
+ return watchMediaQueries[watchMediaQueries.length - 1]
79
+ })
80
+
81
+ /**
82
+ * Updates the media query matches
83
+ * @returns {void}
84
+ */
85
+ export function updateMedia() {
86
+ watchMediaQueries.forEach((query) => {
87
+ query.matches = query.handler(window.innerWidth)
88
+ })
89
+ listeners.forEach((listener) => listener())
90
+ }
@@ -0,0 +1,25 @@
1
+ export class ResizeObserver {
2
+ constructor(callback) {
3
+ this.callback = callback
4
+ this.elements = new Map()
5
+ }
6
+
7
+ observe(element) {
8
+ this.elements.set(element, { contentRect: element.getBoundingClientRect() })
9
+ // Immediately invoke the callback with the initial size
10
+ this.callback([{ target: element, contentRect: element.getBoundingClientRect() }])
11
+ }
12
+
13
+ unobserve(element) {
14
+ this.elements.delete(element)
15
+ }
16
+
17
+ disconnect() {
18
+ this.elements.clear()
19
+ }
20
+
21
+ // Simulate a resize event
22
+ simulateResize(element, contentRect) {
23
+ this.callback([{ target: element, contentRect }])
24
+ }
25
+ }
@@ -0,0 +1,67 @@
1
+ import { vi } from 'vitest'
2
+
3
+ global.Touch = vi.fn().mockImplementation((input) => input)
4
+
5
+ export function simulateMouseEvent(x, y) {
6
+ return {
7
+ clientX: x,
8
+ clientY: y,
9
+ stopPropagation: vi.fn(),
10
+ preventDefault: vi.fn()
11
+ }
12
+ }
13
+ export function simulateTouchEvent(clientX, clientY) {
14
+ return {
15
+ touches: [{ clientX, clientY }],
16
+ preventDefault: vi.fn(),
17
+ stopPropagation: vi.fn()
18
+ }
19
+ }
20
+
21
+ export function simulateTouchSwipe(node, distance, delay = 0) {
22
+ const touchStart = new Touch({
23
+ identifier: 0,
24
+ target: node,
25
+ clientX: 0,
26
+ clientY: 0
27
+ })
28
+ const touchEnd = new Touch({
29
+ identifier: 0,
30
+ target: node,
31
+ clientX: distance.x,
32
+ clientY: distance.y
33
+ })
34
+ const touchStartEvent = new TouchEvent('touchstart', {
35
+ touches: [touchStart]
36
+ })
37
+ node.dispatchEvent(touchStartEvent)
38
+ vi.advanceTimersByTime(delay)
39
+ const touchEndEvent = new TouchEvent('touchend', {
40
+ changedTouches: [touchEnd]
41
+ })
42
+ node.dispatchEvent(touchEndEvent)
43
+ }
44
+
45
+ export function simulateMouseSwipe(node, distance, delay = 0) {
46
+ node.dispatchEvent(new MouseEvent('mousedown', { clientX: 0, clientY: 0 }))
47
+ node.dispatchEvent(
48
+ new MouseEvent('mousemove', {
49
+ clientX: distance.x / 2,
50
+ clientY: distance.y / 2
51
+ })
52
+ )
53
+ vi.advanceTimersByTime(delay)
54
+ node.dispatchEvent(new MouseEvent('mouseup', { clientX: distance.x, clientY: distance.y }))
55
+ }
56
+
57
+ // export function getCustomEventMock() {
58
+ // return vi.fn().mockImplementation((eventType, eventInit) => {
59
+ // class CustomEvent extends Event {
60
+ // constructor(type, init) {
61
+ // super(type, init)
62
+ // this.detail = init
63
+ // }
64
+ // }
65
+ // return new CustomEvent(eventType, eventInit)
66
+ // })
67
+ // }