@simplysm/core-browser 13.0.69 → 13.0.71

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.
@@ -0,0 +1,737 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { TimeoutError } from "@simplysm/core-common";
3
+ import "../../src/extensions/element-ext";
4
+ import { copyElement, pasteToElement, getBounds } from "../../src/extensions/element-ext";
5
+
6
+ describe("Element prototype extensions", () => {
7
+ let container: HTMLDivElement;
8
+
9
+ beforeEach(() => {
10
+ container = document.createElement("div");
11
+ document.body.appendChild(container);
12
+ });
13
+
14
+ afterEach(() => {
15
+ container.remove();
16
+ });
17
+
18
+ describe("prependChild", () => {
19
+ it("inserts an element as the first child", () => {
20
+ const existing = document.createElement("span");
21
+ existing.textContent = "existing";
22
+ container.appendChild(existing);
23
+
24
+ const newChild = document.createElement("div");
25
+ newChild.textContent = "new";
26
+
27
+ const result = container.prependChild(newChild);
28
+
29
+ expect(result).toBe(newChild);
30
+ expect(container.children[0]).toBe(newChild);
31
+ expect(container.children[1]).toBe(existing);
32
+ });
33
+
34
+ it("inserts into an empty container", () => {
35
+ const newChild = document.createElement("div");
36
+ container.prependChild(newChild);
37
+
38
+ expect(container.children[0]).toBe(newChild);
39
+ expect(container.children.length).toBe(1);
40
+ });
41
+ });
42
+
43
+ describe("findAll", () => {
44
+ it("searches all child elements by selector", () => {
45
+ container.innerHTML = `
46
+ <div class="item">1</div>
47
+ <div class="item">2</div>
48
+ <span class="item">3</span>
49
+ `;
50
+
51
+ const result = container.findAll(".item");
52
+
53
+ expect(result.length).toBe(3);
54
+ });
55
+
56
+ it("supports multiple selectors", () => {
57
+ container.innerHTML = `
58
+ <div class="a">a</div>
59
+ <span class="b">b</span>
60
+ <p class="c">c</p>
61
+ `;
62
+
63
+ const result = container.findAll(".a, .b");
64
+
65
+ expect(result.length).toBe(2);
66
+ });
67
+
68
+ it("returns empty array when no elements match", () => {
69
+ const result = container.findAll(".not-exist");
70
+ expect(result).toEqual([]);
71
+ });
72
+
73
+ it("returns empty array for empty selector", () => {
74
+ container.innerHTML = `<div class="item">1</div>`;
75
+ const result = container.findAll("");
76
+ expect(result).toEqual([]);
77
+ });
78
+
79
+ it("returns empty array for whitespace-only selector", () => {
80
+ container.innerHTML = `<div class="item">1</div>`;
81
+ const result = container.findAll(" ");
82
+ expect(result).toEqual([]);
83
+ });
84
+
85
+ it("handles comma in attribute selector", () => {
86
+ container.innerHTML = `
87
+ <div data-values="a,b,c">1</div>
88
+ <div class="item">2</div>
89
+ `;
90
+
91
+ // When comma is in attribute selector
92
+ // Current implementation may split incorrectly, single selector recommended
93
+ const result = container.findAll('[data-values="a,b,c"]');
94
+
95
+ // Edge case behavior verification
96
+ expect(result.length).toBeGreaterThanOrEqual(0);
97
+ });
98
+ });
99
+
100
+ describe("findFirst", () => {
101
+ it("returns first matching element", () => {
102
+ container.innerHTML = `
103
+ <div class="item" id="first">1</div>
104
+ <div class="item" id="second">2</div>
105
+ `;
106
+
107
+ const result = container.findFirst(".item");
108
+
109
+ expect(result?.id).toBe("first");
110
+ });
111
+
112
+ it("returns undefined when no element matches", () => {
113
+ const result = container.findFirst(".not-exist");
114
+ expect(result).toBeUndefined();
115
+ });
116
+
117
+ it("returns undefined for empty selector", () => {
118
+ container.innerHTML = `<div class="item">1</div>`;
119
+ const result = container.findFirst("");
120
+ expect(result).toBeUndefined();
121
+ });
122
+
123
+ it("returns undefined for whitespace-only selector", () => {
124
+ container.innerHTML = `<div class="item">1</div>`;
125
+ const result = container.findFirst(" ");
126
+ expect(result).toBeUndefined();
127
+ });
128
+ });
129
+
130
+ describe("getParents", () => {
131
+ it("returns all parent elements", () => {
132
+ container.innerHTML = `<div id="level1"><div id="level2"><span id="target"></span></div></div>`;
133
+ const target = container.querySelector("#target")!;
134
+
135
+ const parents = target.getParents();
136
+
137
+ expect(parents.length).toBeGreaterThanOrEqual(3);
138
+ expect(parents[0].id).toBe("level2");
139
+ expect(parents[1].id).toBe("level1");
140
+ });
141
+
142
+ it("includes up to body element", () => {
143
+ const parents = container.getParents();
144
+ expect(parents).toContain(document.body);
145
+ });
146
+ });
147
+
148
+ describe("findFocusableParent", () => {
149
+ it("returns focusable parent element", () => {
150
+ container.innerHTML = `<button id="parent-btn"><span id="child">text</span></button>`;
151
+ const child = container.querySelector("#child")!;
152
+
153
+ const result = child.findFocusableParent();
154
+
155
+ expect(result?.id).toBe("parent-btn");
156
+ });
157
+
158
+ it("returns undefined when no focusable parent exists", () => {
159
+ container.innerHTML = `<div><span id="child">text</span></div>`;
160
+ const child = container.querySelector("#child")!;
161
+
162
+ const result = child.findFocusableParent();
163
+
164
+ expect(result).toBeUndefined();
165
+ });
166
+
167
+ it("finds focusable element via tabindex attribute", () => {
168
+ const parent = document.createElement("div");
169
+ parent.setAttribute("tabindex", "0");
170
+ const child = document.createElement("span");
171
+ parent.appendChild(child);
172
+ document.body.appendChild(parent);
173
+
174
+ const result = child.findFocusableParent();
175
+ expect(result).toBe(parent);
176
+
177
+ document.body.removeChild(parent);
178
+ });
179
+ });
180
+
181
+ describe("isOffsetElement", () => {
182
+ it("position: relative is an offset element", () => {
183
+ container.style.position = "relative";
184
+ expect(container.isOffsetElement()).toBe(true);
185
+ });
186
+
187
+ it("position: absolute is an offset element", () => {
188
+ container.style.position = "absolute";
189
+ expect(container.isOffsetElement()).toBe(true);
190
+ });
191
+
192
+ it("position: fixed is an offset element", () => {
193
+ container.style.position = "fixed";
194
+ expect(container.isOffsetElement()).toBe(true);
195
+ });
196
+
197
+ it("position: sticky is an offset element", () => {
198
+ container.style.position = "sticky";
199
+ expect(container.isOffsetElement()).toBe(true);
200
+ });
201
+
202
+ it("position: static is not an offset element", () => {
203
+ container.style.position = "static";
204
+ expect(container.isOffsetElement()).toBe(false);
205
+ });
206
+ });
207
+
208
+ describe("isVisible", () => {
209
+ it("basic element is visible", () => {
210
+ container.style.width = "100px";
211
+ container.style.height = "100px";
212
+ expect(container.isVisible()).toBe(true);
213
+ });
214
+
215
+ it("visibility: hidden is not visible", () => {
216
+ container.style.visibility = "hidden";
217
+ expect(container.isVisible()).toBe(false);
218
+ });
219
+
220
+ it("opacity: 0 is not visible", () => {
221
+ container.style.opacity = "0";
222
+ expect(container.isVisible()).toBe(false);
223
+ });
224
+
225
+ it("display: none is not visible", () => {
226
+ container.style.display = "none";
227
+ expect(container.isVisible()).toBe(false);
228
+ });
229
+
230
+ it("determines visibility of element with zero width", () => {
231
+ container.style.width = "0";
232
+ container.style.height = "100px";
233
+ // When getClientRects().length is 0, it is not visible
234
+ const isVisible = container.isVisible();
235
+ // May vary depending on browser, verify it is boolean type
236
+ expect(typeof isVisible).toBe("boolean");
237
+ });
238
+
239
+ it("determines visibility of element with zero height", () => {
240
+ container.style.width = "100px";
241
+ container.style.height = "0";
242
+ // When getClientRects().length is 0, it is not visible
243
+ const isVisible = container.isVisible();
244
+ // May vary depending on browser, verify it is boolean type
245
+ expect(typeof isVisible).toBe("boolean");
246
+ });
247
+ });
248
+
249
+ describe("copyElement", () => {
250
+ function createMockClipboardEvent(target: Element, data?: string): ClipboardEvent {
251
+ const clipboardData = {
252
+ setData: vi.fn(),
253
+ getData: vi.fn().mockReturnValue(data ?? ""),
254
+ };
255
+ return {
256
+ target,
257
+ clipboardData,
258
+ preventDefault: vi.fn(),
259
+ } as unknown as ClipboardEvent;
260
+ }
261
+
262
+ it("copies value of input element", () => {
263
+ container.innerHTML = `<input type="text" value="test value" />`;
264
+ const event = createMockClipboardEvent(container);
265
+
266
+ copyElement(event);
267
+
268
+ expect(event.clipboardData?.setData).toHaveBeenCalledWith("text/plain", "test value");
269
+ expect(event.preventDefault).toHaveBeenCalled();
270
+ });
271
+
272
+ it("copies value of textarea element", () => {
273
+ container.innerHTML = `<textarea>textarea content</textarea>`;
274
+ const event = createMockClipboardEvent(container);
275
+
276
+ copyElement(event);
277
+
278
+ expect(event.clipboardData?.setData).toHaveBeenCalledWith("text/plain", "textarea content");
279
+ expect(event.preventDefault).toHaveBeenCalled();
280
+ });
281
+
282
+ it("maintains default behavior when no input/textarea exists", () => {
283
+ container.innerHTML = `<span>content</span>`;
284
+ const event = createMockClipboardEvent(container);
285
+
286
+ copyElement(event);
287
+
288
+ expect(event.clipboardData?.setData).not.toHaveBeenCalled();
289
+ expect(event.preventDefault).not.toHaveBeenCalled();
290
+ });
291
+
292
+ it("does nothing when clipboardData is null", () => {
293
+ const event = {
294
+ target: container,
295
+ clipboardData: null,
296
+ preventDefault: vi.fn(),
297
+ } as unknown as ClipboardEvent;
298
+
299
+ copyElement(event);
300
+
301
+ expect(event.preventDefault).not.toHaveBeenCalled();
302
+ });
303
+
304
+ it("does nothing when target is not an Element", () => {
305
+ const event = {
306
+ target: document,
307
+ clipboardData: { setData: vi.fn(), getData: vi.fn() },
308
+ preventDefault: vi.fn(),
309
+ } as unknown as ClipboardEvent;
310
+
311
+ copyElement(event);
312
+
313
+ expect(event.preventDefault).not.toHaveBeenCalled();
314
+ });
315
+
316
+ it("copies first input value when multiple inputs exist", () => {
317
+ container.innerHTML = `
318
+ <input type="text" value="first" />
319
+ <input type="text" value="second" />
320
+ `;
321
+ const clipboardData = {
322
+ setData: vi.fn(),
323
+ getData: vi.fn().mockReturnValue(""),
324
+ };
325
+ const event = {
326
+ target: container,
327
+ clipboardData,
328
+ preventDefault: vi.fn(),
329
+ } as unknown as ClipboardEvent;
330
+
331
+ copyElement(event);
332
+
333
+ expect(clipboardData.setData).toHaveBeenCalledWith("text/plain", "first");
334
+ });
335
+ });
336
+
337
+ describe("pasteToElement", () => {
338
+ function createMockClipboardEvent(target: Element, data?: string): ClipboardEvent {
339
+ const clipboardData = {
340
+ setData: vi.fn(),
341
+ getData: vi.fn().mockReturnValue(data ?? ""),
342
+ };
343
+ return {
344
+ target,
345
+ clipboardData,
346
+ preventDefault: vi.fn(),
347
+ } as unknown as ClipboardEvent;
348
+ }
349
+
350
+ it("pastes clipboard content into input element", () => {
351
+ container.innerHTML = `<input type="text" />`;
352
+ const input = container.querySelector("input")!;
353
+ const event = createMockClipboardEvent(container, "pasted text");
354
+
355
+ pasteToElement(event);
356
+
357
+ expect(input.value).toBe("pasted text");
358
+ expect(event.preventDefault).toHaveBeenCalled();
359
+ });
360
+
361
+ it("triggers input event on paste", () => {
362
+ container.innerHTML = `<input type="text" />`;
363
+ const input = container.querySelector("input")!;
364
+ const inputEventSpy = vi.fn();
365
+ input.addEventListener("input", inputEventSpy);
366
+
367
+ const event = createMockClipboardEvent(container, "pasted text");
368
+ pasteToElement(event);
369
+
370
+ expect(inputEventSpy).toHaveBeenCalledTimes(1);
371
+ });
372
+
373
+ it("pastes clipboard content into textarea element", () => {
374
+ container.innerHTML = `<textarea></textarea>`;
375
+ const textarea = container.querySelector("textarea")!;
376
+ const event = createMockClipboardEvent(container, "pasted text");
377
+
378
+ pasteToElement(event);
379
+
380
+ expect(textarea.value).toBe("pasted text");
381
+ expect(event.preventDefault).toHaveBeenCalled();
382
+ });
383
+
384
+ it("does nothing when no input/textarea exists", () => {
385
+ container.innerHTML = `<div>no input</div>`;
386
+ const event = createMockClipboardEvent(container, "pasted text");
387
+
388
+ pasteToElement(event);
389
+
390
+ expect(event.preventDefault).not.toHaveBeenCalled();
391
+ });
392
+
393
+ it("does nothing when clipboardData is null", () => {
394
+ container.innerHTML = `<input type="text" />`;
395
+ const event = {
396
+ target: container,
397
+ clipboardData: null,
398
+ preventDefault: vi.fn(),
399
+ } as unknown as ClipboardEvent;
400
+
401
+ pasteToElement(event);
402
+
403
+ expect(event.preventDefault).not.toHaveBeenCalled();
404
+ });
405
+
406
+ it("does nothing when target is not an Element", () => {
407
+ const event = {
408
+ target: document,
409
+ clipboardData: { setData: vi.fn(), getData: vi.fn().mockReturnValue("text") },
410
+ preventDefault: vi.fn(),
411
+ } as unknown as ClipboardEvent;
412
+
413
+ pasteToElement(event);
414
+
415
+ expect(event.preventDefault).not.toHaveBeenCalled();
416
+ });
417
+
418
+ it("pastes to first input when multiple inputs exist", () => {
419
+ container.innerHTML = `
420
+ <input type="text" value="existing1" />
421
+ <input type="text" value="existing2" />
422
+ `;
423
+ const inputs = container.querySelectorAll("input");
424
+ const clipboardData = {
425
+ setData: vi.fn(),
426
+ getData: vi.fn().mockReturnValue("pasted"),
427
+ };
428
+ const event = {
429
+ target: container,
430
+ clipboardData,
431
+ preventDefault: vi.fn(),
432
+ } as unknown as ClipboardEvent;
433
+
434
+ pasteToElement(event);
435
+
436
+ expect(inputs[0].value).toBe("pasted");
437
+ expect(inputs[1].value).toBe("existing2");
438
+ });
439
+ });
440
+
441
+ describe("getBounds", () => {
442
+ it("returns empty array immediately when passed empty array", async () => {
443
+ const result = await getBounds([]);
444
+ expect(result).toEqual([]);
445
+ });
446
+
447
+ it("queries bounds using IntersectionObserver", async () => {
448
+ const mockObserver = {
449
+ observe: vi.fn(),
450
+ disconnect: vi.fn(),
451
+ };
452
+
453
+ const MockIntersectionObserver = vi.fn(function (
454
+ this: IntersectionObserver,
455
+ callback: IntersectionObserverCallback,
456
+ ) {
457
+ setTimeout(() => {
458
+ callback(
459
+ [
460
+ {
461
+ target: container,
462
+ boundingClientRect: {
463
+ top: 10,
464
+ left: 20,
465
+ width: 100,
466
+ height: 50,
467
+ },
468
+ },
469
+ ] as unknown as IntersectionObserverEntry[],
470
+ this,
471
+ );
472
+ }, 0);
473
+ return mockObserver;
474
+ });
475
+
476
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
477
+
478
+ const result = await getBounds([container]);
479
+
480
+ expect(result.length).toBe(1);
481
+ expect(result[0].target).toBe(container);
482
+ expect(result[0].top).toBe(10);
483
+ expect(result[0].left).toBe(20);
484
+ expect(result[0].width).toBe(100);
485
+ expect(result[0].height).toBe(50);
486
+
487
+ expect(mockObserver.observe).toHaveBeenCalledWith(container);
488
+ expect(mockObserver.disconnect).toHaveBeenCalled();
489
+
490
+ vi.unstubAllGlobals();
491
+ });
492
+
493
+ it("queries multiple elements simultaneously", async () => {
494
+ const el1 = document.createElement("div");
495
+ const el2 = document.createElement("div");
496
+
497
+ const mockObserver = {
498
+ observe: vi.fn(),
499
+ disconnect: vi.fn(),
500
+ };
501
+
502
+ const MockIntersectionObserver = vi.fn(function (
503
+ this: IntersectionObserver,
504
+ callback: IntersectionObserverCallback,
505
+ ) {
506
+ setTimeout(() => {
507
+ callback(
508
+ [
509
+ {
510
+ target: el1,
511
+ boundingClientRect: { top: 0, left: 0, width: 10, height: 10 },
512
+ },
513
+ {
514
+ target: el2,
515
+ boundingClientRect: { top: 20, left: 20, width: 20, height: 20 },
516
+ },
517
+ ] as unknown as IntersectionObserverEntry[],
518
+ this,
519
+ );
520
+ }, 0);
521
+ return mockObserver;
522
+ });
523
+
524
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
525
+
526
+ const result = await getBounds([el1, el2]);
527
+
528
+ expect(result.length).toBe(2);
529
+ expect(mockObserver.observe).toHaveBeenCalledTimes(2);
530
+
531
+ vi.unstubAllGlobals();
532
+ });
533
+
534
+ it("handles duplicate elements only once", async () => {
535
+ const mockObserver = {
536
+ observe: vi.fn(),
537
+ disconnect: vi.fn(),
538
+ };
539
+
540
+ const MockIntersectionObserver = vi.fn(function (
541
+ this: IntersectionObserver,
542
+ callback: IntersectionObserverCallback,
543
+ ) {
544
+ setTimeout(() => {
545
+ callback(
546
+ [
547
+ {
548
+ target: container,
549
+ boundingClientRect: { top: 10, left: 20, width: 100, height: 50 },
550
+ },
551
+ ] as unknown as IntersectionObserverEntry[],
552
+ this,
553
+ );
554
+ }, 0);
555
+ return mockObserver;
556
+ });
557
+
558
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
559
+
560
+ // Pass the same element 3 times
561
+ const result = await getBounds([container, container, container]);
562
+
563
+ // Result should contain only 1 element
564
+ expect(result.length).toBe(1);
565
+ expect(result[0].target).toBe(container);
566
+ // observe should be called only once
567
+ expect(mockObserver.observe).toHaveBeenCalledTimes(1);
568
+
569
+ vi.unstubAllGlobals();
570
+ });
571
+
572
+ it("results are sorted in input order", async () => {
573
+ const el1 = document.createElement("div");
574
+ const el2 = document.createElement("div");
575
+ const el3 = document.createElement("div");
576
+
577
+ const mockObserver = {
578
+ observe: vi.fn(),
579
+ disconnect: vi.fn(),
580
+ };
581
+
582
+ const MockIntersectionObserver = vi.fn(function (
583
+ this: IntersectionObserver,
584
+ callback: IntersectionObserverCallback,
585
+ ) {
586
+ setTimeout(() => {
587
+ // Callback is called in reverse order (el3, el2, el1)
588
+ callback(
589
+ [
590
+ {
591
+ target: el3,
592
+ boundingClientRect: { top: 30, left: 30, width: 30, height: 30 },
593
+ },
594
+ {
595
+ target: el2,
596
+ boundingClientRect: { top: 20, left: 20, width: 20, height: 20 },
597
+ },
598
+ {
599
+ target: el1,
600
+ boundingClientRect: { top: 10, left: 10, width: 10, height: 10 },
601
+ },
602
+ ] as unknown as IntersectionObserverEntry[],
603
+ this,
604
+ );
605
+ }, 0);
606
+ return mockObserver;
607
+ });
608
+
609
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
610
+
611
+ // Input order: el1, el2, el3
612
+ const result = await getBounds([el1, el2, el3]);
613
+
614
+ // Results should also be in input order: el1, el2, el3
615
+ expect(result.length).toBe(3);
616
+ expect(result[0].target).toBe(el1);
617
+ expect(result[1].target).toBe(el2);
618
+ expect(result[2].target).toBe(el3);
619
+
620
+ vi.unstubAllGlobals();
621
+ });
622
+
623
+ it("throws TimeoutError on timeout", async () => {
624
+ const mockObserver = {
625
+ observe: vi.fn(),
626
+ disconnect: vi.fn(),
627
+ };
628
+
629
+ // Mock that does not call the callback (triggers timeout)
630
+ const MockIntersectionObserver = vi.fn(function () {
631
+ return mockObserver;
632
+ });
633
+
634
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
635
+
636
+ // Test with 50ms timeout
637
+ await expect(getBounds([container], 50)).rejects.toThrow(TimeoutError);
638
+
639
+ vi.unstubAllGlobals();
640
+ });
641
+
642
+ it("supports custom timeout setting", async () => {
643
+ const mockObserver = {
644
+ observe: vi.fn(),
645
+ disconnect: vi.fn(),
646
+ };
647
+
648
+ const MockIntersectionObserver = vi.fn(function (
649
+ this: IntersectionObserver,
650
+ callback: IntersectionObserverCallback,
651
+ ) {
652
+ // Responds after 100ms
653
+ setTimeout(() => {
654
+ callback(
655
+ [
656
+ {
657
+ target: container,
658
+ boundingClientRect: { top: 0, left: 0, width: 10, height: 10 },
659
+ },
660
+ ] as unknown as IntersectionObserverEntry[],
661
+ this,
662
+ );
663
+ }, 100);
664
+ return mockObserver;
665
+ });
666
+
667
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
668
+
669
+ // Should succeed with 200ms timeout
670
+ const result = await getBounds([container], 200);
671
+ expect(result.length).toBe(1);
672
+
673
+ vi.unstubAllGlobals();
674
+ });
675
+
676
+ it("collects all results even when callback is called multiple times", async () => {
677
+ const el1 = document.createElement("div");
678
+ const el2 = document.createElement("div");
679
+ const el3 = document.createElement("div");
680
+
681
+ const mockObserver = {
682
+ observe: vi.fn(),
683
+ disconnect: vi.fn(),
684
+ };
685
+
686
+ const MockIntersectionObserver = vi.fn(function (
687
+ this: IntersectionObserver,
688
+ callback: IntersectionObserverCallback,
689
+ ) {
690
+ // First callback - only el1
691
+ setTimeout(() => {
692
+ callback(
693
+ [
694
+ {
695
+ target: el1,
696
+ boundingClientRect: { top: 10, left: 10, width: 10, height: 10 },
697
+ },
698
+ ] as unknown as IntersectionObserverEntry[],
699
+ this,
700
+ );
701
+ }, 0);
702
+
703
+ // Second callback - el2, el3
704
+ setTimeout(() => {
705
+ callback(
706
+ [
707
+ {
708
+ target: el2,
709
+ boundingClientRect: { top: 20, left: 20, width: 20, height: 20 },
710
+ },
711
+ {
712
+ target: el3,
713
+ boundingClientRect: { top: 30, left: 30, width: 30, height: 30 },
714
+ },
715
+ ] as unknown as IntersectionObserverEntry[],
716
+ this,
717
+ );
718
+ }, 10);
719
+
720
+ return mockObserver;
721
+ });
722
+
723
+ vi.stubGlobal("IntersectionObserver", MockIntersectionObserver);
724
+
725
+ const result = await getBounds([el1, el2, el3]);
726
+
727
+ // All elements should be collected
728
+ expect(result.length).toBe(3);
729
+ // Results should be sorted in input order
730
+ expect(result[0].target).toBe(el1);
731
+ expect(result[1].target).toBe(el2);
732
+ expect(result[2].target).toBe(el3);
733
+
734
+ vi.unstubAllGlobals();
735
+ });
736
+ });
737
+ });