mtrl 0.0.0 → 0.0.2

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.
package/test/setup.js ADDED
@@ -0,0 +1,458 @@
1
+ // test/setup.js
2
+ // Setup global DOM environment for testing
3
+
4
+ // Mock the document and window objects for DOM testing
5
+ // test/setup.js
6
+
7
+ // Mock DOM environment for testing
8
+ class MockElement {
9
+ constructor (tagName) {
10
+ this.tagName = tagName.toUpperCase()
11
+ this.className = ''
12
+ this.style = {}
13
+ this.attributes = {}
14
+ this.children = []
15
+ this.eventListeners = {}
16
+ this.innerHTML = ''
17
+ this.textContent = ''
18
+ this.dataset = {}
19
+ }
20
+
21
+ appendChild (child) {
22
+ this.children.push(child)
23
+ return child
24
+ }
25
+
26
+ insertBefore (newChild, referenceChild) {
27
+ const index = referenceChild ? this.children.indexOf(referenceChild) : 0
28
+ this.children.splice(index, 0, newChild)
29
+ return newChild
30
+ }
31
+
32
+ removeChild (child) {
33
+ const index = this.children.indexOf(child)
34
+ if (index !== -1) {
35
+ this.children.splice(index, 1)
36
+ }
37
+ return child
38
+ }
39
+
40
+ getAttribute (name) {
41
+ return this.attributes[name]
42
+ }
43
+
44
+ setAttribute (name, value) {
45
+ this.attributes[name] = value
46
+ }
47
+
48
+ removeAttribute (name) {
49
+ delete this.attributes[name]
50
+ }
51
+
52
+ hasAttribute (name) {
53
+ return name in this.attributes
54
+ }
55
+
56
+ querySelector (selector) {
57
+ // Super simple selector matching for testing
58
+ if (selector.startsWith('.')) {
59
+ const className = selector.substring(1)
60
+ return this.getElementsByClassName(className)[0] || null
61
+ }
62
+ return null
63
+ }
64
+
65
+ querySelectorAll (selector) {
66
+ if (selector.startsWith('.')) {
67
+ return this.getElementsByClassName(selector.substring(1))
68
+ }
69
+ return []
70
+ }
71
+
72
+ getElementsByClassName (className) {
73
+ const results = []
74
+ if (this.className.split(' ').includes(className)) {
75
+ results.push(this)
76
+ }
77
+ this.children.forEach(child => {
78
+ if (child.getElementsByClassName) {
79
+ results.push(...child.getElementsByClassName(className))
80
+ }
81
+ })
82
+ return results
83
+ }
84
+
85
+ addEventListener (type, listener) {
86
+ if (!this.eventListeners[type]) {
87
+ this.eventListeners[type] = []
88
+ }
89
+ this.eventListeners[type].push(listener)
90
+ }
91
+
92
+ removeEventListener (type, listener) {
93
+ if (this.eventListeners[type]) {
94
+ this.eventListeners[type] = this.eventListeners[type]
95
+ .filter(l => l !== listener)
96
+ }
97
+ }
98
+
99
+ dispatchEvent (event) {
100
+ if (this.eventListeners[event.type]) {
101
+ this.eventListeners[event.type].forEach(listener => {
102
+ listener(event)
103
+ })
104
+ }
105
+ return !event.defaultPrevented
106
+ }
107
+
108
+ get classList () {
109
+ const classNames = this.className.split(' ').filter(Boolean)
110
+ return {
111
+ add: (...classes) => {
112
+ classes.forEach(c => {
113
+ if (!classNames.includes(c)) {
114
+ classNames.push(c)
115
+ }
116
+ })
117
+ this.className = classNames.join(' ')
118
+ },
119
+ remove: (...classes) => {
120
+ classes.forEach(c => {
121
+ const index = classNames.indexOf(c)
122
+ if (index !== -1) {
123
+ classNames.splice(index, 1)
124
+ }
125
+ })
126
+ this.className = classNames.join(' ')
127
+ },
128
+ toggle: (c) => {
129
+ const index = classNames.indexOf(c)
130
+ if (index !== -1) {
131
+ classNames.splice(index, 1)
132
+ } else {
133
+ classNames.push(c)
134
+ }
135
+ this.className = classNames.join(' ')
136
+ return index === -1
137
+ },
138
+ contains: (c) => classNames.includes(c)
139
+ }
140
+ }
141
+
142
+ getBoundingClientRect () {
143
+ return {
144
+ width: 100,
145
+ height: 50,
146
+ top: 0,
147
+ left: 0,
148
+ right: 100,
149
+ bottom: 50
150
+ }
151
+ }
152
+
153
+ remove () {
154
+ if (this.parentNode) {
155
+ this.parentNode.removeChild(this)
156
+ }
157
+ }
158
+ }
159
+
160
+ // Set up global document object for tests
161
+ global.document = {
162
+ createElement: (tag) => new MockElement(tag),
163
+ createDocumentFragment: () => new MockElement('fragment'),
164
+ body: new MockElement('body'),
165
+ eventListeners: {},
166
+ addEventListener: function (type, listener) {
167
+ if (!this.eventListeners[type]) {
168
+ this.eventListeners[type] = []
169
+ }
170
+ this.eventListeners[type].push(listener)
171
+ },
172
+ removeEventListener: function (type, listener) {
173
+ if (this.eventListeners[type]) {
174
+ this.eventListeners[type] = this.eventListeners[type]
175
+ .filter(l => l !== listener)
176
+ }
177
+ },
178
+ dispatchEvent: function (event) {
179
+ if (this.eventListeners[event.type]) {
180
+ this.eventListeners[event.type].forEach(listener => {
181
+ listener(event)
182
+ })
183
+ }
184
+ return !event.defaultPrevented
185
+ }
186
+ }
187
+
188
+ // Set up global window object
189
+ global.window = {
190
+ getComputedStyle: () => ({
191
+ position: 'static'
192
+ })
193
+ }
194
+
195
+ // Set up Event constructor
196
+ global.Event = class Event {
197
+ constructor (type) {
198
+ this.type = type
199
+ this.defaultPrevented = false
200
+ }
201
+
202
+ preventDefault () {
203
+ this.defaultPrevented = true
204
+ }
205
+ }
206
+
207
+ // Set up AbortController
208
+ global.AbortController = class AbortController {
209
+ constructor () {
210
+ this.signal = { aborted: false }
211
+ }
212
+
213
+ abort () {
214
+ this.signal.aborted = true
215
+ }
216
+ }
217
+
218
+ // Additional DOM setup if needed
219
+
220
+ global.document = {
221
+ createElement: (tag) => {
222
+ const element = {
223
+ tagName: tag.toUpperCase(),
224
+ classList: {
225
+ add: (...classes) => {
226
+ element.className = (element.className || '').split(' ')
227
+ .concat(classes)
228
+ .filter(Boolean)
229
+ .join(' ')
230
+ },
231
+ remove: (...classes) => {
232
+ if (!element.className) return
233
+ const currentClasses = element.className.split(' ')
234
+ element.className = currentClasses
235
+ .filter(cls => !classes.includes(cls))
236
+ .join(' ')
237
+ },
238
+ toggle: (cls) => {
239
+ if (!element.className) {
240
+ element.className = cls
241
+ return true
242
+ }
243
+
244
+ const currentClasses = element.className.split(' ')
245
+ const hasClass = currentClasses.includes(cls)
246
+
247
+ if (hasClass) {
248
+ element.className = currentClasses
249
+ .filter(c => c !== cls)
250
+ .join(' ')
251
+ return false
252
+ } else {
253
+ element.className = [...currentClasses, cls]
254
+ .filter(Boolean)
255
+ .join(' ')
256
+ return true
257
+ }
258
+ },
259
+ contains: (cls) => {
260
+ if (!element.className) return false
261
+ return element.className.split(' ').includes(cls)
262
+ },
263
+ toString: () => element.className || ''
264
+ },
265
+ style: {},
266
+ dataset: {},
267
+ attributes: {},
268
+ children: [],
269
+ childNodes: [],
270
+ innerHTML: '',
271
+ textContent: '',
272
+ appendChild: (child) => {
273
+ element.children.push(child)
274
+ element.childNodes.push(child)
275
+ child.parentNode = element
276
+ return child
277
+ },
278
+ insertBefore: (newChild, refChild) => {
279
+ const index = refChild ? element.children.indexOf(refChild) : 0
280
+ if (index === -1) {
281
+ element.children.push(newChild)
282
+ } else {
283
+ element.children.splice(index, 0, newChild)
284
+ }
285
+ element.childNodes = [...element.children]
286
+ newChild.parentNode = element
287
+ return newChild
288
+ },
289
+ removeChild: (child) => {
290
+ const index = element.children.indexOf(child)
291
+ if (index !== -1) {
292
+ element.children.splice(index, 1)
293
+ element.childNodes = [...element.children]
294
+ }
295
+ return child
296
+ },
297
+ remove: () => {
298
+ if (element.parentNode) {
299
+ element.parentNode.removeChild(element)
300
+ }
301
+ },
302
+ querySelector: (selector) => {
303
+ // Very basic selector implementation - only supports class selectors for now
304
+ if (selector.startsWith('.')) {
305
+ const className = selector.slice(1)
306
+ return element.children.find(child =>
307
+ child.className && child.className.split(' ').includes(className)
308
+ )
309
+ }
310
+ return null
311
+ },
312
+ querySelectorAll: (selector) => {
313
+ // Very basic selector implementation for tests
314
+ if (selector.startsWith('.')) {
315
+ const className = selector.slice(1)
316
+ return element.children.filter(child =>
317
+ child.className && child.className.split(' ').includes(className)
318
+ )
319
+ }
320
+ return []
321
+ },
322
+ getBoundingClientRect: () => ({
323
+ width: 100,
324
+ height: 50,
325
+ top: 0,
326
+ left: 0,
327
+ right: 100,
328
+ bottom: 50
329
+ }),
330
+ setAttribute: (name, value) => {
331
+ element.attributes[name] = value
332
+ if (name === 'class') element.className = value
333
+ },
334
+ removeAttribute: (name) => {
335
+ delete element.attributes[name]
336
+ if (name === 'class') element.className = ''
337
+ },
338
+ getAttribute: (name) => element.attributes[name] || null,
339
+ hasAttribute: (name) => name in element.attributes,
340
+ addEventListener: (event, handler) => {
341
+ element.__handlers = element.__handlers || {}
342
+ element.__handlers[event] = element.__handlers[event] || []
343
+ element.__handlers[event].push(handler)
344
+ },
345
+ removeEventListener: (event, handler) => {
346
+ if (!element.__handlers?.[event]) return
347
+ element.__handlers[event] = element.__handlers[event].filter(h => h !== handler)
348
+ },
349
+ dispatchEvent: (event) => {
350
+ if (!element.__handlers?.[event.type]) return true
351
+ element.__handlers[event.type].forEach(handler => handler(event))
352
+ return !event.defaultPrevented
353
+ }
354
+ }
355
+ return element
356
+ },
357
+ createDocumentFragment: () => {
358
+ return {
359
+ children: [],
360
+ childNodes: [],
361
+ appendChild: function (child) {
362
+ this.children.push(child)
363
+ this.childNodes.push(child)
364
+ child.parentNode = this
365
+ return child
366
+ },
367
+ hasChildNodes: function () {
368
+ return this.childNodes.length > 0
369
+ },
370
+ querySelector: () => null,
371
+ querySelectorAll: () => []
372
+ }
373
+ },
374
+ body: {
375
+ appendChild: () => {},
376
+ classList: {
377
+ add: () => {},
378
+ remove: () => {},
379
+ contains: () => false
380
+ },
381
+ dispatchEvent: () => true,
382
+ getAttribute: () => null,
383
+ setAttribute: () => {}
384
+ }
385
+ }
386
+
387
+ global.window = {
388
+ getComputedStyle: () => ({
389
+ position: 'static',
390
+ getPropertyValue: () => ''
391
+ }),
392
+ addEventListener: () => {},
393
+ removeEventListener: () => {},
394
+ dispatchEvent: () => {},
395
+ innerWidth: 1024,
396
+ innerHeight: 768,
397
+ history: {
398
+ pushState: () => {}
399
+ },
400
+ location: {
401
+ pathname: '/'
402
+ },
403
+ navigator: {
404
+ userAgent: 'test'
405
+ },
406
+ performance: {
407
+ now: () => Date.now()
408
+ },
409
+ localStorage: {
410
+ getItem: () => null,
411
+ setItem: () => {},
412
+ removeItem: () => {}
413
+ }
414
+ }
415
+
416
+ // Mock for CustomEvent
417
+ global.CustomEvent = class CustomEvent {
418
+ constructor (type, options = {}) {
419
+ this.type = type
420
+ this.detail = options.detail || {}
421
+ this.defaultPrevented = false
422
+ }
423
+
424
+ preventDefault () {
425
+ this.defaultPrevented = true
426
+ }
427
+ }
428
+
429
+ global.Event = class Event {
430
+ constructor (type) {
431
+ this.type = type
432
+ this.defaultPrevented = false
433
+ }
434
+
435
+ preventDefault () {
436
+ this.defaultPrevented = true
437
+ }
438
+ }
439
+
440
+ // Mock console methods to prevent test output pollution
441
+ const originalConsole = { ...console }
442
+ global.console = {
443
+ ...console,
444
+ log: (...args) => {
445
+ if (process.env.DEBUG) {
446
+ originalConsole.log(...args)
447
+ }
448
+ },
449
+ warn: (...args) => {
450
+ if (process.env.DEBUG) {
451
+ originalConsole.warn(...args)
452
+ }
453
+ },
454
+ error: (...args) => {
455
+ // Always log errors
456
+ originalConsole.error(...args)
457
+ }
458
+ }