@domql/element 3.4.5 → 3.4.6

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 (47) hide show
  1. package/event/__tests__/applyAnimationFrame.test.js +114 -0
  2. package/event/__tests__/applyEvent.test.js +159 -0
  3. package/event/__tests__/applyEventUpdate.test.js +198 -0
  4. package/event/__tests__/applyEventsOnNode.test.js +216 -0
  5. package/event/__tests__/canRenderTag.test.js +50 -0
  6. package/event/__tests__/index.test.js +39 -0
  7. package/event/__tests__/initAnimationFrame.test.js +156 -0
  8. package/event/__tests__/registerFrameListener.test.js +97 -0
  9. package/event/__tests__/store.test.js +93 -0
  10. package/event/__tests__/triggerEventOn.test.js +195 -0
  11. package/event/__tests__/triggerEventOnUpdate.test.js +207 -0
  12. package/event/animationFrame.js +92 -0
  13. package/event/can.js +8 -0
  14. package/event/index.js +5 -0
  15. package/event/on.js +71 -0
  16. package/event/store.js +6 -0
  17. package/methods/set.js +73 -0
  18. package/methods/v2.js +83 -0
  19. package/mixins/attr.js +32 -0
  20. package/mixins/classList.js +62 -0
  21. package/mixins/content.js +65 -0
  22. package/mixins/data.js +26 -0
  23. package/mixins/html.js +19 -0
  24. package/mixins/index.js +23 -0
  25. package/mixins/registry.js +46 -0
  26. package/mixins/scope.js +23 -0
  27. package/mixins/state.js +18 -0
  28. package/mixins/style.js +25 -0
  29. package/mixins/text.js +31 -0
  30. package/package.json +13 -8
  31. package/render/__tests__/appendNode.test.js +53 -0
  32. package/render/__tests__/assignNode.test.js +151 -0
  33. package/render/__tests__/cacheNode.test.js +168 -0
  34. package/render/__tests__/createHTMLNode.test.js +118 -0
  35. package/render/__tests__/createNode.test.js +9 -0
  36. package/render/__tests__/detectTag.test.js +99 -0
  37. package/render/__tests__/index.test.js +56 -0
  38. package/render/__tests__/insertNodeAfter.test.js +111 -0
  39. package/render/__tests__/insertNodeBefore.test.js +65 -0
  40. package/render/append.js +61 -0
  41. package/render/cache.js +68 -0
  42. package/render/create.js +3 -0
  43. package/render/index.js +5 -0
  44. package/utils/applyParam.js +33 -0
  45. package/utils/extendUtils.js +135 -0
  46. package/utils/index.js +4 -0
  47. package/utils/propEvents.js +36 -0
@@ -0,0 +1,50 @@
1
+ import { canRenderTag } from '../can'
2
+
3
+ // Mock the error registry
4
+ global.ERRORS_REGISTRY = {
5
+ en: {
6
+ HTMLInvalidTag: {
7
+ description: 'Invalid HTML tag provided'
8
+ }
9
+ }
10
+ }
11
+
12
+ describe('canRenderTag', () => {
13
+ describe('valid tags', () => {
14
+ test('should return true for div tag', () => {
15
+ expect(canRenderTag('div')).toBe(true)
16
+ })
17
+
18
+ test('should default to div and return true when tag is undefined', () => {
19
+ expect(canRenderTag(undefined)).toBe(true)
20
+ })
21
+
22
+ test('should default to div and return true when tag is null', () => {
23
+ expect(canRenderTag(null)).toBe(true)
24
+ })
25
+
26
+ test('should return true for common HTML tags', () => {
27
+ const commonTags = ['p', 'span', 'button', 'input', 'a']
28
+ commonTags.forEach(tag => {
29
+ expect(canRenderTag(tag)).toBe(true)
30
+ })
31
+ })
32
+
33
+ test('should use custom error description from registry', () => {
34
+ // Arrange
35
+ global.ERRORS_REGISTRY = {
36
+ en: {
37
+ HTMLInvalidTag: {
38
+ description: 'Custom error description'
39
+ }
40
+ }
41
+ }
42
+
43
+ // Act
44
+ const result = canRenderTag('invalid-tag')
45
+
46
+ // Assert
47
+ expect(result).toBeInstanceOf(Error)
48
+ })
49
+ })
50
+ })
@@ -0,0 +1,39 @@
1
+ import * as moduleExports from '../index'
2
+
3
+ describe('Module exports', () => {
4
+ // Expected exports from on.js
5
+ test('should export on.js functions', () => {
6
+ expect(moduleExports).toHaveProperty('applyEvent')
7
+ expect(typeof moduleExports.applyEvent).toBe('function')
8
+
9
+ expect(moduleExports).toHaveProperty('triggerEventOn')
10
+ expect(typeof moduleExports.triggerEventOn).toBe('function')
11
+
12
+ expect(moduleExports).toHaveProperty('applyEventUpdate')
13
+ expect(typeof moduleExports.applyEventUpdate).toBe('function')
14
+
15
+ expect(moduleExports).toHaveProperty('triggerEventOnUpdate')
16
+ expect(typeof moduleExports.triggerEventOnUpdate).toBe('function')
17
+
18
+ expect(moduleExports).toHaveProperty('applyEventsOnNode')
19
+ expect(typeof moduleExports.applyEventsOnNode).toBe('function')
20
+ })
21
+
22
+ // Expected exports from can.js
23
+ test('should export can.js functions', () => {
24
+ expect(moduleExports).toHaveProperty('canRenderTag')
25
+ expect(typeof moduleExports.canRenderTag).toBe('function')
26
+ })
27
+
28
+ // Expected exports from animationFrame.js
29
+ test('should export animationFrame.js functions', () => {
30
+ expect(moduleExports).toHaveProperty('initAnimationFrame')
31
+ expect(typeof moduleExports.initAnimationFrame).toBe('function')
32
+
33
+ expect(moduleExports).toHaveProperty('registerFrameListener')
34
+ expect(typeof moduleExports.registerFrameListener).toBe('function')
35
+
36
+ expect(moduleExports).toHaveProperty('applyAnimationFrame')
37
+ expect(typeof moduleExports.applyAnimationFrame).toBe('function')
38
+ })
39
+ })
@@ -0,0 +1,156 @@
1
+ import { jest } from '@jest/globals'
2
+ import { initAnimationFrame } from '../animationFrame'
3
+
4
+ describe('initAnimationFrame', () => {
5
+ let requestAnimationFrameCallback
6
+ let frameListeners
7
+ let mockElement
8
+ let mockOnFrame
9
+
10
+ beforeEach(() => {
11
+ // Store callback instead of executing it immediately
12
+ window.requestAnimationFrame = jest.fn(cb => {
13
+ requestAnimationFrameCallback = cb
14
+ return 1 // Return a dummy frame ID
15
+ })
16
+
17
+ // Mock console.warn to avoid test output noise
18
+ console.warn = jest.fn()
19
+
20
+ // Create mock element with required structure
21
+ mockOnFrame = jest.fn()
22
+ mockElement = {
23
+ node: document.createElement('div'),
24
+ parent: {
25
+ node: document.createElement('div')
26
+ },
27
+ props: {
28
+ onFrame: mockOnFrame
29
+ },
30
+ onFrame: mockOnFrame, // Add direct onFrame property to the element
31
+ state: { foo: 'bar' },
32
+ context: { baz: 'qux' }
33
+ }
34
+
35
+ // Append child to parent for contains() to work
36
+ mockElement.parent.node.appendChild(mockElement.node)
37
+
38
+ // Initialize frameListeners
39
+ frameListeners = initAnimationFrame()
40
+ })
41
+
42
+ afterEach(() => {
43
+ // Clean up DOM
44
+ if (mockElement.parent.node.parentNode) {
45
+ mockElement.parent.node.parentNode.removeChild(mockElement.parent.node)
46
+ }
47
+ })
48
+
49
+ describe('initialization', () => {
50
+ test('should return an empty Set', () => {
51
+ expect(frameListeners).toBeInstanceOf(Set)
52
+ expect(frameListeners.size).toBe(0)
53
+ })
54
+
55
+ test('should request first animation frame', () => {
56
+ expect(window.requestAnimationFrame).toHaveBeenCalledTimes(1)
57
+ })
58
+ })
59
+
60
+ describe('frame processing', () => {
61
+ test('should call onFrame with correct arguments when element is in DOM', () => {
62
+ // Arrange
63
+ frameListeners.add(mockElement)
64
+
65
+ // Act - trigger a single frame
66
+ requestAnimationFrameCallback()
67
+
68
+ // Assert
69
+ expect(mockOnFrame).toHaveBeenCalledWith(
70
+ mockElement,
71
+ mockElement.state,
72
+ mockElement.context
73
+ )
74
+ })
75
+
76
+ test('should use on.frame if available instead of onFrame', () => {
77
+ // Arrange
78
+ const onFrameHandler = jest.fn()
79
+ mockElement.on = { frame: onFrameHandler }
80
+ frameListeners.add(mockElement)
81
+
82
+ // Act - trigger a single frame
83
+ requestAnimationFrameCallback()
84
+
85
+ // Assert
86
+ expect(onFrameHandler).toHaveBeenCalledWith(
87
+ mockElement,
88
+ mockElement.state,
89
+ mockElement.context
90
+ )
91
+ expect(mockOnFrame).not.toHaveBeenCalled()
92
+ })
93
+
94
+ test('should remove element if not in DOM', () => {
95
+ // Arrange
96
+ frameListeners.add(mockElement)
97
+ mockElement.parent.node.removeChild(mockElement.node)
98
+
99
+ // Act - trigger a single frame
100
+ requestAnimationFrameCallback()
101
+
102
+ // Assert
103
+ expect(frameListeners.has(mockElement)).toBe(false)
104
+ expect(frameListeners.size).toBe(0)
105
+ })
106
+
107
+ test('should catch and warn on onFrame errors', () => {
108
+ // Arrange
109
+ const error = new Error('Test error')
110
+ mockElement.onFrame = jest.fn(() => {
111
+ throw error
112
+ })
113
+ frameListeners.add(mockElement)
114
+
115
+ // Act - trigger a single frame
116
+ requestAnimationFrameCallback()
117
+
118
+ // Assert
119
+ expect(console.warn).toHaveBeenCalledWith(error)
120
+ // Element should still be in the set despite the error
121
+ expect(frameListeners.has(mockElement)).toBe(true)
122
+ })
123
+
124
+ test('should process multiple elements in the set', () => {
125
+ // Arrange
126
+ const mockElement2 = {
127
+ ...mockElement,
128
+ node: document.createElement('div'),
129
+ props: { onFrame: jest.fn() },
130
+ onFrame: jest.fn() // Add direct onFrame property to mockElement2
131
+ }
132
+ mockElement.parent.node.appendChild(mockElement2.node)
133
+
134
+ frameListeners.add(mockElement)
135
+ frameListeners.add(mockElement2)
136
+
137
+ // Act - trigger a single frame
138
+ requestAnimationFrameCallback()
139
+
140
+ // Assert
141
+ expect(mockOnFrame).toHaveBeenCalled()
142
+ expect(mockElement2.onFrame).toHaveBeenCalled()
143
+ expect(frameListeners.size).toBe(2)
144
+ })
145
+ })
146
+
147
+ describe('animation loop', () => {
148
+ test('should request next animation frame after processing', () => {
149
+ // Act - trigger a single frame
150
+ requestAnimationFrameCallback()
151
+
152
+ // Assert - should have requested the next frame
153
+ expect(window.requestAnimationFrame).toHaveBeenCalledTimes(2)
154
+ })
155
+ })
156
+ })
@@ -0,0 +1,97 @@
1
+ import { registerFrameListener } from '../animationFrame'
2
+
3
+ describe('registerFrameListener', () => {
4
+ let mockElement
5
+ let mockFrameListeners
6
+
7
+ beforeEach(() => {
8
+ // Reset mockFrameListeners before each test
9
+ mockFrameListeners = new Set()
10
+
11
+ // Create a mock element with the required structure
12
+ mockElement = {
13
+ __ref: {
14
+ root: {
15
+ data: {
16
+ frameListeners: mockFrameListeners
17
+ }
18
+ }
19
+ }
20
+ }
21
+ })
22
+
23
+ describe('successful cases', () => {
24
+ test('should add element to frameListeners when not already present', () => {
25
+ // Arrange
26
+ expect(mockFrameListeners.size).toBe(0)
27
+
28
+ // Act
29
+ registerFrameListener(mockElement)
30
+
31
+ // Assert
32
+ expect(mockFrameListeners.size).toBe(1)
33
+ expect(mockFrameListeners.has(mockElement)).toBe(true)
34
+ })
35
+
36
+ test('should not add element to frameListeners when already present', () => {
37
+ // Arrange
38
+ mockFrameListeners.add(mockElement)
39
+ expect(mockFrameListeners.size).toBe(1)
40
+
41
+ // Act
42
+ registerFrameListener(mockElement)
43
+
44
+ // Assert
45
+ expect(mockFrameListeners.size).toBe(1)
46
+ expect(mockFrameListeners.has(mockElement)).toBe(true)
47
+ })
48
+
49
+ test('should handle case when frameListeners is undefined', () => {
50
+ // Arrange
51
+ mockElement.__ref.root.data.frameListeners = undefined
52
+
53
+ // Act & Assert
54
+ expect(() => {
55
+ registerFrameListener(mockElement)
56
+ }).not.toThrow()
57
+ })
58
+ })
59
+
60
+ describe('error cases', () => {
61
+ test('should throw error when element is null', () => {
62
+ expect(() => {
63
+ registerFrameListener(null)
64
+ }).toThrow('Element reference is invalid')
65
+ })
66
+
67
+ test('should throw error when element is undefined', () => {
68
+ expect(() => {
69
+ registerFrameListener(undefined)
70
+ }).toThrow('Element reference is invalid')
71
+ })
72
+
73
+ test('should throw error when element.__ref is undefined', () => {
74
+ const invalidElement = {}
75
+
76
+ expect(() => {
77
+ registerFrameListener(invalidElement)
78
+ }).toThrow('Element reference is invalid')
79
+ })
80
+
81
+ test('should throw error when ref.root is undefined', () => {
82
+ mockElement.__ref.root = undefined
83
+
84
+ expect(() => {
85
+ registerFrameListener(mockElement)
86
+ }).toThrow('Root reference is invalid')
87
+ })
88
+
89
+ test('should throw error when ref.root.data is undefined', () => {
90
+ mockElement.__ref.root.data = undefined
91
+
92
+ expect(() => {
93
+ registerFrameListener(mockElement)
94
+ }).toThrow('Data are undefined')
95
+ })
96
+ })
97
+ })
@@ -0,0 +1,93 @@
1
+ import eventRegistry from '../store'
2
+
3
+ describe('eventRegistry', () => {
4
+ test('should have the correct initial structure', () => {
5
+ expect(eventRegistry).toEqual({
6
+ click: [],
7
+ render: []
8
+ })
9
+ })
10
+
11
+ test('should have empty arrays as initial values', () => {
12
+ expect(Array.isArray(eventRegistry.click)).toBe(true)
13
+ expect(Array.isArray(eventRegistry.render)).toBe(true)
14
+ expect(eventRegistry.click.length).toBe(0)
15
+ expect(eventRegistry.render.length).toBe(0)
16
+ })
17
+
18
+ test('should allow adding items to click array', () => {
19
+ // Arrange
20
+ const clickHandler = () => {}
21
+
22
+ // Act
23
+ eventRegistry.click.push(clickHandler)
24
+
25
+ // Assert
26
+ expect(eventRegistry.click).toContain(clickHandler)
27
+ expect(eventRegistry.click.length).toBe(1)
28
+
29
+ // Cleanup
30
+ eventRegistry.click.length = 0
31
+ })
32
+
33
+ test('should allow adding items to render array', () => {
34
+ // Arrange
35
+ const renderHandler = () => {}
36
+
37
+ // Act
38
+ eventRegistry.render.push(renderHandler)
39
+
40
+ // Assert
41
+ expect(eventRegistry.render).toContain(renderHandler)
42
+ expect(eventRegistry.render.length).toBe(1)
43
+
44
+ // Cleanup
45
+ eventRegistry.render.length = 0
46
+ })
47
+
48
+ test('should maintain separate arrays for different events', () => {
49
+ // Arrange
50
+ const clickHandler = () => {}
51
+ const renderHandler = () => {}
52
+
53
+ // Act
54
+ eventRegistry.click.push(clickHandler)
55
+ eventRegistry.render.push(renderHandler)
56
+
57
+ // Assert
58
+ expect(eventRegistry.click).toContain(clickHandler)
59
+ expect(eventRegistry.click).not.toContain(renderHandler)
60
+ expect(eventRegistry.render).toContain(renderHandler)
61
+ expect(eventRegistry.render).not.toContain(clickHandler)
62
+
63
+ // Cleanup
64
+ eventRegistry.click.length = 0
65
+ eventRegistry.render.length = 0
66
+ })
67
+
68
+ test('should allow array operations on event arrays', () => {
69
+ // Test various array operations
70
+ const handler1 = () => {}
71
+ const handler2 = () => {}
72
+
73
+ // Push
74
+ eventRegistry.click.push(handler1)
75
+ expect(eventRegistry.click).toContain(handler1)
76
+
77
+ // Push multiple
78
+ eventRegistry.click.push(handler2)
79
+ expect(eventRegistry.click).toEqual([handler1, handler2])
80
+
81
+ // Pop
82
+ const popped = eventRegistry.click.pop()
83
+ expect(popped).toBe(handler2)
84
+ expect(eventRegistry.click).toEqual([handler1])
85
+
86
+ // Cleanup
87
+ eventRegistry.click.length = 0
88
+ })
89
+
90
+ test('should have independent arrays', () => {
91
+ expect(eventRegistry.click).not.toBe(eventRegistry.render)
92
+ })
93
+ })
@@ -0,0 +1,195 @@
1
+ import { jest } from '@jest/globals'
2
+ import { triggerEventOn } from '../on'
3
+
4
+ describe('triggerEventOn', () => {
5
+ let mockElement
6
+ let mockEventHandler
7
+ let mockOptions
8
+
9
+ beforeEach(() => {
10
+ mockEventHandler = jest.fn().mockReturnValue('eventResult')
11
+ mockOptions = { option1: 'value1' }
12
+
13
+ mockElement = {
14
+ state: { elementState: 'test' },
15
+ context: { elementContext: 'test' },
16
+ on: {},
17
+ props: {}
18
+ }
19
+ })
20
+
21
+ describe('event resolution', () => {
22
+ test('should trigger event from on property', () => {
23
+ // Arrange
24
+ mockElement.on.click = mockEventHandler
25
+
26
+ // Act
27
+ const result = triggerEventOn('click', mockElement, mockOptions)
28
+
29
+ // Assert
30
+ expect(result).toBe('eventResult')
31
+ expect(mockEventHandler).toHaveBeenCalledWith(
32
+ mockElement,
33
+ mockElement.state,
34
+ mockElement.context,
35
+ mockOptions
36
+ )
37
+ })
38
+
39
+ test('should trigger event from props using camelCase', () => {
40
+ // Arrange
41
+ mockElement.props.onClick = mockEventHandler
42
+
43
+ // Act
44
+ const result = triggerEventOn('click', mockElement, mockOptions)
45
+
46
+ // Assert
47
+ expect(result).toBe('eventResult')
48
+ expect(mockEventHandler).toHaveBeenCalledWith(
49
+ mockElement,
50
+ mockElement.state,
51
+ mockElement.context,
52
+ mockOptions
53
+ )
54
+ })
55
+
56
+ test('should prioritize on property over props', () => {
57
+ // Arrange
58
+ const onEventHandler = jest.fn().mockReturnValue('onResult')
59
+ const propsEventHandler = jest.fn().mockReturnValue('propsResult')
60
+
61
+ mockElement.on.click = onEventHandler
62
+ mockElement.props.onClick = propsEventHandler
63
+
64
+ // Act
65
+ const result = triggerEventOn('click', mockElement, mockOptions)
66
+
67
+ // Assert
68
+ expect(result).toBe('onResult')
69
+ expect(onEventHandler).toHaveBeenCalled()
70
+ expect(propsEventHandler).not.toHaveBeenCalled()
71
+ })
72
+ })
73
+
74
+ describe('event parameters', () => {
75
+ test('should pass state and context correctly', () => {
76
+ // Arrange
77
+ mockElement.on.click = mockEventHandler
78
+ mockElement.state = { custom: 'state' }
79
+ mockElement.context = { custom: 'context' }
80
+
81
+ // Act
82
+ triggerEventOn('click', mockElement, mockOptions)
83
+
84
+ // Assert
85
+ expect(mockEventHandler).toHaveBeenCalledWith(
86
+ mockElement,
87
+ mockElement.state,
88
+ mockElement.context,
89
+ mockOptions
90
+ )
91
+ })
92
+
93
+ test('should handle missing state and context', () => {
94
+ // Arrange
95
+ mockElement = {
96
+ on: { click: mockEventHandler }
97
+ }
98
+
99
+ // Act
100
+ triggerEventOn('click', mockElement, mockOptions)
101
+
102
+ // Assert
103
+ expect(mockEventHandler).toHaveBeenCalledWith(
104
+ mockElement,
105
+ undefined,
106
+ undefined,
107
+ mockOptions
108
+ )
109
+ })
110
+
111
+ test('should handle missing options', () => {
112
+ // Arrange
113
+ mockElement.on.click = mockEventHandler
114
+
115
+ // Act
116
+ triggerEventOn('click', mockElement)
117
+
118
+ // Assert
119
+ expect(mockEventHandler).toHaveBeenCalledWith(
120
+ mockElement,
121
+ mockElement.state,
122
+ mockElement.context,
123
+ undefined
124
+ )
125
+ })
126
+ })
127
+
128
+ describe('event naming', () => {
129
+ test('should handle various event names correctly', () => {
130
+ const eventTests = [
131
+ { input: 'mouseenter', prop: 'onMouseenter' },
132
+ { input: 'mouseleave', prop: 'onMouseleave' },
133
+ { input: 'submit', prop: 'onSubmit' },
134
+ { input: 'change', prop: 'onChange' }
135
+ ]
136
+
137
+ for (const { input, prop } of eventTests) {
138
+ // Arrange
139
+ mockElement.props[prop] = mockEventHandler
140
+
141
+ // Act
142
+ triggerEventOn(input, mockElement, mockOptions)
143
+
144
+ // Assert
145
+ expect(mockEventHandler).toHaveBeenCalledWith(
146
+ mockElement,
147
+ mockElement.state,
148
+ mockElement.context,
149
+ mockOptions
150
+ )
151
+
152
+ // Reset for next test
153
+ mockElement.props[prop] = undefined
154
+ mockEventHandler.mockClear()
155
+ }
156
+ })
157
+ })
158
+
159
+ describe('edge cases', () => {
160
+ test('should return undefined when no event handler exists', () => {
161
+ // Act
162
+ const result = triggerEventOn('click', mockElement, mockOptions)
163
+
164
+ // Assert
165
+ expect(result).toBeUndefined()
166
+ })
167
+
168
+ test('should handle error thrown in event handler', () => {
169
+ // Arrange
170
+ const error = new Error('Event handler error')
171
+ mockElement.on.click = jest.fn(() => {
172
+ throw error
173
+ })
174
+
175
+ // Act & Assert
176
+ expect(() => triggerEventOn('click', mockElement, mockOptions)).toThrow(
177
+ 'Event handler error'
178
+ )
179
+ })
180
+
181
+ test('should handle undefined element', () => {
182
+ // Assert
183
+ expect(() => triggerEventOn('click', undefined, mockOptions)).toThrow(
184
+ 'Element is required'
185
+ )
186
+ })
187
+
188
+ test('should handle null element', () => {
189
+ // Assert
190
+ expect(() => triggerEventOn('click', null, mockOptions)).toThrow(
191
+ 'Element is required'
192
+ )
193
+ })
194
+ })
195
+ })